{"repro_id":"REPRO-2026-00327","version":6,"title":"Zimbra Collaboration unauthenticated RCE via Swatchdog/SNMP log-injection command injection (swatchrc dosnmp Perl backtick)","repro_type":"security","status":"published","severity":"high","description":"Unauthenticated remote OS command injection (CWE-78) in Zimbra Collaboration (ZCS) before 10.1.20. CVSS 8.9 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:L). AC:H because exploitation requires the optional zimbra-snmp package installed AND SNMP notifications enabled AND Swatchdog (zmswatch) running.","root_cause":"# Root Cause Analysis: CVE-2026-73570\n\n## Summary\n\nCVE-2026-73570 is an unauthenticated command-injection vulnerability in Zimbra Collaboration's SNMP monitoring path. An external SMTP peer can make the ZCS-packaged Postfix service log attacker-controlled text resembling a Zimbra `Service status change` record. The installed, packaged `zmswatch` process matches that log line and passes its attacker-controlled `SERVICE` capture to `dosnmp()`. Before ZCS 10.1.20, `dosnmp()` interpolates `SERVICE` into a Perl backtick command, invoking `/bin/sh` and permitting command execution as the `zimbra` operating-system user.\n\n## Impact\n\n- **Affected component:** Zimbra Collaboration Suite MTA/SNMP monitoring (`zimbra-mta`, optional `zimbra-snmp`, `/opt/zimbra/conf/swatchrc`, packaged Postfix, and packaged `zmswatch`).\n- **Affected versions:** ZCS releases before 10.1.20 with the vulnerable Swatchdog configuration.\n- **Required configuration:** The SNMP package is installed, SNMP notifications are enabled, `zmswatch` is running, and the SMTP listener is reachable.\n- **Risk:** High. A remote unauthenticated attacker can execute shell commands as the `zimbra` account, which can disclose Zimbra configuration/credentials or alter mail-service data.\n\n## Impact Parity\n\n- **Disclosed/claimed maximum impact:** Unauthenticated remote code/command execution through the public SMTP protocol path.\n- **Reproduced impact from this run:** Two clean installed-ZCS product instances accepted attacker-controlled SMTP bytes over an external TCP connection to packaged Postfix, logged them to `/var/log/zimbra.log`, processed them with packaged `zmswatch` running as `zimbra`, and created unique shell-expanded markers: `v1:u999:nzimbra` and `v2:u999:nzimbra`.\n- **Parity:** `full`\n- **Not demonstrated:** No interactive shell, persistence, credential theft, or privilege escalation beyond the `zimbra` account was attempted or claimed.\n\n## Root Cause\n\nThe vulnerable Zimbra Swatchdog rules match Postfix log records of the form `: Service status change: (\\S+) (.*) changed from ...`. A compact SMTP command-pipelining violation can place attacker input in that log format. The second capture becomes `SERVICE` and flows to `dosnmp()`.\n\nThe vulnerable ZCS 10.1.0 package input contains:\n\n```perl\n`$snmptrap $snmpsvctrap $snmpsvcname s $args{SERVICE} $snmpsvcstatus i $statuses{$args{STATUS}}`;\n```\n\nPerl backticks invoke a shell. Consequently, shell metacharacters and command substitutions in `SERVICE` are interpreted instead of remaining one SNMP argument. The installed vulnerable `/opt/zimbra/conf/swatchrc.in` has SHA-256 `b0b36f69787aad1ad02b3bccac8043187de7113bea1aa49669e31ed98160c02e`.\n\nThe authentic ZCS 10.1.20 `zimbra-mta-patch` package replaces this with LIST-form `system()`:\n\n```perl\nsystem(\"/opt/zimbra/common/bin/snmptrap\", \"-v\", \"2c\", \"-c\", \"zimbra\", $traphost, \"\", $snmpsvctrap, $snmpsvcname, \"s\", $args{SERVICE}, $snmpsvcstatus, \"i\", $statuses{$args{STATUS}});\n```\n\nLIST-form execution passes `SERVICE` as one literal argument without invoking a shell. The authentic fixed `swatchrc.in` has SHA-256 `06e9be7dfad44519dd3f0f9c70673d7ccce6dbfb18bf54dc5ff2c5078a4c4a5c`. The exact official fixed package is `zimbra-mta-patch 10.1.20.1783342495-1.u22`, package SHA-256 `f242ee41af609b940c091d4bf9dab7ff1dd641134865d045850cfe00cbdee593`.\n\nThe release-level fix is documented at <https://wiki.zimbra.com/wiki/Zimbra_Releases/10.1.20>; this reproduction does not rely on a generated approximation or an inferred source commit.\n\n## Reproduction Steps\n\n1. Run `bash bundle/repro/reproduction_steps.sh` from any directory. The script uses `PRUVA_ROOT` when set.\n2. The script reads `bundle/project_cache_context.json` and first uses the prepared project cache. If necessary, it downloads the pinned official ZCS 10.1.0 Ubuntu 22 installer archive and authentic ZCS 10.1.20 MTA patch, verifies their SHA-256 digests, and constructs installed product images.\n3. It creates two clean vulnerable and two clean fixed instances. Every instance starts installed ZCS LDAP state, packaged ZCS Postfix, rsyslog's Zimbra mail-log route, and packaged `zmswatch` as `zimbra`.\n4. For each instance, an external Python TCP peer connects through a host-loopback published port, receives the real Postfix banner, and pipelines `VRFY` with a 99-byte forged service-status record.\n5. Vulnerable success requires both unique victim-shell markers. Fixed success requires the authentic 10.1.20 package identity, the same log/parser path, no marker, and a trace showing the entire metacharacter-bearing payload preserved as literal `snmptrap` `argv[9]`.\n6. The final script was executed successfully twice consecutively; each execution internally performed all four isolated attempts.\n\n## Evidence\n\nPrimary current-run evidence is under `bundle/repro/evidence/`:\n\n- `source-identity.txt` binds the official archive/package digests and immutable vulnerable/fixed runtime image IDs.\n- `vulnerable-1/attack-request.txt` contains the actual SMTP transaction bytes.\n- `vulnerable-1/smtp.transcript` records the real ZCS Postfix banner and protocol exchange.\n- `vulnerable-1/zimbra.log` proves packaged Postfix logged the attacker-selected forged record.\n- `vulnerable-1/processes.txt` shows Postfix `smtpd` as `postfix` and packaged Swatchdog plus its generated parser as `zimbra`.\n- `vulnerable-1/package-identity.txt` records ZCS 10.1.0, `zimbra-postfix 3.6.14`, `zimbra-perl-swatchdog 3.2.4`, and `zimbra-snmp 10.1.0`.\n- `vulnerable-1/marker.txt` contains `v1:u999:nzimbra` (SHA-256 `85fe5f908055df71908e93700df6808558cda5b1e30d390f0247ec365f4d2803`).\n- `vulnerable-2/marker.txt` contains `v2:u999:nzimbra` (SHA-256 `483742d1e7d96aab6b5fdab8ac45189bed7036ccc9ba3d1b0cf150dbb54bf7ba`).\n- `fixed-1/result.json` and `fixed-2/result.json` record `target_path_reached=true` and `marker_present=false`.\n- `fixed-1/snmptrap.trace` and `fixed-2/snmptrap.trace` show the attack string preserved literally as one argument under LIST-form `system()`.\n- `bundle/repro/runtime_manifest.json` records `entrypoint_kind=tcp_peer`, all three runtime gates as true, exact target identities, and digest-bound proof artifacts.\n- `bundle/repro/validation_verdict.json` records `confirmed`, `network_protocol`, `production_path`, and observed `code_execution`.\n- `bundle/logs/reproduction_steps.log` records the latest complete orchestration result.\n\nKey current-run excerpts:\n\n```text\nv1:u999:nzimbra\nv2:u999:nzimbra\n```\n\n```text\npostfix smtpd ... improper command pipelining after VRFY ... : Service status change: h x;echo v1:u$(id -u):n$(id -un)>/tmp/p0;# ...\n```\n\n```text\nargv[9]=<x;echo f1:u$(id -u):n$(id -un)>/tmp/p2;#>\n```\n\n## Recommendations / Next Steps\n\n- Upgrade affected installations to ZCS 10.1.20 or later with the complete vendor-supported package closure.\n- Verify that `/opt/zimbra/conf/swatchrc.in` uses LIST-form `system()` and regenerate `/opt/zimbra/conf/swatchrc` with the normal Zimbra tooling.\n- Until upgrading, disable SNMP notifications or stop `zmswatch` if operationally acceptable.\n- Inspect Zimbra logs and filesystem/web-root modifications for exploitation indicators; rotate credentials in Zimbra configuration if compromise is suspected.\n- Add an end-to-end regression that sends shell metacharacters through SMTP and asserts they arrive at `snmptrap` as one literal argument without command side effects.\n\n## Additional Notes\n\n- The reproduction is idempotent. It deletes prior attempt evidence, starts fresh isolated containers and ports, and removes every attempt container on exit.\n- The primary oracle is non-sanitized product-visible shell execution through the installed production path; no sanitizer was used.\n- The real `snmptrap` executable is used for the vulnerable success path. A recording wrapper is introduced only after the fixed negative-control path has already proven no marker, solely to demonstrate literal fixed-version argv boundaries.\n- LDAP's test database is recreated per instance because installation-time LMDB files are sparse and unsuitable for a portable image; this does not replace or modify Postfix, `zmswatch`, `swatchrc`, or the vulnerable/fixed sink.\n- The trigger must remain within Postfix's observed 100-byte client-text log limit; the final forged record is 99 bytes.\n","cve_id":"CVE-2026-73570","cwe_id":"CWE-78","source_url":"https://nvd.nist.gov/vuln/detail/CVE-2026-73570","reproduced_at":"2026-08-23T15:38:37.177340+00:00","duration_secs":5641.0,"tool_calls":520,"handoffs":3,"total_cost_usd":61.304638,"agent_costs":{"claim_matcher":0.048113,"judge":1.918486,"learning_policy":0.014597,"repro":56.173987,"support":0.085997,"vuln_variant":3.063458},"cost_breakdown":{"claim_matcher":{"gpt-5.4-mini-2026-03-17":0.048113},"judge":{"gpt-5.6-sol":1.918486},"learning_policy":{"gpt-5.4-mini-2026-03-17":0.014597},"repro":{"accounts/fireworks/models/kimi-k3":2.034196,"gpt-5.6-sol":54.139791},"support":{"accounts/fireworks/models/kimi-k3":0.085997},"vuln_variant":{"gpt-5.6-sol":3.063458}},"quality":{"confidence":"high","idempotent_verified":false,"community_verifications":0},"evidence":{"workflow":{"profile":"known_vulnerability","schema_version":2,"stages":["support","claim_contract","repro","judge","vuln_variant"]}},"environment":{"sandbox_image":"ghcr.io/n3mes1s/pruva-sandbox@sha256:8096b2518d6022e13d68f885c3b8ded6b4fe607098b1a1ccbfb99abc004d1dc1"},"published_at":"2026-08-23T15:38:38.153529+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":8302,"category":"analysis"},{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":22401,"category":"reproduction_script"},{"path":"bundle/repro/evidence/fixed-1/attack-request.txt","filename":"attack-request.txt","size":142,"category":"other"},{"path":"bundle/repro/evidence/fixed-1/package-identity.txt","filename":"package-identity.txt","size":246,"category":"other"},{"path":"bundle/repro/evidence/fixed-1/processes.txt","filename":"processes.txt","size":1719,"category":"other"},{"path":"bundle/repro/evidence/fixed-1/smtp.transcript","filename":"smtp.transcript","size":490,"category":"other"},{"path":"bundle/repro/evidence/fixed-1/snmptrap.trace","filename":"snmptrap.trace","size":1158,"category":"other"},{"path":"bundle/repro/evidence/fixed-1/swatchrc.sha256","filename":"swatchrc.sha256","size":187,"category":"other"},{"path":"bundle/repro/evidence/fixed-1/zimbra.log","filename":"zimbra.log","size":416,"category":"log"},{"path":"bundle/repro/evidence/fixed-1/zmswatch.out","filename":"zmswatch.out","size":753,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/attack-request.txt","filename":"attack-request.txt","size":142,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/package-identity.txt","filename":"package-identity.txt","size":246,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/processes.txt","filename":"processes.txt","size":1719,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/result.json","filename":"result.json","size":171,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/smtp.transcript","filename":"smtp.transcript","size":490,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/snmptrap.trace","filename":"snmptrap.trace","size":1158,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/swatchrc.sha256","filename":"swatchrc.sha256","size":187,"category":"other"},{"path":"bundle/repro/evidence/fixed-2/zimbra.log","filename":"zimbra.log","size":416,"category":"log"},{"path":"bundle/repro/evidence/fixed-2/zmswatch.out","filename":"zmswatch.out","size":753,"category":"other"},{"path":"bundle/repro/evidence/source-identity.txt","filename":"source-identity.txt","size":790,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-1/package-identity.txt","filename":"package-identity.txt","size":260,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-1/result.json","filename":"result.json","size":175,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-1/swatchrc.sha256","filename":"swatchrc.sha256","size":187,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-2/attack-request.txt","filename":"attack-request.txt","size":142,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-2/package-identity.txt","filename":"package-identity.txt","size":260,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-2/processes.txt","filename":"processes.txt","size":1719,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-2/result.json","filename":"result.json","size":175,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-2/smtp.transcript","filename":"smtp.transcript","size":490,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-2/swatchrc.sha256","filename":"swatchrc.sha256","size":187,"category":"other"},{"path":"bundle/repro/evidence/vulnerable-2/zimbra.log","filename":"zimbra.log","size":416,"category":"log"},{"path":"bundle/repro/evidence/vulnerable-2/zmswatch.out","filename":"zmswatch.out","size":595,"category":"other"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":7458,"category":"other"},{"path":"bundle/repro/src/swatchrc.gen.fixed","filename":"swatchrc.gen.fixed","size":2362,"category":"other"},{"path":"bundle/repro/src/swatchrc.vuln","filename":"swatchrc.vuln","size":2278,"category":"other"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":956,"category":"other"}]}