Apache Tomcat EncryptInterceptor Bypass via CVE-2026-29146 Fix Error - Missing Encryption of Sensitive Data
What's the vulnerability?
CVE-2026-34486 is a Missing Encryption of Sensitive Data vulnerability in Apache Tomcat's EncryptInterceptor that allows an attacker to bypass encryption entirely. The vulnerability was inadvertently introduced by an error in the fix for CVE-2026-29146 (padding oracle vulnerability), where a code refactoring error moved the messageReceived() call outside the exception handling block. This means if decryption fails, the encrypted (or malformed) message is still processed as if it were legitimate.\n\nRoot Cause Analysis:\nThe vulnerability stems from a refactoring error in the CVE-2026-29146 fix. In the vulnerable code (versions 11.0.20, 10.1.53, 9.0.116), the super.messageReceived(msg) call was placed OUTSIDE the try-catch block handling decryption.\n\njava\npublic void messageReceived(ChannelMessage msg) {\n try {\n // Decryption logic\n xbb.clear();\n xbb.append(data, 0, data.length);\n } catch (GeneralSecurityException gse) {\n log.error(sm.getString("encryptInterceptor.decrypt.failed"), gse);\n }\n super.messageReceived(msg); // BUG: Outside try - executes regardless!\n}\n\n\nWhen super.messageReceived(msg) is outside the try-catch, it executes regardless of whether decryption succeeded. If decryption fails (throws GeneralSecurityException), the exception is logged but the unprocessed (encrypted/corrupted) message still passes down the interceptor chain, allowing message processing without proper decryption.\n\nAttack Flow:\n1. EncryptInterceptor receives an encrypted cluster message\n2. Decryption fails (malformed/corrupted data triggers GeneralSecurityException)\n3. Exception is caught and logged but ignored\n4. Message (still encrypted/corrupted) continues to downstream components\n5. Downstream components process the message without proper decryption\n\nTarget Setup for Reproduction:\n- Apache Tomcat 9.0.116, 10.1.53, or 11.0.20\n- Configure clustering with EncryptInterceptor in server.xml\n- Enable session replication between multiple Tomcat nodes\n\nExample vulnerable configuration:\nxml\n\u003cCluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"\u003e\n \u003cChannel className="org.apache.catalina.tribes.group.GroupChannel"\u003e\n \u003cMembership className="org.apache.catalina.tribes.membership.McastService"\u003e\n \u003cReceiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"\u003e\n \u003cInterceptors\u003e\n \u003cInterceptor className="org.apache.catalina.tribes.group.interceptors.EncryptInterceptor"\n encryptionKey="a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"\n encryptionAlgorithm="AES/CBC/PKCS5Padding"\u003e\n \u003c/Interceptors\u003e\n \u003c/Channel\u003e\n\u003c/Cluster\u003e\n\n\nExpected exploitation outcome:\n- Send malformed encrypted messages to the cluster\n- Observe log entries: "Failed to decrypt message"\n- Message is still processed despite decryption failure\n- Session data may be exposed or corrupted\n\nThe Fix:\nMove super.messageReceived(msg) inside the try block so it only executes on successful decryption.\n\nCVSS Details:\n- Score: 7.5 (HIGH)\n- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N\n- Network attack vector, low complexity, no privileges required, high impact on confidentiality
Root Cause Analysis
# CVE-2026-34486: Root Cause Analysis
## Summary
CVE-2026-34486 is a **Missing Encryption of Sensitive Data** vulnerability in Apache Tomcat's `EncryptInterceptor` component within the tomcat-tribes clustering subsystem. The vulnerability allows an attacker to bypass encryption entirely by sending malformed or corrupted ciphertext that causes decryption to fail. Due to a code refactoring error introduced in the fix for CVE-2026-29146 (a padding oracle vulnerability), the `super.messageReceived(msg)` call was placed **outside** the try-catch block that handles decryption. As a result, when decryption throws a `GeneralSecurityException` (or any exception), the exception is caught and logged but the **unprocessed (encrypted/corrupted) message is still forwarded downstream** as if it were legitimate, effectively bypassing the encryption layer.
## Impact
- **Package/Component affected**: `org.apache.catalina.tribes.group.interceptors.EncryptInterceptor` (tomcat-tribes module)
- **Affected versions**: Apache Tomcat 11.0.20, 10.1.53, 9.0.116
- **Risk level**: HIGH (CVSS: 7.5)
- **CVSS Vector**: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`
- **Consequences**:
- Encrypted cluster messages can be processed without proper decryption
- Session replication data may be exposed or corrupted
- Attackers with network access to Tomcat cluster can send arbitrary malformed messages that bypass encryption checks
- Confidentiality impact: HIGH — sensitive session/cluster data may leak in plaintext or corrupted form
## Root Cause
The vulnerability stems from a **refactoring error** in the patch for CVE-2026-29146. In the vulnerable versions, the `EncryptInterceptor.messageReceived()` method has the following flawed structure:
```java
public void messageReceived(ChannelMessage msg) {
try {
// Decryption logic
xbb.clear();
xbb.append(data, 0, data.length);
} catch (GeneralSecurityException gse) {
log.error(sm.getString("encryptInterceptor.decrypt.failed"), gse);
}
super.messageReceived(msg); // BUG: Outside try-catch — executes regardless of decryption failure!
}
```
The `super.messageReceived(msg)` call is **outside** the try-catch block, meaning it executes even when decryption fails and throws an exception. The correct behavior would be to only forward the message downstream if decryption succeeded, or to drop/abort processing on failure.
This is a **classic exception handling bug**: the catch block swallows the exception but doesn't prevent the downstream code path from executing.
## Reproduction Steps
1. **Reference**: `repro/reproduction_steps.sh` in this repository
2. **What the script does**:
- Downloads Apache Tomcat tomcat-tribes 11.0.20 JARs (vulnerable version)
- Compiles a Java test class (`TestEncryptInterceptorBypass.java`) that:
- Creates an `EncryptInterceptor` with a known encryption key
- Chains a `ValueCaptureInterceptor` downstream to observe forwarded messages
- **Step 1**: Sends a valid plaintext message and confirms normal encryption/decryption works (baseline)
- **Step 2**: Sends malformed ciphertext (random bytes, wrong length) that triggers `IllegalBlockSizeException` during decryption
- Asserts that the malformed ciphertext was forwarded downstream **unchanged** (bypass confirmed)
- Runs the test and exits 0 on success, 1 on failure
3. **Expected evidence**:
- Log output showing "Failed to decrypt message" with `IllegalBlockSizeException`
- Captured downstream bytes matching the input malformed ciphertext exactly
- Final message: ">>> VULNERABILITY CONFIRMED <<<"
## Evidence
- **Log output**: `logs/reproduction.log` (generated by running `repro/reproduction_steps.sh`)
- **Key excerpts proving reproduction**:
```
Apr 14, 2026 12:41:17 PM org.apache.catalina.tribes.group.interceptors.EncryptInterceptor messageReceived
SEVERE: Failed to decrypt message
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
...
Captured downstream: 000102030405060708090a0b0c0d0e0ffffefdfc4142434445464748
>>> VULNERABILITY CONFIRMED <<<
The malformed ciphertext was forwarded downstream UNCHANGED!
Decrypt failed but super.messageReceived() was still called.
CVE-2026-34486 is REPRODUCED.
```
- **Environment**: Java 17 (OpenJDK), tomcat-tribes 11.0.20, tomcat-juli 11.0.20
- **Script exit code**: 0 (confirmed)
- **Reproducibility**: Verified twice consecutively with consistent results
## Recommendations / Next Steps
1. **Immediate fix**: Move `super.messageReceived(msg)` **inside** the try block so it only executes on successful decryption. If decryption fails, the method should return early or propagate the exception.
2. **Upgrade guidance**: Upgrade to a patched version of Apache Tomcat once available. The fixed versions should ensure that `super.messageReceived()` is only called within the try block, or that an explicit return/throw follows exception handling.
3. **Testing recommendations**:
- Add unit tests that verify malformed ciphertext does NOT reach downstream interceptors
- Test with various malformed inputs: wrong key, truncated ciphertext, invalid padding, wrong block size
- Verify that legitimate encrypted messages still decrypt and forward correctly after the fix
- Test the CVE-2026-29146 fix did not regress the padding oracle protection
## Additional Notes
- **Idempotency**: The reproduction script is fully idempotent — it uses a temporary directory and downloads dependencies fresh each run. Running it twice produces identical results.
- **Edge cases**:
- The bug manifests with **any** input that causes decryption to fail, including wrong key, wrong block size, invalid padding, or corrupted ciphertext.
- The exception type observed was `IllegalBlockSizeException` (a subclass of `GeneralSecurityException`), but other `GeneralSecurityException` subclasses would trigger the same bypass.
- The downstream interceptor receives the **raw unprocessed bytes**, not any form of decrypted output.
- **Limitations**: This reproduction tests the library-level `EncryptInterceptor` via a Java harness. A full cluster-level test with running Tomcat instances would further demonstrate real-world impact but is not required to confirm the bug.
Verify with pruva-verify
Run the Pruva CLI to automatically fetch and execute the reproduction script.
pruva-verify REPRO-2026-00131 pruva-verify CVE-2026-34486 curl -fsSL https://pruva.dev/install.sh | sh Or Run Manually
Download the script
curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00131/artifacts/reproduction_steps.sh Make executable
chmod +x reproduction_steps.sh Run the script
./reproduction_steps.sh How Pruva Reproduced This
Watch the AI agent's step-by-step process.
Loading session...
Artifacts
No artifacts available