top of page

Hunt Zimbra CVE-2026-73570: the trap in the mailroom

Writer: Inception Security
Inception Security
22 hours ago
8 min read
Neon illustration of a Zimbra mailbox node running a generated script that appends a curl command to an snmptrap call

Picture the process tree on a Zimbra mailbox node. Perl is running a generated script called .swatchdog_script. Under it sits a shell, and in that shell's command line a normal snmptrap call runs straight into a semicolon, a curl, and a trailing hash mark that comments out the legitimate arguments. The mail only had to arrive.


That is the shape Microsoft Threat Intelligence published on 30 September 2026 for CVE-2026-73570, an unauthenticated OS command injection in the SNMP notification path of Zimbra Collaboration Suite, rated CVSS 8.9. The timeline is the uncomfortable part. Zimbra shipped the fix in 10.1.20 on 20 July 2026, the CVE was not disclosed publicly until 13 August, CERT Polska confirmed active exploitation on 18 August, and CISA added it to the KEV catalog on 21 August with a three-day federal deadline of 24 August. Shadowserver has since counted more than 270 compromised Zimbra instances among roughly 12,000 publicly reachable servers it scans.


Carry two questions. Does any Zimbra node you run have zimbra-snmp installed with notifications on? And if you find one webshell, who checks the other mailbox nodes?


How the attack works


The monitor trusted the service value


Zimbra's optional zimbra-snmp package lets the server send SNMP notifications, and Microsoft says exploitation needs that package installed with notifications enabled. So the scoping question is narrow and answerable, which is the good news here: check those two things on each node before you spend a day hunting. The bad news is that optional packages are exactly the ones nobody has an inventory of.


An attacker sends a specially crafted SMTP request carrying shell metacharacters. When a service-state change triggers health monitoring, swatchdog takes the attacker-controlled value and builds a snmptrap shell invocation around it. The shell reads the metacharacters as commands, and they run with the privileges of the zimbra service account.


Microsoft describes the signature as a legitimate snmptrap invocation followed by shell metacharacters and a wget or curl call, with a trailing # comment swallowing the remaining arguments.


One timeline detail should change your lookback. Microsoft saw two scanning tools probing this path between 28 July and 7 August, which is after the fix existed and before anyone disclosed the CVE. They confirmed command execution with callbacks and a small fingerprint check rather than a full payload, so that window was reconnaissance. Our reading, which goes beyond what Microsoft states: start your review at the fix date, not the disclosure date. Anyone watching Zimbra's release notes knew about this three weeks before the rest of us did.


Risk: what the mail server holds


One caveat on reading the chain below. Microsoft's write-up combines behaviours from several confirmed compromises, so no single host necessarily showed every stage, and affected organisations sat in more than one region and industry. No victims are named in the research, and none are named here.


What Microsoft saw after the first command:


  • JSP webshells in Jetty and mailboxd paths, with extra copies on peer mailbox nodes, plus reverse shells.

  • Privilege escalation through zmmailboxdmgr, the sudo PAM configuration and pam_exec, ending in a NOPASSWD: ALL entry for zimbra.

  • A systemd unit named zimlog.service, with timestamps copied from rsync.service and sshd.service.

  • Theft of shared secrets. zmlocalconfig -s exposed service credentials, which fed LDAP queries for zimbraPreAuthKey, zimbraAuthTokenKey and zimbraTwoFactorAuthSecret. The auth token key lets an attacker sign session tokens for arbitrary accounts. The preauth key builds pre-authenticated login URLs for any user.

  • Movement between nodes with the zimbra SSH identity and rsync.

  • On one server, a mailbox-backup archive at /opt/zimbra/final.tar.gz and an AzCopy attempt. Microsoft says the evidence does not confirm the transfer finished.


CISA lists known ransomware campaign use for this CVE as Unknown, and we will leave it there rather than guess.


Mitigate and reduce risk


