CVE-2026-34486: Apache Tomcat EncryptInterceptor Fail-Open Bypass
How a one-line refactor moved cluster-message decryption from fail-closed to fail-open, letting an unauthenticated attacker reach Tomcat's Java deserialization path on port 4000.

On April 9, 2026, the Apache Tomcat security team disclosed CVE-2026-34486, a fail-open regression in Apache Tomcat's Tribes cluster encryption, reported by Bartłomiej Dmitruk of Striga. Apache rates it Important, and NVD carries a 7.5 High CVSS 3.1 score, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N, classifying it as Missing Encryption of Sensitive Data (CWE-311). It affects Tomcat 9.0.116, 10.1.53, and 11.0.20, the exact releases that shipped the incomplete fix for CVE-2026-29146, and is fixed in 9.0.117, 10.1.54, and 11.0.21. A single line moved during that earlier fix turned the EncryptInterceptor from fail-closed to fail-open, so a cluster message that fails decryption is now forwarded up the handler chain instead of dropped. The finder and several public proof-of-concept repositories show that same path delivering unauthenticated remote code execution through Java deserialization on port 4000. Tomcat 8.5 is not affected.
Running Tomcat toward or past end of life? See NES for Apache Tomcat.
What is CVE-2026-34486?
CVE-2026-34486 is a fail-open flaw in the EncryptInterceptor used by Apache Tomcat's Tribes clustering framework. Apache assigned it CWE-311 (Missing Encryption of Sensitive Data). The finder frames the same bug as fail-open behavior (CWE-636) that opens a deserialization-of-untrusted-data path (CWE-502), and that second framing is the one that matters for your risk model.
Tomcat's Tribes framework replicates HTTP session state across a cluster. When a session changes on one node, the change is serialized and broadcast to the other nodes over TCP. The receiver listens on port 4000 by default, and it binds to the primary network interface rather than localhost. The EncryptInterceptor sits in the channel's interceptor chain and is supposed to decrypt each incoming message before it reaches GroupChannel.messageReceived(), where the payload is deserialized by XByteBuffer.deserialize(). That deserialize call creates a bare ObjectInputStream with no ObjectInputFilter and calls readObject().
The intended security model is simple. If you configure the EncryptInterceptor, only messages encrypted with the shared cluster key should ever reach the deserialization layer. Everything else should be discarded. CVE-2026-34486 breaks that guarantee: after the regression, a message that fails decryption is passed on to the deserialization layer anyway.
CVE-2026-34486 is a regression from CVE-2026-29146, a separate padding-oracle flaw in the same interceptor. The fix for the padding oracle is what introduced this one, which is why only the releases carrying that fix are affected.
Severity and exploit conditions
The Apache Tomcat security team publishes textual severity ratings rather than CVSS scores, and it rates CVE-2026-34486 Important. The 7.5 High CVSS 3.1 score below comes from NVD as a secondary assessment by CISA's ADP program, and a second scorer in NVD agrees on the same vector.
There is an important gap between what this vector scores and what the bug actually does. The C:H/I:N/A:N vector describes the encryption bypass in isolation: an attacker on the network can read cluster traffic that the operator configured the interceptor to protect. It does not capture the code execution the bypass enables. The finder's own writeup, Fail Open, Game Over, and multiple public proof-of-concept repositories demonstrate unauthenticated remote code execution through Java deserialization on the same fail-open path. Treat the 7.5 as a floor, not a ceiling.
Exploitation is not universal, though. Four conditions have to hold for the RCE path to be reachable, and stating them honestly matters more than inflating the affected count:
- Tribes clustering is enabled (a <Cluster> element with a GroupChannel in server.xml).
- The EncryptInterceptor is configured in that channel. This bug is specifically about that interceptor failing open.
- The Tribes receiver port (default 4000) is reachable by the attacker.
- A deserialization gadget library is present on the server classpath. Without gadget classes the deserialization still happens (provable with a URLDNS callback), but a full command-execution chain needs a library such as Commons Collections 3.x, which Spring, Hibernate, and similar frameworks routinely pull in transitively.
A standalone, non-clustered Tomcat is not exposed through this path.
Root cause: one line moved out of a try block
The bug is unusually easy to see, which is part of why it reproduces so cleanly. CVE-2026-29146 identified that the interceptor's default algorithm (AES/CBC/PKCS5Padding) was vulnerable to a padding-oracle attack. The March 13, 2026 fix refactored the encryption manager to support additional algorithms. During that refactor, one structural change slipped through.
Before the fix, super.messageReceived(msg) was inside the try block. If decryption threw, control jumped to the catch block, the error was logged, and the method returned. The message was dropped. Fail-closed.
// Before (fail-closed): forwarding happens only on successful decrypt
try {
data = encryptionManager.decrypt(data);
// ... rewrite the buffer with the decrypted bytes ...
super.messageReceived(msg); // inside the try
} catch (GeneralSecurityException gse) {
log.error(...); // decrypt failed -> log and return
}
// After (fail-open): forwarding happens unconditionally
try {
data = encryptionManager.decrypt(data);
// ... rewrite the buffer with the decrypted bytes ...
} catch (GeneralSecurityException gse) {
log.error(...); // decrypt failed -> log, then fall through
}
super.messageReceived(msg); // now runs even when decryption threw
After the change, super.messageReceived(msg) runs unconditionally. When decryption fails, the catch block logs the error and then the original, unmodified, attacker-controlled bytes are forwarded up the chain to XByteBuffer.deserialize(). The send path in the same class still throws on encryption failure, so outbound messages stay encrypted. The interceptor is fail-closed on send and fail-open on receive. That asymmetry is the whole vulnerability.
There is no membership check to catch the injected message either. NioReceiver accepts any TCP connection, and ChannelCoordinator.accept() returns true unconditionally. The Tribes wire envelope has no cryptographic protection of its own, so nothing between the socket and the deserialization call ever verifies that the sender is a real cluster member.
Exploitation status
CVE-2026-34486 is in the CISA Known Exploited Vulnerabilities catalog, added August 4, 2026, with a federal remediation due date of August 7, 2026. CISA records exploitation status as active, automatable as yes, and technical impact as partial. Its KEV entry also notes the vulnerability can be chained with CVE-2025-24813, the partial-PUT path-equivalence RCE that is itself in the KEV catalog. Two actively exploited Tomcat RCE paths that combine is a bad pairing to leave unpatched.
Public proof-of-concept code is widely available and mature. The finder published a technical writeup and a coordinated-disclosure reproduction, and several independent PoC repositories followed within days of the April 9 disclosure. What makes this CVE notable for defenders is not that PoCs exist but how low they set the bar: the reproductions are self-contained and run with a single command.
We do not publish weaponized payloads or targeted attack recipes. What follows is the shape of the public reproductions, which is what a poc or exploit searcher actually needs in order to test their own environment and understand the risk.
How the public reproductions work
The public PoCs converge on the same design, and understanding it tells you exactly what to test for.
A typical reproduction is fully Dockerized. It builds an image, starts a vulnerable Tomcat (11.0.20 is common) with a two-node cluster and the EncryptInterceptor configured, generates a serialized Java payload, sends it unencrypted to the Tribes receiver on port 4000, and then verifies code execution by checking for a marker file such as /tmp/pwned inside the container. The entire sequence is one command, for example bash run.sh or a single shell script that installs its own dependencies. There is no manual setup, no timing to get right, and no authentication step, because there is nothing to authenticate against.
The payload is a standard ysoserial CommonsCollections6 (CC6) gadget chain. CC6 is the relevant choice because it uses HashSet as its deserialization entry point rather than the older AnnotationInvocationHandler path that JDK 8u72 broke, so it works on modern runtimes including Java 17 and 21. The gadget is wrapped in a minimal Tribes protocol frame (a fixed header, a length, a ChannelData structure with a synthetic member address, and a footer) and written straight to the socket. The interceptor tries to decrypt it, fails with an AEADBadTagException or BadPaddingException, logs a single decrypt-failure line, and forwards the raw bytes anyway. They reach ObjectInputStream.readObject() with no filter, and the chain executes.
The tell in the logs is minimal and easy to miss during triage. On a successful exploitation attempt Tomcat records one SEVERE line, "Failed to decrypt message," with a crypto exception. No deserialization error follows, because from Tomcat's point of view nothing went wrong after the decrypt failure. One logged decrypt failure with no other error, on a clustered node, is the signal worth alerting on.
What an attacker can do
A remote, unauthenticated attacker who can reach an affected node's Tribes receiver can:
- Connect to the receiver (default TCP 4000) with no credentials. It binds to the primary interface, accepts any TCP connection, and performs no membership verification on the data channel.
- Send a raw, unencrypted Tribes message wrapping a serialized Java object. The EncryptInterceptor fails to decrypt it, logs one error, and forwards it into the deserialization layer regardless.
- Achieve remote code execution as the Tomcat process user when a gadget library is on the classpath, which is the common case for real applications built on Spring, Hibernate, or other frameworks that carry Commons Collections transitively.
- Hit every cluster member, since the injected message reaches each node's receiver on the same port.
- Read sensitive session and cluster data that the EncryptInterceptor was configured to protect. This is the confidentiality impact the CVSS vector actually scores, and it stands even where no RCE gadget is present.
The blast radius is worst in container platforms. As the finder notes, in a Kubernetes deployment without a NetworkPolicy, any pod in the same namespace can reach port 4000 on every Tomcat cluster member. The port is not something most teams think to lock down, because the interceptor was supposed to make it safe.
Who is affected?
Only the three specific releases that shipped the incomplete CVE-2026-29146 fix are affected. Earlier releases on each line predate the regression, and the next patch release on each line fixes it.
Everyone affected by CVE-2026-34486 is on a currently supported branch. This is a straight upgrade. If you are on 9.0.116, 10.1.53, or 11.0.20 with clustering and the EncryptInterceptor configured, move to the next patch release today. There is no end-of-life exposure to this specific CVE, and no case where you need remediated builds for it.
Additionally, Tomcat 8.5 is not affected by CVE-2026-34486. The regression rode in on the CVE-2026-29146 fix, and 8.5 reached end of life on March 31, 2024, long before that fix existed, so it never received the change that introduced this bug. Do not let a scanner that flags "Tomcat 8.5, EncryptInterceptor" push you toward treating this CVE ID as an 8.5 problem. It is not.
The "see note below" for the EOL rows points at the real end-of-life story, which is not this CVE but the pattern around it.
Mitigation guidance
Related CVEs
CVE-2026-34486 sits inside a tight cluster of related Tomcat issues worth reviewing together:
- CVE-2026-29146: the padding-oracle flaw in the same EncryptInterceptor whose fix introduced this regression. The parent of this bug, and the reason the affected version set is so narrow.
- CVE-2025-24813: the partial-PUT path-equivalence RCE that CISA flags as chainable with CVE-2026-34486. Both are in the KEV catalog. We covered the mechanics in our deep dive on CVE-2025-24813.
- CVE-2025-31651: a rewrite-rule access-control bypass, another remotely reachable Tomcat flaw where NVD and Apache scored the impact very differently.
For the broader Tomcat picture, see our August 2026 round-up of 11 CVEs fixed in 9.0.121 and the Apache Tomcat versions and EOL dates reference.
Taking action
If you run a clustered Tomcat on 9.0.116, 10.1.53, or 11.0.20 with the EncryptInterceptor configured, upgrade to 9.0.117, 10.1.54, or 11.0.21 now. It is a patch-level bump, it closes an actively exploited RCE path, and there is no reason to wait. While you are in there, firewall port 4000 to cluster members and confirm you actually need Tribes clustering at all.
The more useful takeaway is what this CVE shows about the cost of staying on a branch after upstream walks away. Look at the timeline: a padding oracle reported in February, a fix in March, that fix quietly introducing a fail-open RCE path in the same release, a re-fix within weeks, and a CISA KEV listing with active exploitation by August. That is the normal churn of a live security surface, and it is trivially reproducible with a one-command PoC. On a supported branch you get every one of those fixes for free. The day your branch goes end of life, that stream stops, and the vulnerabilities keep coming.
Tomcat 8.5 has been living that reality since March 31, 2024. Our August 2026 round-up counted 48 CVEs confirmed to affect 8.5 with no upstream fix, one of them actively exploited. Tomcat 9.0.x is next: public support ends March 31, 2027. If you are running 9.0.x today, you have a window to plan, and the question is not whether you will hit a vulnerability like CVE-2026-34486 after that date but how you will patch it when Apache no longer will.
That is what NES for Apache Tomcat is for: drop-in replacement builds that resolve vulnerabilities on end-of-life Tomcat branches, so you migrate on your roadmap instead of on a CVE's. If Tomcat is in your estate and a 2027 migration is not realistic, talk to us about coverage before the deadline, not after.
Resources
View All Articles


