Back to Blog

If decryption fails, the message goes through anyway

One line outside the try
CVE-2026-34486 · Apache Tomcat 11.0.20 · 10.1.53 · 9.0.116

On 30 March, Mark Thomas pushed a commit to Apache Tomcat titled "Better error handling - partial revert of 6d955cc". In the source file, one line added and one removed; the other three are the changelog entry, which settles for a flat "Better error handling for the EncryptInterceptor". What that change did was move the call that processes a cluster message inside the try block. Because it was outside it. And while it was outside, when decryption of a message failed, Tomcat wrote the error to the log and processed the message anyway.

That is CVE-2026-34486, which on 4 August entered CISA's exploited-vulnerabilities catalogue with a remediation deadline of the 7th — the same batch that carried the 9.8 in Langflow we wrote about this morning. It is worth saying where each number comes from, because they do not share a source: Apache, as CNA, only rated it "Important", with no score; the 9.8 you will see in NVD was added by CISA — network access, no prior authentication, no user interaction, high impact across all three legs; and Red Hat, looking at the same flaw, stops at 7.5. Three different readings that agree on the only part you need to keep: it is reachable over the network and it needs no credential. All of that, from one line in the wrong place.

What the EncryptInterceptor protects

When you cluster several Tomcats so that a user's session survives a node going down, the nodes talk to each other over their own channel — Tribes — listening on port 4000 by default. What travels there is not HTTP requests: it is serialised Java objects. The project documentation is literal about it: every session attribute must implement java.io.Serializable, and what goes over the TCP socket is "the serialized data".

Tomcat's own documentation opens the cluster security chapter with a sentence worth reading slowly: "The cluster implementation is written on the basis that a secure, trusted network is used for all of the cluster related network traffic. It is not safe to run a cluster on a insecure, untrusted network." And on the encryption interceptor it says that, correctly configured, it provides confidentiality and integrity protection, but that it does not protect against all risks of running the cluster on a network you do not control. The EncryptInterceptor is not the door: it is the padlock you put on a door that should already be inside the house.

The patch's patch

The timeline is public and you can follow it commit by commit. It is worth doing, because it explains how this happened:

  • 22 February 2026: someone reports CVE-2026-29146. The EncryptInterceptor used CBC by default, vulnerable to a padding oracle attack. A real, textbook flaw.
  • 13 March: the fix lands — a large commit that adds support for more algorithms, changes the recommendation to AES/GCM/NoPadding and logs a warning when it detects the insecure algorithm. Good work. And along the way, among the 64 lines it changes in that file, it moves the super.messageReceived(msg) call outside the try.
  • 20 March: Tomcat 11.0.20 ships with that fix inside. (11.0.19 existed but its release vote did not pass, so getting the patch meant going to 20.)
  • 26 March: six days later, someone reports that the fix can be bypassed. That is CVE-2026-34486.
  • 30 March: the one-line commit, described as a "partial revert" of the previous one. The fixed versions ship on 2, 3 and 4 April (10.1.54, 9.0.117 and 11.0.21).
  • 9 April: both CVEs are published. 4 August: CISA adds the second one to KEV. Four months to the day from the last fixed version shipping to people using this to get in.

Failing open

The vulnerable code, boiled down, was this:

} catch (GeneralSecurityException gse) {
    log.error(sm.getString("encryptInterceptor.decrypt.failed"), gse);
}
super.messageReceived(msg);   // outside the try: always runs

And the fix: the same call, inside the try. That is all.

It is worth landing what "goes through anyway" actually is, because in the abstract it sounds harmless: it is a Java object nobody could verify entering the cluster's processing chain — the same chain that ends up deserialising sessions. In the assessment CISA attaches to the NVD record, dated 4 August, exploitation is listed as active and automatable. CISA does not publish how it is being used, and we are not going to invent it; with those two adjectives and a three-day deadline for federal agencies, there is already enough to move the update from the "pending" list to the "today" list.

The striking part is not the slip — slips are human, and anyone who has written Java has made one. The striking part is which way it fails. A security control can fail in two directions: closed (if I cannot verify this, I drop it) or open (if I cannot verify this, I let it through and write a note about it). The first causes availability incidents and calls to support. The second causes nothing at all: everything keeps working, users do not complain, the dashboards are green, and the protection has been off for weeks.

And note the detail that interests us most: the signal was there. Every message that could not be decrypted left a Failed to decrypt message in the node's log. This is not a silent failure: it is a failure that announced itself and nobody was listening. A healthy cluster never produces that message; seeing it means, at best, that two nodes do not share the same key, and at worst, that somebody is sending things to your port 4000. It is exactly the kind of line that should have an alert behind it — something we wrote about in our post on alert fatigue: the problem is almost never missing information, it is having too much and nobody knowing which line matters.

The flaw only hit those who had bothered to encrypt

Here is the uncomfortable part. Look at the affected versions NVD publishes: 11.0.20, 10.1.53 and 9.0.116. Not "from X to Y". Three individual versions. Precisely the three that carried the padding-oracle patch. Anyone one version behind was never exposed to this (they had the previous flaw instead, which is hardly a prize). And the EncryptInterceptor is not on by default: you have to add it by hand to the channel's interceptor pipeline. In other words, to catch this CVE you had to meet three conditions in a row: run a cluster, have bothered to encrypt the channel, and have updated quickly.

You can spin this into a lazy moral — "see what rushing gets you" — and that would be dangerous nonsense. The flawed version was out for fifteen days on the 11.0 branch, from 20 March to 4 April; the alternative was keeping an encryption scheme that could be broken another way. Neither option is good. The useful conclusion is a different, more boring one: what saves you is not patching fast or slow, it is knowing exactly which version you are on and being able to change it the same day you need to. If you are learning today that 11.0.20 had this and you cannot say from memory — or with one command — what each of your nodes runs, that is the problem to fix, not the cadence. We wrote about it at length in "latest is not a version", and this case illustrates it better than we did.