Microsoft's guidance first:


  • Upgrade every Zimbra instance to 10.1.20 or later.

  • Where patching must wait, uninstall zimbra-snmp, disable SNMP notifications, and restrict SNMP and SMTP access to trusted hosts only.

  • Treat reverse-shell alerts on internet-facing mail servers as priority incidents. Some of the worst outcomes involved only a plain interactive shell.

  • Rotate all domain zimbraPreAuthKey values and review systemd units for unexpected ownership, enablement or timestamp changes.

  • Inspect application and servlet work directories on every mailbox node for unexpected JSP files and generated *_jsp.java artifacts. Removing one known JSP does not end the access.


Our reading, which is separate from Microsoft's guidance: the patch closes the injection and says nothing about what ran before you applied it. So rotate secrets on any node with a hit, and not only on the node that alerted, because a mail server that was reachable from the internet for three weeks before disclosure is a credential problem as much as a code problem.


General-knowledge controls, all test first: list which nodes have zimbra-snmp installed, forward mail server logs off the box, and alert on new .service files in /etc/systemd/system.


How to hunt the activity


Defender XDR advanced hunting keeps 30 days of raw events, so older activity needs Sentinel or archived logs. Microsoft also publishes network indicators and SHA-256 hashes in its appendix. Use them as optional pivots across your fleet, taken from the source linked below.


These need Linux process and file telemetry from the Zimbra nodes. Each hunt takes a different angle. All four adapt Microsoft's published Defender queries, and the field names we ported to each platform are ours and test first. The table under the hunts, "Interpret before you escalate", says what is benign and what is not.


Hunt in Splunk: the swatchdog shell with metacharacters in the service value


index=YOUR_LINUX_PROCESS_INDEX
| eval proc=lower(coalesce(process_name, FileName))
| eval cmd=lower(coalesce(process, CommandLine))
| eval parent_cmd=lower(coalesce(parent_process, ParentCommandLine))
| where proc IN ("sh","bash","dash")
| where like(parent_cmd, "%perl%") AND like(parent_cmd, "%.swatchdog_script%")
| where like(cmd, "%snmptrap%")
| where match(cmd, "::zmservicename\s+s\s+.*[;&|<>`$].*\s+\S+-mib::zmservicestatus")
| table _time host user proc cmd parent_cmd
| sort 0 _time

Field mapping: the parent, child and zmservicename logic is adapted from Microsoft's published query for this boundary. The field names follow Splunk's CIM endpoint model (test first).


False positives: a plain health-check snmptrap has no shell grammar in the service value, so matches should be rare.


Empty result: confirm process data is forwarded and the range reaches back to 20 July. Missing data looks like a clean node.


Hunt in Kibana: JSP files written by something that is not Jetty


