
maltrail v2.2
Sistema di rilevamento del traffico dannoso in tempo reale che utilizza blacklist pubbliche, tracce statiche di malware e analisi euristica per identificare minacce nel traffico DNS, HTTP e IP.

Sistema di rilevamento del traffico dannoso. Maltrail osserva la tua rete per contatti con elementi che sono noti per essere dannosi — e ti dice, in una riga, cosa è stato visto e perché è considerato dannoso.
"2026-08-07 09:14:22.117034" gw 10.13.13.2 57809 1.1.1.1 53 UDP DNS malware.bakewithdavid.com "asyncrat (malware)" (static)
Nessun linguaggio di regole, nessun rituale di tuning, nessun ML. Una trail è un dominio, URL, indirizzo IP, IP:port o
User-Agent noto per appartenere a qualcosa di dannoso, e Maltrail ti dice quando una di esse appare sulla
rete.
Perché Maltrail
La maggior parte degli strumenti di rilevamento di rete ti chiedono di descrivere comportamenti. Maltrail pone una domanda più semplice che risponde alla maggior parte degli incidenti reali: questo host sta parlando con qualcosa che sappiamo già essere dannoso?
- Più di 1,5 milioni di trail, da oltre 3.000 liste statiche curate e 46 feed pubblici, aggiornati quotidianamente e in crescita. Fortemente orientate verso malware — domini C2, dropper, stealer, infrastrutture APT — perché è quello che compare in una compromissione reale.
- Le trail sono testo semplice. Un indicatore per riga, in un file che puoi leggere, fare grep e su cui inviare una pull request. È per questo che la copertura resta aggiornata, e perché puoi sempre rispondere a "perché è scattato questo?".
- Abbastanza veloce da smettere di pensarci. Un singolo core gestisce un link 10 GbE con un mix di traffico realistico; vedi Performance.
- Euristica in aggiunta, non in sostituzione — ognuna nominata nell'evento, mai un punteggio nudo: scansione di porte, UDP e web, esaurimento DNS, query con forma DGA (soglie di entropia e consonanti, NXDOMAIN eccessivo), domini sinkholed, sequestrati e parcheggiati, domini lunghi, download diretti via IP e di malware IoT, user agent sospetti e probe proxy.
Architettura
Due processi indipendenti. Esegui entrambi su una singola macchina o su più macchine.
┌──────────┐ events (UDP or file) ┌──────────┐
│ sensor │ ───────────────────────► │ server │ ◄── browser
└──────────┘ └──────────┘
Rust Python
libpcap + PACKET_FANOUT reporting UI + API
trail matching, heuristics
Un sensore può registrare localmente (LOG_DIR), inviare a un server remoto (LOG_SERVER), o entrambi. Per un
SIEM esistente emette anche CEF via syslog (SYSLOG_SERVER) e JSON Logstash
(LOGSTASH_SERVER).
Performance
Il sensore è in Rust, un thread per worker di cattura, che condividono un unico store di trail immutabile. Costo per pacchetto, per tipo di traffico:
| traffico | per pacchetto |
|---|---|
| ICMP echo (58 B) | 101 ns |
| TCP SYN (70 B) | 302 ns |
| bulk TLS (1,473 B) | 402 ns |
| query DNS, cache calda (93 B) | 452 ns |
| traffico misto (media 866 B) | 552 ns |
| richiesta HTTP (169 B) | 602 ns |
| query DNS, ogni nome univoco (flood DGA) | 1,102 ns |
I worker non condividono nulla di mutabile, quindi quel costo è ciò che ogni core aggiuntivo ti offre. Riproducendo il mix da 866 byte:
| workers | pacchetti/s | Gbit/s | vs 1 worker |
|---|---|---|---|
| 1 | 1,687,991 | 11.69 | 1.00× |
| 2 | 3,209,627 | 22.24 | 1.90× |
| 4 | 5,379,436 | 37.27 | 3.19× |
| 8 | 8,552,231 | 59.25 | 5.07× |
| 16 | 10,165,773 | 70.43 | 6.02× |
Un singolo core satura 10 GbE. Lo scaling è quasi lineare fino a quattro worker, poi diminuisce su una macchina con otto core fisici, perché il resto è SMT — hardware, non contesa sui lock.
Rispetto al vecchio sensore Python, riproducendo la stessa cattura di 300,000 pacchetti con le stesse 1,505,265 trail e la stessa configurazione, un worker ciascuno — escluso l'avvio, quindi questo è il costo a pacchetto a regime. Entrambe le cifre qui sono per l'intero processo (inclusi lettura pcap e dispatch), motivo per cui il numero del sensore è più alto del percorso a 552 ns per pacchetto misurato isolatamente sopra:
| per pacchetto | pacchetti/s | |
|---|---|---|
| sensore (Rust) | 865 ns | 1,156,423 |
| vecchio sensore (Python) | 23,448 ns | 42,648 |
| 27× più veloce |
Riproducilo tu stesso — l'harness è incluso nel repository, e stampa il conteggio degli eventi di entrambi i sensori, così un valore di throughput non potrà mai essere citato senza il suo contesto di correttezza:
python3 sensor/tools/bench_compare.py --packets 300000 --trails ~/.maltrail/trails.csv --repeat 3
La memoria non cresce con il numero di core: lo store da 1,5M di trail è 68,5 MB, costruito in 1,2 s e condiviso in modo immutabile da ogni worker.
Un solo worker di cattura di default, che gestisce ~1,1M di pacchetti/s ed è sufficiente per quasi tutti gli
host sensore. I worker aggiuntivi sono un'opzione esplicita (CAPTURE_FANOUT), perché il kernel esegue l'hash
di flusso della cattura mentre le euristiche di scansione contano per sorgente: degli allarmi euristici generati
da un worker, il 91% sopravvive con 2 socket, l'86% con 4, il 65% con 8. Il rilevamento esatto delle trail è
identico a qualsiasi numero di worker. Fai scale out quando maltrail_capture_dropped_total lo indica, non
prima.
AMD Ryzen 7 PRO 4750U (8 core fisici), euristiche attive, set di trail reale, migliore di tre esecuzioni.
Il rapporto è variato da 14–27× tra esecuzioni e hardware; i costi per pacchetto sopra sono quelli che
delimitano quanto traffico un worker può assorbire. Queste sono cifre del percorso software — una NIC reale
aggiunge costi di driver e ring, quindi misura il tuo hardware e controlla maltrail_capture_dropped_total.
Metodo, suddivisione per protocollo, conteggi di istruzioni e output del profiler sono in
sensor/docs/REPORT.md.
Avvio rapido
Linux, libpcap, Rust 1.74+ per il sensore, Python 3.7+ per il server.
I binari del sensore precompilati per x86_64 e aarch64 sono allegati a ogni
release con un checksum SHA-256, quindi una toolchain Rust
serve solo per compilare dai sorgenti. Per compilarla comunque:
git clone --depth 1 https://github.com/stamparm/maltrail.git
cd maltrail
# 1. build the sensor
cd sensor && cargo build --release && cd ..
# 2. let it capture without running as root
sudo setcap cap_net_raw,cap_net_admin=eip sensor/target/release/maltrail-sensor
# 3. give it somewhere to write events (LOG_DIR, /var/log/maltrail by default)
sudo install -d -o "$USER" -g "$USER" -m 750 /var/log/maltrail
# 4. check the deployment before trusting it — exits non-zero if anything is wrong
sensor/target/release/maltrail-sensor -T
# 5. run it (first start builds the trail set; takes a minute)
sensor/target/release/maltrail-sensor
In un altro terminale, o su un'altra macchina:
python3 server.py
Poi apri http://127.0.0.1:8338 e accedi con le credenziali in maltrail.conf (USERS).
-T è la scorciatoia per "funzionerà?" — valida la configurazione, le trail, la whitelist, la directory di log, il
filtro di cattura e i privilegi, e ti dice esattamente cosa manca:
[o] log directory: '/var/log/maltrail' is writable
[o] capture privileges: CAP_NET_RAW present
[o] capture filter: udp or icmp or (tcp and (tcp[tcpflags] == tcp-syn or port 80 or port...
[o] interface: any
[o] workers: 16 (PACKET_FANOUT required; verify with tools/fanout_check.py as root)
[o] whitelist: 3440 entries, 18 CIDR range(s)
[o] trails: 1505265 loaded (0 malformed row(s)), ipv4=144758 ipv4:port=253517 ipv6=2014 wildcard=29
[o] heuristics: on (disabled: none)
[i] configuration test PASSED
Saltare i passaggi 2 o 3 è il modo più comune per ritrovarsi con un sensore che si avvia e non rileva nulla;
-T li segnala entrambi.
Come servizio
sudo useradd --system --no-create-home --shell /usr/sbin/nologin maltrail
sudo rsync -a --exclude .git . /opt/maltrail/
sudo cp /opt/maltrail/maltrail-{server,sensor}.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now maltrail-server maltrail-sensor
Questa è l'intera installazione — nessuna directory da creare, nessun setcap. Le unità creano e possiedono
/var/log/maltrail (eventi) e /var/lib/maltrail (il set di trail) tramite LogsDirectory=/StateDirectory=
di systemd, eseguono entrambi i processi come utente non privilegiato maltrail con un filesystem in sola
lettura, e danno al sensore esattamente CAP_NET_RAW e CAP_NET_ADMIN — nient'altro, e nessun root da
nessuna parte. Il sensore esegue -T come ExecStartPre, quindi una distribuzione rotta fallisce su
systemctl start invece di eseguire alla cieca.
Verifica: systemctl status maltrail-sensor e journalctl -u maltrail-sensor -f.
Docker
docker compose -f docker/docker-compose.yml up -d
Vedi docker/README.md.
Configurazione
Tutto si trova in maltrail.conf, diviso in [Sensor] e [Server]. Le opzioni più utili da conoscere:
| opzione | funzione |
|---|---|
MONITOR_INTERFACE | interfaccia/e su cui catturare, o any |
CAPTURE_FILTER | filtro BPF; il default tiene il traffico bulk a velocità di linea fuori dallo userspace |
PROCESS_COUNT | worker di cattura — uno per core è un default ragionevole |
LOG_DIR | dove vengono scritti gli eventi (/var/log/maltrail) |
TRAILS_FILE | dove vive il set di trail costruito (~/.maltrail/trails.csv; /var/lib/maltrail sotto le unità) |
LOG_SERVER | invia gli eventi a un server remoto invece di, o oltre a, registrarli localmente |
STATS_ADDRESS | espone metriche Prometheus (sensore; disattivato se non impostato) |
UPDATE_PERIOD | ogni quanto vengono aggiornate le trail |
USER_WHITELIST | la tua lista personale di elementi da non segnalare mai |
CUSTOM_TRAILS_DIR | le tue trail personali, insieme a quelle incluse |
Trails
trails/static/malware/asyncrat.txt # one indicator per line
trails/static/malicious/…
trails/static/suspicious/…
trails/feeds/*.py # public feeds, pulled on update
Aggiungere un indicatore significa aggiungere una riga a un file di testo. Aggiungere un feed è un piccolo modulo Python. Entrambi sono normali pull request, ed è questo basso attrito a mantenere utile il set.
I tuoi indicatori personali vanno in CUSTOM_TRAILS_DIR; tutto ciò di cui non vuoi mai sentire parlare va in
USER_WHITELIST.
Eventi
Una riga per rilevamento, separati da spazi bianchi, con virgolette CSV dove serve:
"<time>" <sensor> <src_ip> <src_port> <dst_ip> <dst_port> <proto> <type> <trail> "<info>" <reference>
type è ciò che ha fatto match — DNS, IP, IPORT, URL, PATH, HTTP, UA, PORT — info spiega
perché è considerato dannoso, e reference indica da dove proviene la trail: (static), un nome di feed, o
(heuristic).
Gestione
-
-Tvalida una configurazione ed esce. Utilizzabile come gate di deploy; l'unità systemd lo esegue comeExecStartPre. -
STATS_ADDRESSespone metriche Prometheus. Le quattro su cui vale la pena fare alerting, tutte significano questo sensore non sta rilevando ciò che pensi:metrica cosa significa maltrail_up == 0nessun worker di cattura è attivo — questo host non è monitorato rate(maltrail_capture_dropped_total)il ring sta scartando pacchetti — rilevamenti mancati rate(maltrail_local_log_errors_total)i rilevamenti sono stati prodotti e poi persi maltrail_trail_generationnon avanzale trail hanno smesso di aggiornarsi Utili anche:
maltrail_log_dir_free_bytes(vedi sotto) emaltrail_state_saturations_total, che è diverso da zero quando un flood di esaurimento dello stato ha ridotto le euristiche. Il matching esatto delle trail non è influenzato da questo, per design. -
systemctl reload(SIGHUP) ricarica le trail senza riavvio. Le trail aggiornate da qualsiasi altra cosa vengono rilevate entro un secondo, con uno scambio atomico — nessun riavvio, nessun pacchetto perso. -
Lo store osservabile condensato (
USE_CONDENSED_STORAGE,meta.sqlite) che alimenta le viste/metadi novità e retro-hunt del server è scritto nello stesso formato prodotto dal vecchio sensore, e i due vengono confrontati riga per riga dall'harness di parità. Ogni differenza deliberata tra i sensori è elencata insensor/docs/COMPATIBILITY.md.
Conservazione degli eventi
Maltrail non elimina mai le prove degli eventi. Non esiste un'impostazione di conservazione che faccia scadere i tuoi log, ed è una scelta deliberata: questi sono i record a cui torni dopo un incidente, e uno strumento che li scarta silenziosamente è peggio che inutile durante l'unica settimana in cui ti servono.
Questo rende lo spazio libero qualcosa da gestire attivamente, non da ignorare:
- Spedisci la copia durevole fuori dalla macchina.
LOG_SERVER(oSYSLOG_SERVER/LOGSTASH_SERVER) rende il server o il tuo SIEM il sistema di registrazione di riferimento, e il file locale del sensore un buffer. Questa è la strategia di conservazione; il disco locale non lo è. - Imposta alert su
maltrail_log_dir_free_bytescon un margine reale.-Tlo riporta anch'esso, e avvisa sotto i 10 GB. Quando arriva a zero il sensore non può più appendere e i rilevamenti vengono persi. - L'archiviazione è una tua decisione. Comprimi o sposta i vecchi log giornalieri secondo i tuoi tempi se ti serve spazio. Nota che l'interfaccia di reporting serve i log storici come semplici file seekable, quindi comprimerli sul posto rimuove quei giorni dall'interfaccia — archiviali altrove.
Se la tua policy richiede la cancellazione (i log degli eventi contengono indirizzi IP e domini, che in alcune giurisdizioni sono dati personali), quella è una decisione esplicita dell'operatore — prendila con i tuoi strumenti, deliberatamente, piuttosto che lasciare che un default del sensore lo faccia in silenzio.
Documentazione
sensor/docs/INSTALL.md | installazione, privilegi, configurazione, risoluzione dei problemi |
sensor/docs/ARCHITECTURE.md | come funziona internamente il sensore |
sensor/docs/COMPATIBILITY.md | ogni differenza deliberata rispetto al vecchio sensore |
sensor/docs/REPORT.md | misurazioni, profili e risultati dei test |
sensor/docs/ROADMAP.md | cosa resta ancora aperto |
old/README.md | il precedente sensore Python, conservato come riferimento e oracolo di test |
Contribuire
Le trail sono il contributo più prezioso: una riga nel file giusto, con una fonte. Feed, segnalazioni di bug e lavoro sul sensore sono ugualmente benvenuti.
Il gate completo del sensore è un unico comando:
bash sensor/tools/check.sh
Esegue formattazione, lint, la suite di test in entrambi i profili debug e release, e riproduce un corpus sia
con il sensore attuale sia con quello vecchio in Python, richiedendo eventi identici byte per byte. Il lato Python
è bash tests/run.sh.
Licence
MIT. Vedi LICENSE.