Volver a actualizaciones
Nuevo releaseAug 1, 2026

maltrail v2.2

Sistema de detección de tráfico malicioso en tiempo real que utiliza listas negras públicas, rastros estáticos de malware y análisis heurístico para identificar amenazas en el tráfico DNS, HTTP e IP.

Compartir

Maltrail

License Sensor Server Trails X

Sistema de detección de tráfico malicioso. Maltrail vigila tu red en busca de contactos con elementos que se sabe que son maliciosos — y te indica, en una sola línea, qué se ha visto y por qué se considera malicioso.

"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)

Sin lenguaje de reglas, sin rituales de ajuste, sin ML. Un trail es un dominio, una URL, una dirección IP, un IP:port o un User-Agent que se sabe que pertenece a algo malicioso, y Maltrail te avisa cuando uno aparece en el tráfico.


Por qué Maltrail

La mayoría de las herramientas de detección de red te piden que describas comportamientos. Maltrail plantea una pregunta más simple que responde a la mayoría de los incidentes reales: ¿está este host hablando con algo que ya sabemos que es malicioso?

  • Más de 1.5 millones de trails, de más de 3,000 listas estáticas seleccionadas y 46 fuentes públicas, actualizadas a diario y en crecimiento. Con un fuerte peso hacia el malware — dominios C2, droppers, stealers, infraestructura APT — porque es lo que aparece en un compromiso real.
  • Los trails son texto plano. Un indicador por línea, en un archivo que puedes leer, sobre el que hacer grep y enviar una pull request. Por eso la cobertura se mantiene al día, y por qué siempre puedes responder «¿por qué ha saltado esto?».
  • Lo bastante rápido como para dejar de pensar en ello. Un solo núcleo gestiona un enlace de 10 GbE con una mezcla de tráfico realista; ver Rendimiento.
  • Heurísticas añadidas, no en sustitución — cada una aparece nombrada en el evento, nunca una puntuación a secas: escaneo de puertos, UDP y web, agotamiento de DNS, consultas con forma de DGA (umbrales de entropía y de consonantes, NXDOMAIN excesivo), dominios sinkholeados, incautados y aparcados, dominios largos, descargas directas por IP y de malware IoT, user agents sospechosos y sondas de proxy.

Arquitectura

Dos procesos independientes. Ejecútalos en una máquina o en varias.

   ┌──────────┐   events (UDP or file)   ┌──────────┐
   │  sensor  │ ───────────────────────► │  server  │ ◄── browser
   └──────────┘                          └──────────┘
    Rust                                  Python
    libpcap + PACKET_FANOUT               reporting UI + API
    trail matching, heuristics

Un sensor puede registrar localmente (LOG_DIR), enviar a un servidor remoto (LOG_SERVER), o ambas cosas. Para un SIEM existente también emite CEF por syslog (SYSLOG_SERVER) y JSON de Logstash (LOGSTASH_SERVER).


Rendimiento

El sensor está en Rust, con un hilo por worker de captura, compartiendo un único almacén inmutable de trails. Coste por paquete, según el tipo de tráfico:

tráficopor paquete
eco ICMP (58 B)101 ns
TCP SYN (70 B)302 ns
TLS masivo (1,473 B)402 ns
consulta DNS, caché caliente (93 B)452 ns
tráfico mixto (866 B de media)552 ns
petición HTTP (169 B)602 ns
consulta DNS, cada nombre único (inundación DGA)1,102 ns

Los workers no comparten nada mutable, así que ese coste es lo que te aporta cada núcleo adicional. Reproduciendo la mezcla de 866 bytes:

workerspackets/sGbit/svs 1 worker
11,687,99111.691.00×
23,209,62722.241.90×
45,379,43637.273.19×
88,552,23159.255.07×
1610,165,77370.436.02×

Un solo núcleo satura 10 GbE. La escalabilidad es casi lineal hasta cuatro workers y luego se reduce en una máquina de ocho núcleos físicos, porque el resto es SMT — hardware, no contención de bloqueos.

Frente al antiguo sensor de Python, reproduciendo la misma captura de 300,000 paquetes con los mismos 1,505,265 trails y la misma configuración, un worker cada uno — excluyendo el arranque, así que este es el coste por paquete en estado estable. Ambas cifras son de proceso completo (lectura del pcap y despacho incluidas), por eso la cifra del sensor es mayor que los 552 ns del recorrido por paquete medido de forma aislada arriba:

por paquetepackets/s
sensor (Rust)865 ns1,156,423
sensor antiguo (Python)23,448 ns42,648
27× más rápido

Reprodúcelo tú mismo — el arnés está en el repositorio, e imprime los contadores de eventos de ambos sensores para que una cifra de rendimiento nunca pueda citarse sin su contexto de corrección:

python3 sensor/tools/bench_compare.py --packets 300000 --trails ~/.maltrail/trails.csv --repeat 3

La memoria no crece con los núcleos: el almacén de 1.5M de trails ocupa 68.5 MB, se construye en 1.2 s y todos los workers lo comparten de forma inmutable.

Un worker de captura por defecto, que mueve ~1.1M de paquetes/s y es suficiente para casi cualquier host sensor. Los workers adicionales son una opción explícita (CAPTURE_FANOUT), porque el kernel calcula el hash de flujo de la captura mientras que las heurísticas de escaneo cuentan por origen: de los avisos heurísticos que genera un worker, el 91% sobrevive con 2 sockets, el 86% con 4, el 65% con 8. La detección exacta de trails es idéntica con cualquier número de workers. Escala horizontalmente cuando maltrail_capture_dropped_total lo indique, y no antes.

AMD Ryzen 7 PRO 4750U (8 núcleos físicos), heurísticas activadas, conjunto de trails real, mejor de tres ejecuciones. La proporción ha oscilado entre 14–27× según la ejecución y el hardware; los costes por paquete de arriba son los que limitan cuánto tráfico puede absorber un worker. Estas son cifras de la ruta de software — una NIC real añade costes de driver y de anillo, así que mide tu propio hardware y vigila maltrail_capture_dropped_total. El método, el desglose por protocolo, los recuentos de instrucciones y la salida del profiler están en sensor/docs/REPORT.md.


Inicio rápido

Linux, libpcap, Rust 1.74+ para el sensor, Python 3.7+ para el servidor.

Los binarios precompilados del sensor para x86_64 y aarch64 se adjuntan a cada release con una suma de verificación SHA-256, así que solo se necesita un toolchain de Rust para compilar desde el código fuente. Para compilarlo de todos modos:

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

En otra terminal, o en otra máquina:

python3 server.py

A continuación, abre http://127.0.0.1:8338 e inicia sesión con las credenciales de maltrail.conf (USERS).

-T es el atajo para «¿funcionará esto?» — valida la configuración, los trails, la whitelist, el directorio de logs, el filtro de captura y los privilegios, y te dice exactamente qué falta:

[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

Omitir el paso 2 o el 3 es la forma más común de acabar con un sensor que arranca y no detecta nada; -T los señala.

Como servicio

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

Esa es toda la instalación — no hay directorios que crear, ni setcap. Las units crean y son dueñas de /var/log/maltrail (eventos) y /var/lib/maltrail (el conjunto de trails) mediante LogsDirectory=/StateDirectory= de systemd, ejecutan ambos procesos como el usuario sin privilegios maltrail con un sistema de archivos de solo lectura, y otorgan al sensor exactamente CAP_NET_RAW y CAP_NET_ADMIN — nada más, y nada de root en ningún sitio. El sensor ejecuta -T como ExecStartPre, así que un despliegue roto falla al hacer systemctl start en lugar de funcionar a ciegas.

Compruébalo: systemctl status maltrail-sensor y journalctl -u maltrail-sensor -f.

Docker

docker compose -f docker/docker-compose.yml up -d

Ver docker/README.md.


Configuración

Todo vive en maltrail.conf, dividido en [Sensor] y [Server]. Las opciones que más merece la pena conocer:

opciónqué hace
MONITOR_INTERFACEinterfaz o interfaces en las que capturar, o any
CAPTURE_FILTERfiltro BPF; el predeterminado mantiene el tráfico masivo a velocidad de línea fuera del espacio de usuario
PROCESS_COUNTworkers de captura — uno por núcleo es un valor predeterminado razonable
LOG_DIRdónde se escriben los eventos (/var/log/maltrail)
TRAILS_FILEdónde vive el conjunto de trails generado (~/.maltrail/trails.csv; /var/lib/maltrail bajo las units)
LOG_SERVERenviar eventos a un servidor remoto en lugar de, o además de, registrar localmente
STATS_ADDRESSexponer métricas de Prometheus (sensor; desactivado salvo que se configure)
UPDATE_PERIODcada cuánto se actualizan los trails
USER_WHITELISTtu propia lista de no-alerta
CUSTOM_TRAILS_DIRtus propios trails, junto a los incluidos

Trails

trails/static/malware/asyncrat.txt      # one indicator per line
trails/static/malicious/…
trails/static/suspicious/…
trails/feeds/*.py                       # public feeds, pulled on update

Añadir un indicador es añadir una línea a un archivo de texto. Añadir una fuente es un pequeño módulo de Python. Ambos son pull requests ordinarias, y esa baja fricción es la razón de que el conjunto siga siendo útil.

Tus propios indicadores van en CUSTOM_TRAILS_DIR; cualquier cosa de la que nunca quieras oír hablar va en USER_WHITELIST.


Eventos

Una línea por detección, separada por espacios en blanco, con comillas CSV cuando sea necesario:

"<time>" <sensor> <src_ip> <src_port> <dst_ip> <dst_port> <proto> <type> <trail> "<info>" <reference>

type es lo que ha coincidido — DNS, IP, IPORT, URL, PATH, HTTP, UA, PORTinfo es por qué se considera malicioso, y reference es de dónde procede el trail: (static), el nombre de una fuente, o (heuristic).


Operación

  • -T valida una configuración y termina. Utilizable como puerta de despliegue; la unit de systemd lo ejecuta como ExecStartPre.

  • STATS_ADDRESS expone métricas de Prometheus. Las cuatro por las que merece la pena alertar, y todas significan que este sensor no está detectando lo que crees que detecta:

    métricaqué significa
    maltrail_up == 0ningún worker de captura está vivo — este host no está monitorizado
    rate(maltrail_capture_dropped_total)el anillo de captura está descartando paquetes — detecciones perdidas
    rate(maltrail_local_log_errors_total)se produjeron detecciones y luego se perdieron
    maltrail_trail_generation sin avanzarlos trails han dejado de actualizarse

    También son útiles: maltrail_log_dir_free_bytes (ver más abajo) y maltrail_state_saturations_total, que es distinta de cero cuando una inundación por agotamiento de estado ha reducido las heurísticas. La coincidencia exacta de trails no se ve afectada por ello, por diseño.

  • systemctl reload (SIGHUP) recarga los trails sin reiniciar. Los trails actualizados por cualquier otro medio se recogen en menos de un segundo, con un intercambio atómico — sin reinicio, sin paquetes perdidos.

  • El almacén condensado de observables (USE_CONDENSED_STORAGE, meta.sqlite) que alimenta las vistas de novedades y retro-hunt /meta del servidor se escribe en el mismo formato que produce el antiguo sensor, y ambos se comparan fila por fila con el arnés de paridad. Cada diferencia deliberada entre los sensores se enumera en sensor/docs/COMPATIBILITY.md.

Retención de eventos

Maltrail nunca elimina las evidencias de eventos. No hay ningún ajuste de retención que haga expirar tus logs, y eso es deliberado: son los registros a los que vuelves después de un incidente, y una herramienta que los descarta silenciosamente es peor que inútil durante la única semana en que los necesitas.

Eso convierte el espacio libre en algo que se gestiona en lugar de ignorarse:

  • Envía la copia duradera fuera de la máquina. LOG_SERVER (o SYSLOG_SERVER / LOGSTASH_SERVER) convierte al servidor o a tu SIEM en el sistema de registro, y al archivo local del sensor en un buffer. Esta es la estrategia de retención; el disco local no lo es.
  • Alerta sobre maltrail_log_dir_free_bytes con margen real. -T también lo informa, y avisa por debajo de 10 GB. Cuando llega a cero, el sensor no puede añadir eventos y las detecciones se pierden.
  • El archivado es decisión tuya. Comprime o mueve los logs diarios antiguos según tu propio calendario si necesitas espacio. Ten en cuenta que la interfaz de informes sirve los logs históricos como archivos planos y buscables, así que comprimirlos en su sitio elimina esos días de la interfaz — archívalos en otro lugar.

Si tu política exige el borrado (los logs de eventos contienen direcciones IP y dominios, que son datos personales en algunas jurisdicciones), esa es una decisión explícita del operador — hazlo con tus propias herramientas, deliberadamente, en lugar de dejar que un valor predeterminado del sensor lo haga silenciosamente.


Documentación

sensor/docs/INSTALL.mdinstalación, privilegios, configuración, resolución de problemas
sensor/docs/ARCHITECTURE.mdcómo funciona el sensor internamente
sensor/docs/COMPATIBILITY.mdcada diferencia deliberada respecto al antiguo sensor
sensor/docs/REPORT.mdmediciones, perfiles y resultados de pruebas
sensor/docs/ROADMAP.mdlo que aún está pendiente
old/README.mdel anterior sensor de Python, conservado como referencia y oráculo de pruebas

Contribuciones

Los trails son la contribución más valiosa: una línea en el archivo correcto, con una fuente. Las fuentes, los informes de errores y el trabajo en el sensor son igualmente bienvenidos.

La verificación completa del sensor es un solo comando:

bash sensor/tools/check.sh

Ejecuta el formateo, los lints, la suite de pruebas en ambos perfiles —debug y release—, y reproduce un corpus a través del sensor actual y del antiguo de Python, exigiendo eventos idénticos byte a byte. La parte de Python es bash tests/run.sh.


Licencia

MIT. Ver LICENSE.

Categorías