Open a message in Zimbra's Classic Web Client and you may be handing the sender a running script inside your mailbox. An attacker crafts an email whose body carries markup the Classic client fails to fully sanitize, sends it to you, and when you open the message that markup executes as JavaScript in the origin of your webmail session. There is no attachment to launch and no link to click past opening the mail. Zimbra shipped the fix in Collaboration 10.1.19, Google's Threat Analysis Group reported the flaw, and no CVE number is attached yet. The missing identifier is part of the problem: a stored cross-site scripting bug with no CVE and a quiet vendor release note is exactly the kind of fix that slips past a busy mail admin who patches by watching the KEV feed.
Here is who should care. If you run self-hosted Zimbra Collaboration, on-prem or on your own cloud instance, and your users can reach the Classic Web Client, this belongs at the front of your patch queue this week. If your mail lives entirely in a hosted provider like Microsoft 365 or Google Workspace and you run no Zimbra, this one is not yours and you can close the tab. Everyone in between - shops that flipped the default to the Modern UI months ago but never removed the Classic client - should run the check in the next section before deciding, because Classic usually still answers on its own URL even when nobody sees it in the menu. Zimbra webmail has been a repeat target for exactly this class of bug; attackers have used prior XSS flaws in the wild to lift session tokens and quietly forward mail out of high-value mailboxes, so treat a rendering-time script bug here as a mail-theft problem, not a cosmetic one.
What the flaw actually does
This is a stored cross-site scripting vulnerability in the Classic Web Client's message rendering. The attacker controls the content of an email - the HTML body, or a header or field the client reflects into the page - and the Classic client renders it without stripping the active markup. When you view the message, the browser runs the attacker's script under the webmail application's own origin, inside your authenticated session. Google's Threat Analysis Group reported it, and Zimbra's own Security Center is the authoritative place to confirm the affected builds and the fixed release as the advisory lands.
The word "stored" is what makes this worse than a reflected XSS you would have to be tricked into clicking. The payload lives in the message. It sits in the mailbox until the message is opened, and it fires on a normal action every mail user performs dozens of times a day. What a script running in your session buys the attacker is broad:
- Session and token theft. The script can read the authenticated session context and drive authenticated requests to Zimbra's SOAP API as you, without ever knowing your password.
- Silent mail exfiltration. It can create a server-side filter or a forwarding address that copies inbound mail to an attacker-controlled inbox, so the theft continues long after the original message is deleted.
- Mailbox reconnaissance and pivot. It can read and search your existing mail, and if the reader is a domain or global admin, the same script reaches admin-only functions and turns one opened email into a foothold on the whole server.
None of that requires a second victim action. The interaction cost is one message open, which is why a webmail XSS with a working payload is closer to a click-to-own than a theoretical finding.
The reason this lands on the Classic client specifically comes down to how the two interfaces treat message HTML. The Classic Web Client is the older AJAX and HTML front end that many organizations kept for muscle-memory reasons after Zimbra shipped the Modern UI. It renders a more permissive slice of message markup, and the sanitization gap that 10.1.19 closes lives in that path. The Modern UI handles the same message through a different rendering pipeline and is not the target here. That distinction is why your default-UI setting matters to the risk math: users parked on Modern are far less exposed, but a Classic client still reachable by URL keeps the vulnerable path open for anyone whose crafted email drives them to it.
Find out whether you are exposed before you do anything else
Two questions decide your exposure: what Collaboration build you are on, and whether the Classic Web Client is reachable. Run this on the Zimbra host as the zimbra service user. Anything below 10.1.19 is unpatched, and a Classic client that answers on /h/ is reachable whether or not it is your default UI.
# On the Zimbra mailbox host, as the zimbra user
su - zimbra -c "zmcontrol -V" # exact build; must be 10.1.19 or later
su - zimbra -c "zmcontrol status" # confirm mailbox service is the one running
# Is the Classic Web Client still reachable by URL?
# The Classic HTML client answers under /h/ even when Modern is the default.
curl -sk -o /dev/null -w "%{http_code}\n" https://mail.example.com/h/
# 200 or 302 means Classic is live and this bug is in scope for your users.
# What UI does a normal login land on? (domain- and COS-level default)
su - zimbra -c "zmprov getDomain mail.example.com zimbraWebClientDefaultUI"
su - zimbra -c "zmprov getCos default zimbraWebClientDefaultUI"
If the build is 10.1.19 or newer, you are patched and can move to the detection step to confirm nothing happened during the exposure window. If the build is older and /h/ answers, assume your users can be hit and keep reading. The relevance verdict is that plain: patched build plus no Classic client equals safe; anything else is in scope.
Patch to 10.1.19, and what to run tonight if you cannot
The real fix is the upgrade. Zimbra corrected the sanitization in Collaboration 10.1.19; the version-specific release notes list the exact packages and the upgrade order. Take a snapshot, apply the patch, and verify the build afterward. If an upgrade window is not available before your users open tomorrow's mail, reduce the blast radius by taking the Classic client out of reach at the reverse proxy and pushing everyone to the Modern UI in the meantime.
# Interim mitigation while you schedule the 10.1.19 upgrade.
# 1) Make Modern the default UI at the domain and COS level.
su - zimbra -c "zmprov modifyDomain mail.example.com zimbraWebClientDefaultUI modern"
su - zimbra -c "zmprov modifyCos default zimbraWebClientDefaultUI modern"
# 2) Block the Classic client path at the front-end proxy so it cannot be
# reached by direct URL. In the Zimbra nginx template, or your WAF:
# location ^~ /h/ { return 403; }
# Then reload the proxy so the rule takes effect.
su - zimbra -c "zmproxyctl restart"
# 3) Confirm Classic no longer answers.
curl -sk -o /dev/null -w "%{http_code}\n" https://mail.example.com/h/ # expect 403
Be honest with yourself about what the interim step buys you. Forcing the Modern default keeps ordinary users off Classic, but until you block /h/ at the proxy the client is still one URL away for anyone who has it bookmarked - or anyone whose crafted email links straight to it. The proxy block is the control that actually closes the path; the UI default is convenience. Neither replaces the upgrade, because the sanitization gap lives in the client code, and only 10.1.19 fixes that.
Assume some mail was opened, then go hunt for it
Because the payload is stored and fires on a routine open, you cannot assume the exposure window was quiet just because no alarm fired. Treat every day the server ran an unpatched Classic client as a day a crafted message could have executed. The tells of post-exploitation are the persistence an attacker sets up through your own session: new mail filters, new forwarding addresses, new data sources, and admin SOAP calls from odd places. Pull them from the mailbox and audit logs.
# On the Zimbra host - hunt the last two weeks of persistence changes.
cd /opt/zimbra/log
# Server-side filter, forwarding, and data-source changes (the quiet exfil setup)
grep -Ei "ModifyFilterRules|zimbraMailForwardingAddress|CreateDataSource|AddDataSource" \
mailbox.log audit.log
# Admin-privileged SOAP actions - review who ran them and from where
grep -Ei "DelegateAuthRequest|GetAdminSavedSearches|CreateAccountRequest|ModifyAccountRequest" \
audit.log
# Classic client hits and SOAP traffic by source IP (spot session reuse from new IPs)
awk '$0 ~ "/h/" || $0 ~ "/service/soap" {print $1}' nginx.access.log \
| sort | uniq -c | sort -rn | head -20
If you find a forwarding address or filter nobody created, or admin SOAP calls from an IP that has no business holding an admin session, work the mailbox as compromised. Remove the rogue rule, invalidate active sessions so a stolen token stops working (zmprov modifyAccount user@example.com zimbraAuthTokenValidityValue $(date +%s) forces reissue), reset the account password, and review what that mailbox could read. For an admin account, widen the review to every account it could delegate into. The forwarding-rule check is the highest-value one on the list, because a silent copy of inbound mail is how a single opened message becomes months of quiet collection.
Upgrade to 10.1.19 tonight, then read back what Classic already rendered
Mail is the account-recovery backbone for every other system you run, so a webmail foothold is rarely the end of the story - it is the start of a password-reset spree across everything that emails a reset link. That is why this earns a same-week change window even without a CVE to point compliance at. Confirm your build, get to 10.1.19, block /h/ until you do, and hunt the last two weeks of filter and forwarding changes so a quiet exfiltration rule does not outlive the patch. If you want a second set of eyes on a Zimbra estate - the build inventory, the proxy hardening, and the mailbox hunt - that is the kind of engagement we run, and the checks above are yours to keep either way.
Want a second set of eyes on your mail server exposure?
Red Hound helps teams inventory their Zimbra and mail estate, harden the front-end proxy, and build the mailbox detections that turn a webmail foothold into an alert instead of a quiet forwarding rule. Book a 30-minute working session and we will map your exposure to this Classic Web Client bug and the class of webmail XSS behind it.
