The Delivery Got the Headlines

TA488 spent eight months reading mail out of Zimbra servers belonging to Ukrainian government agencies, US defense industrial base contractors, and nuclear installations. Proofpoint published the writeup Thursday, alongside a joint advisory from CISA, NSA, FBI and partner agencies.

The delivery got the headlines, and it earns them. A message arrives carrying markup that's been split apart with fake CSS @import directives and HTML comments. Zimbra's own sanitizer strips those @import sequences, the surviving fragments close back up into <svg onload=eval(atob(...))>, and the browser runs what the sanitizer just finished assembling. No link, no attachment, nothing to click. Opening the message is the whole interaction, which is why Proofpoint calls it half-click rather than zero-click. The flaw is CVE-2025-66376, a stored XSS in the Classic UI.

Underneath the delivery are two properties November's patch never touched, because neither one's a bug.

It Never Needed Privileges of Its Own

ZimReaper never needed privileges of its own. It ran as JavaScript inside the browser page of a user already authenticated to their own mailbox and inherited that session: CSRF token, autofilled password read back out of hidden login fields it created, 2FA scratch codes, the full Global Address List enumerated two characters at a time, and 90 days of mail pulled out through Zimbra's own export function as a TGZ archive. Every one of those was a legitimate, correctly authenticated API call. The credentials and tokens went out Base32-encoded over DNS queries, the mail archive over HTTP POST.

Proofpoint notes the browser-side execution leaves most EDR and antivirus without a clear forensic trail, which is the polite version. No binary, no child process, no impossible-travel alert, because nothing anomalous occurred. Somebody opened their mail.

Then It Left Itself a Key

Then it called CreateAppSpecificPasswordRequest and minted a password named ZimbraWeb, granting IMAP, POP3 and SMTP without two-factor.

Then comes Monday. Patch to 10.1.13, or 10.0.18 if you're still on the 10.0 branch. Force a password reset. Re-enroll everyone in 2FA. Three correct actions, none of which touch an app-specific password, because surviving a password reset is the entire reason app passwords exist.

That's the Standard Configuration for an AI Agent

Strip the tradecraft off and you have a process that ran with a human's session authority and provisioned itself a durable non-human credential before anyone noticed either thing.

That's the standard configuration for an AI agent.

An agent under a user's OAuth grant produces calls your logs attribute to the user. Give it the ability to create service accounts, API keys, or MCP credentials and it leaves artifacts that outlive the session, outlive the incident, and never appear on an offboarding checklist written for people who quit. Anthropic's platform team sketched exactly this future on camera, cheerfully, and they were right that a per-agent service account is what makes an agent auditable. The account is also what makes it durable. Both things are true at once, and only one of them shows up in the demo.

The controls that catch this are the ones that never lived in the model: identity, egress, metering, audit, and a kill switch. A prompt is a request. A revoked credential is a fact.

The Patch Was Ours to Apply

I deploy on-premises AI for a living, and these were on-premises boxes. Zimbra shipped 10.1.13 and 10.0.18 on November 6. TA488 kept working unpatched servers into February. CISA didn't add the CVE to the Known Exploited Vulnerabilities catalog until March 18, with an April 1 deadline for federal agencies. Zimbra 10.0 went end of life December 31. Eight months after the fix shipped, the joint advisory says unpatched instances still being exploited.

On-prem puts the patch in your hands, and the failure mode with it. Nobody applied this one for months.

Go Build the Inventory

Grep /opt/zimbra/log/audit.log for CreateAppSpecificPassword and revoke anything named ZimbraWeb. Patching closes the door the payload walked through; it doesn't touch the key the payload cut for itself.

Then go build the list of every non-human credential in your environment and the human who approved each one. Service accounts, API keys, app passwords, MCP tokens, CI runners, the lot. When I run a deployment on site, that inventory is the first artifact I build, before a single model gets selected. It's routinely the longest week of the engagement.

Frequently Asked Questions

What is CVE-2025-66376?

It's a stored cross-site scripting flaw in Zimbra's Classic web client, exploited by splitting HTML with fake CSS @import directives that the sanitizer strips back into working JavaScript. Zimbra fixed it in ZCS 10.1.13 and 10.0.18 on November 6, 2025. CISA added it to the Known Exploited Vulnerabilities catalog on March 18, 2026, with an April 1 federal deadline.

Does patching Zimbra remove ZimReaper's access?

No. Patching closes the delivery path and doesn't touch an app-specific password the payload already created. They're built to survive password resets, so IMAP, POP3 and SMTP access continues without two-factor until somebody revokes the app password itself. Check /opt/zimbra/log/audit.log for CreateAppSpecificPassword entries and revoke anything named ZimbraWeb.

Why didn't EDR or antivirus catch it?

Because nothing anomalous ran. Execution happened inside the browser's JavaScript engine on a session the user had legitimately authenticated, so there wasn't a binary to scan, a child process to flag, or an impossible-travel signal to fire. Every API call was correctly authenticated. Proofpoint's own wording is that browser-side execution leaves most EDR and antivirus without a clear forensic trail.

What does a Zimbra exploit have to do with AI agents?

Both run with a human's session authority and can provision credentials that outlive the session. An agent holding a user's OAuth grant generates activity your logs attribute to that user, and an agent permitted to create service accounts or API keys leaves artifacts behind that no offboarding process is looking for. It's the same shape.

What belongs in a non-human credential inventory?

Every credential in the environment that isn't a person, plus the human who approved each one. Service accounts, API keys, app-specific passwords, OAuth grants, MCP tokens, CI runners, and machine certificates, each with an owner, a scope, an expiry, and a revocation path somebody has tested. It's the first artifact I build on a deployment, and it's the one most organizations have never built.

Summary: TA488's ZimReaper payload exploited CVE-2025-66376 to run inside authenticated Zimbra sessions, then minted an app-specific password that survived password resets and 2FA re-enrollment. A process running with human session authority that provisions its own durable credential is the default configuration of an AI agent, which is why the first deliverable on any deployment is an inventory of every non-human credential and the human who approved it.