event.category: file AND event.type: (creation OR change)
AND (file.extension: "jsp" OR file.name: *_jsp.java)
AND file.path: (*/jetty_base/webapps/* OR */jetty/webapps/* OR */mailboxd/webapps/* OR */work/zimbra/jsp/*)
AND process.name: (sh OR bash OR dash OR curl OR wget OR tee OR base64 OR rsync OR perl OR jspawnhelper)

Field mapping: the directories and writer processes come from Microsoft's published JSP-write query. The ECS field names depend on your agent (test first). Add file.path, process.name and process.command_line as columns.


False positives: java is left out of the writer list on purpose, because Jetty itself compiles JSPs into *_jsp.java files in the work directory. Upgrades and admin deployments also write real JSPs.


Empty result: confirm file events are collected and record the writing process. Without the writer you can only list JSPs by name.


Hunt in Falcon CQL: Java or jspawnhelper launching a shell


(#event_simpleName=ProcessRollup2)
| in(field=ParentBaseFileName, values=[java, jspawnhelper])
| in(field=FileName, values=[sh, bash, dash, mkfifo, openssl])
| CommandLine=/( -c | -i|mkfifo|s_client)/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
| sort(@timestamp, order=desc)

Field mapping: the angle follows Microsoft's published Java-to-native-shell query, rebuilt by us on Falcon's ProcessRollup2 fields (test first). Microsoft saw a reverse shell joining a named pipe at /tmp/s to openssl s_client, so mkfifo plus s_client in one chain is the pattern.


False positives: Zimbra and admin scripts call shells from Java for ordinary work. Scope to Zimbra nodes and list the shells you expect first.


Empty result: confirm the sensor reports on these Linux hosts and the window covers the exposure period.


Hunt in KQL: persistence units, sudo and PAM edits, and archive staging


Adapted from the persistence and collection queries in Microsoft's Defender XDR section, merged into one by us. Run it in Defender or Sentinel where Device tables exist.


let lookback = 30d;
union
(
    DeviceProcessEvents
    | where Timestamp > ago(lookback)
    | where ProcessCommandLine has_any ("zimlog.service", "chronyd-helper.service", "syslog_init.service",
        "zimbraPreAuthKey", "pam_exec", "zmstat-fd", "/etc/sudoers.d/",
        "/opt/zimbra/final.tar.gz", "downloadazcopy-v10-linux", "azcopy copy")
    | project Timestamp, DeviceName, EvidenceType="Process", ActionType, FileName, FolderPath,
              Detail=ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
),
(
    DeviceFileEvents
    | where Timestamp > ago(lookback)
    | where FileName in~ ("zimlog.service", "chronyd-helper.service", "syslog_init.service")
        or FolderPath in~ ("/etc/pam.d", "/etc/sudoers.d")
        or (FolderPath =~ "/etc/systemd/system" and FileName endswith ".service")
    | project Timestamp, DeviceName, EvidenceType="File", ActionType, FileName, FolderPath,
              Detail=InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
)
| order by Timestamp desc

Field mapping: the unit names, pam_exec, zmstat-fd, final.tar.gz and AzCopy strings come from Microsoft's published queries and write-up. The rule on any .service file in /etc/systemd/system is ours (test first).


False positives: package installs and configuration management write unit files and sudoers entries. Compare against your change records.


Empty result: confirm Defender for Endpoint on Linux is onboarded on every mailbox node.


Interpret before you escalate


Finding

Benign read

Escalate when

swatchdog shell with snmptrap and metacharacters in the service value

None that we know of

Always. Scope the node, then look for shells, downloads and webshell writes

New .jsp under a Jetty or mailboxd webapps path

A documented Zimbra upgrade or admin deployment

Written by a shell, curl, wget or perl, with no change record, or the same name on a peer node

Java or jspawnhelper launching an interactive shell, mkfifo or s_client

A known admin script

Interactive shell, a pipe to openssl, or a connection to an unfamiliar address

New systemd unit, sudoers.d or pam.d change

Package or configuration management you run

Unit named like Zimbra or logging, copied from /tmp, or a NOPASSWD entry for zimbra

Archive under /opt/zimbra or AzCopy download

A backup job you own

Mailbox backups archived, then AzCopy to storage you do not control. Check egress

Empty results

Nothing matched

Telemetry is missing or retention is short, so fix coverage first


Sources



What This Means for Your Team


Find which Zimbra nodes have zimbra-snmp, then patch to 10.1.20 or later. If patching has to wait, remove the package or turn notifications off, because either one takes the precondition away. Run the hunts back to the fix date rather than the disclosure date, and when one node turns up a hit, check every other mailbox node before you close anything. And be realistic about whether you can run them at all. These hunts need Linux process and file telemetry from the mail servers, which is precisely the place most shops have none: the Zimbra nodes were built by a mail admin, no agent was ever deployed, syslog goes to a box nobody reads, and nobody has looked at a process tree on that host in years. Over 270 compromised instances are already counted in the wild. If you cannot tell whether yours is one of them, that is the finding, and it is worth writing down as clearly as any alert.


Inception Protection


Inception Protection is our managed detection and response service, and it works with the Microsoft licenses you already have. Zimbra logs reach us only if you forward them, so we start there. Then we read the process, file and sign-in evidence around a mail server as one story, and you keep the decisions.


Inception Foresight


If you want to see how well your Microsoft environment would surface activity like this, the free Inception Foresight M365 Assessment is a good place to start: https://www.inceptionsecurity.com/m365assessment. You can also follow Inception Security on LinkedIn and @inceptionsec on X.

bg-map-white.png

INCEPTION SECURITY™

A cybersecurity company with in depth knowledge of the threat landscape and security controls.

NAVIGATION

GET IN TOUCH

© 2025 All Rights Reserved by INCEPTION SECURITY™ .

bottom of page