"Better error handling"

The changelog entry for the fixed version reads, literally, "Better error handling for the EncryptInterceptor". No "bypass", no "security". To be clear, the project did the right thing: it published the CVE on its security page, with the severity, the affected versions and a link to the commit. But if your process for deciding whether an update is urgent consists of reading the changelog and seeing whether it "sounds like security", this is the perfect counterexample. A changelog tells you what changed; the project's security page tells you what can happen to you if you do not change. They are two different documents and only one of them is useful for prioritising.

This afternoon's five minutes

If you have Java in production — and there is far more of it than shows up in inventories, usually underneath a line-of-business application another department bought eight years ago — this is what we check:

  • The exact version, on every node. Not the one in the inventory: the one in the process running right now. If it says 11.0.20, 10.1.53 or 9.0.116, there is work to do today.
  • Is the <Cluster> block enabled in server.xml? Declared clusters that nobody uses, inherited from a template, are more common than you would think. If you are not replicating sessions, that block should not be there.
  • Who can reach the 4000-4100 range? That is the receiver default port plus the hundred after it, because Tomcat auto-binds to the first free one. From outside the nodes' segment, the answer has to be "nobody". Not because encryption is bad, but because encryption is the second line, and this week we have seen what happens when the second line switches itself off.
  • An alert on Failed to decrypt message. And on the insecure-algorithm warning Tomcat itself writes if you are still on CBC. Two log lines, two alerts, ten minutes of work.
  • How you find out about the next one. The CISA KEV feed and the security pages of the products you actually run, read by an actual named human being. It is the least glamorous part of managed cybersecurity and the one that most often prevents the bad day.

And one thing that is not in the advisory but has always been in Tomcat's documentation: traffic between cluster nodes should never leave a segment you control. When the nodes sit in different places — two offices, two data centres — that means a tunnel, not "the public IP, the interceptor encrypts it anyway". It is the conversation we have every time somebody asks us to build the network between their sites: what goes inside, what goes outside, and who gets to talk to whom.

How many of your controls fail open?

That is the question we take away from this, and it has nothing to do with Tomcat. The proxy inspecting your outbound traffic: if it goes down, does traffic stop or go straight out? The spam filter: if analysis times out, does it hold the message or deliver it? Conditional access: if the service evaluating the policy does not answer, does it deny or grant? The EDR agent: if the service is stopped, does anyone find out the same day? Every one of those answers is a design decision somebody made, often without writing it down anywhere, and almost always in favour of things continuing to work. That is a legitimate decision — there are systems where failing closed is worse than the attack — but it has to be a decision, not an inherited accident.

We have written that catch ourselves. Anyone who has been writing code for years has: you swallow the exception so the service does not fall over at three in the morning, and in doing so you turn a noisy error into a silent one. That is why the rule we apply when reviewing an architecture is not "is encryption enabled?" but "what exactly happens when this fails, and who finds out?". The first question is answered with a screenshot. The second has to be tested: switch the control off on purpose, inside a maintenance window, and see whether something breaks or somebody shouts. If nothing breaks and nobody shouts, you already know which way it fails.

If you are not sure what is running underneath your applications — or you are sure, but nobody reads the logs on those machines — get in touch. The first conversation is usually short: which versions, who can reach what, and what happens when something breaks.

Sources (verified): CVE-2026-34486, "Missing Encryption of Sensitive Data vulnerability in Apache Tomcat due to the fix for CVE-2026-29146 allowing the bypass of the EncryptInterceptor", affecting 11.0.20, 10.1.53 and 9.0.116, fixed in 11.0.21, 10.1.54 and 9.0.117, CVSS 3.1 score of 9.8 (critical, vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) added by CISA-ADP as a secondary metric in NVD, not by the project: the Apache Software Foundation, as CNA, only rated it "Important" with no score, and Red Hat scores it 7.5; the CISA Coordinator SSVC assessment of 4 August 2026 included in the record itself marks "exploitation: active" and "automatable: yes". Published 9 April 2026 — NVD; the "Important" rating, the report date (26 March 2026), the publication date and the commit links — Apache Tomcat 11 security advisories (equivalents for the 10.1 and 9.0 branches); CVE-2026-29146 (EncryptInterceptor using CBC by default, vulnerable to a padding oracle attack, reported 22 February 2026); commits 6d955cce (13 March 2026) and 1fab40cc (30 March 2026, "Better error handling - partial revert of 6d955cc", 2 files, 4 insertions and 1 deletion) — Apache Tomcat repository; the default Tribes receiver port 4000, the java.io.Serializable requirement, the trusted-network premise and the scope of the EncryptInterceptor — Tomcat 11 clustering documentation; the encryptInterceptor.decrypt.failed and encryptInterceptor.algorithm.switch log messages — the project's own LocalStrings.properties; addition to the Known Exploited Vulnerabilities catalogue on 4 August 2026 with a 7 August deadline — CISA KEV. CISA does not publish details of how it is being exploited; we are not making them up. The readings, the fail-open rule and the operational recommendations are ours.

Do you know which way your controls fail?

At everyWAN we check versions, who can reach what, and what happens when a protection stops working. No scares and no slide decks: commands, logs and a list of what needs fixing.

Talk to everyWAN

Tags:

Share:

Subscribe to our newsletter

To receive IT stories, everyWAN news and exclusive subscriber offers, sign up to our mailing list

Minorisa de Sistemas Informaticos y Gestión S.L. © 2026
everyWAN
everyWAN