作者 akihi(白帽攻防录讲师),某甲方网络安全工程师,合合 SRC 年度第一、腾讯 SRC 连续三年前十,单漏洞赏金 4w+,擅长 Web/App/PC 客户端漏洞挖掘。专注中间件安全、反序列化漏洞、代码审计。本文基于公开 CVE 信息与技术原理深度复盘,供安全研究与防护参考。
一、漏洞时间线
2026 年 4 月,Apache Tomcat 安全团队披露了一个 Critical 级别的漏洞 CVE-2026-34486。这个漏洞非常特殊------它不是一个新发现的漏洞,而是由之前修复另一个漏洞时引入的回归缺陷。一行代码的位置变化,让整个集群加密机制变成了 fail-open 的安全隐患。
| 时间 | 事件 | 影响版本 |
|---|---|---|
| 2026-03 | 修复 CVE-2026-29146(padding oracle) | ------ |
| 2026-04-09 | NVD 发布 CVE-2026-34486 详情 | 10.1.x / 11.0.x |
| 2026-04-21 | 安全团队发布技术分析 | ------ |
| 2026-04-28 | FreeBuf 发布中文分析 | ------ |
| 2026-08-04 | CISA 将其加入 KEV 已知被利用漏洞清单 | ------ |
| 2026-08-31 | 安全厂商发布高优先级预警 | ------ |
这个漏洞的 CVSS 评分为 9.8(Critical),攻击复杂度低,无需认证,无需用户交互,是典型的"一键利用"漏洞。CISA 在 8 月 4 日将其列入 KEV 清单,确认已被实际利用。
二、攻击链全景
让我们先用一张流程图还原完整的攻击路径:
攻击者发现暴露在公网的 Tomcat 集群节点
│
▼
第一步:发现 Tribes 集群通信端口
│ Tomcat Tribes 默认使用 TCP 4000 端口
│ 用于集群节点之间的消息同步
│ 正常情况下消息应该加密
▼
第二步:分析加密机制
│ Tomcat 使用 EncryptInterceptor 加密集群消息
│ 正常流程:加密消息 → 传输 → 解密 → 处理
│ 漏洞版本:解密失败 → 消息仍然继续处理
▼
第三步:构造恶意序列化消息
│ 攻击者不需要加密
│ 直接发送恶意 Java 序列化对象
│ 内容是精心构造的反序列化 payload
▼
第四步:发送到 TCP 4000 端口
│ 原始字节流直接发送到 NioReceiver
│ EncryptInterceptor 尝试解密
│ 解密失败,抛出 GeneralSecurityException
▼
第五步:fail-open 发生
│ catch 块记录了错误日志
│ 但是!super.messageReceived(msg) 在 try 块外面
│ 消息没有被丢弃,而是继续传递下去
▼
第六步:到达反序列化层
│ 原始的恶意字节流进入 Tribes 消息处理
│ Java 反序列化执行
│ payload 中的恶意代码被执行
▼
攻击者获得远程代码执行权限
│
├─ 在 Tomcat 服务器上执行任意命令
├─ 读取/修改服务器上的文件
├─ 横向移动到内网其他节点
└─ 完全控制整个集群
关键结论: 这个漏洞的核心是"一行代码的位置"------
super.messageReceived(msg)从 try 块里面移到了外面。从编译器的角度看代码完全正确,但从安全的角度看,这就是从 fail-close 变成了 fail-open。解密失败不再丢弃消息,而是让原始字节流直接通过。
三、Tomcat Tribes 原理
3.1 什么是 Tomcat Tribes
Apache Tomcat Tribes 是 Tomcat 的集群通信组件,用于在多个 Tomcat 节点之间同步会话、配置和消息:
| 组件 | 作用 |
|---|---|
| NioReceiver | 监听 TCP 端口(默认 4000),接收集群消息 |
| EncryptInterceptor | 加密/解密集群消息,防止窃听和篡改 |
| DispatchChannel | 消息分发通道,将消息分发给各个拦截器 |
| Deserialization | 将接收到的字节流反序列化为 Java 对象 |
3.2 正常的加密流程
正常情况下,集群消息的处理流程:
- 发送端:用密钥加密消息内容
- 网络传输:消息是密文,即使被窃听也无法解读
- 接收端:EncryptInterceptor 尝试解密
- 解密成功:消息继续传递给下一个拦截器
- 解密失败:丢弃消息,记录错误日志
- 反序列化:只有解密成功的消息才会被反序列化
3.3 为什么需要加密
集群通信消息中包含敏感信息:
// 集群消息中可能包含的数据
// - 用户会话数据(Session)
// - 配置信息
// - 应用状态
// - 节点之间的认证凭证
// 如果不加密:
// 1. 攻击者可以窃听集群通信
// 2. 攻击者可以伪造集群消息
// 3. 攻击者可以直接发送恶意序列化对象
// 4. 导致远程代码执行
四、漏洞深度分析
4.1 漏洞根因:一行代码的位置
根据安全团队的分析,漏洞的根本原因是修复 CVE-2026-29146 时引入的回归:
// 修复前(正确的代码)
@Override
public void messageReceived(ChannelMessage msg) {
try {
// 尝试解密消息
byte[] decrypted = decrypt(msg.getMessage());
msg.getMessage().transferFrom(decrypted);
// 只有解密成功才继续处理
super.messageReceived(msg);
} catch (GeneralSecurityException gse) {
// 解密失败,丢弃消息
log.error("Failed to decrypt message", gse);
// 消息被丢弃,不继续处理
}
}
// 漏洞版本(错误的代码)
@Override
public void messageReceived(ChannelMessage msg) {
try {
// 尝试解密消息
byte[] decrypted = decrypt(msg.getMessage());
msg.getMessage().transferFrom(decrypted);
} catch (GeneralSecurityException gse) {
// 解密失败,只记录日志
log.error("Failed to decrypt message", gse);
// 注意!super.messageReceived 不在 try 块里面了!
}
// 这行代码被移到了 try-catch 外面
// 无论解密成功还是失败,都会执行
super.messageReceived(msg);
}
// 区别只有一个:
// super.messageReceived(msg) 从 try 块里面移到了外面
// 从编译器看,代码完全正确
// 但从安全看,这就是 fail-open
4.2 为什么会引入这个回归
这个回归是在修复另一个漏洞 CVE-2026-29146 时引入的:
| 漏洞 | 问题 | 修复方式 |
|---|---|---|
| CVE-2026-29146 | CBC 模式存在 padding oracle | 改进解密逻辑 |
| CVE-2026-34486 | 修复时把 super.messageReceived 移到了外面 | 把这行代码移回 try 块里面 |
这是一个典型的"修复引入新漏洞"的案例。开发者本意是好的------修复 padding oracle 漏洞,但在修改代码时不小心改变了控制流,导致了更严重的安全问题。
4.3 攻击利用示例
攻击者如何利用这个漏洞执行代码:
// 攻击步骤
// 1. 攻击者发现 Tomcat 集群的 TCP 4000 端口
nmap -sV target.com -p 4000
// 2. 构造恶意 Java 序列化 payload
// 比如使用 CommonsCollections 链
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(evilPayload);
// 3. 直接发送原始字节流
// 不需要加密,EncryptInterceptor 会尝试解密
// 解密失败,但消息仍然继续处理
Socket s = new Socket("target.com", 4000);
OutputStream os = s.getOutputStream();
os.write(serializedPayload);
os.flush();
// 4. Tomcat 接收到消息
// EncryptInterceptor.messageReceived() 被调用
// decrypt() 抛出 GeneralSecurityException
// catch 块记录错误
// super.messageReceived(msg) 仍然执行
// 消息到达反序列化层
// 恶意代码执行
// 实际影响:
// - 远程代码执行(RCE)
// - 读取服务器文件
// - 修改应用配置
// - 横向移动
4.4 影响范围
| 产品 | 影响版本 | 影响程度 |
|---|---|---|
| Apache Tomcat 10.1.x | 所有启用集群加密的版本 | 未授权 RCE |
| Apache Tomcat 11.0.x | 所有启用集群加密的版本 | 未授权 RCE |
| 利用端口 | TCP 4000(Tribes 集群通信) | ------ |
| 利用条件 | 启用了集群和加密拦截器 | ------ |
| CISA KEV | 2026-08-04 列入 | 确认被利用 |

五、修复方案分析
Apache Tomcat 在后续版本中修复了这个问题:
| 修复项 | 修复方式 |
|---|---|
| 代码位置 | 把 super.messageReceived(msg) 移回 try 块里面 |
| 异常处理 | 解密失败时丢弃消息,不继续处理 |
| 安全默认 | fail-close:失败时拒绝,而不是放行 |
5.1 安全编码原则
// 安全编码的 fail-close 原则
// 不好的做法(fail-open)
try {
decrypt(data);
} catch (Exception e) {
log.error("decrypt failed", e);
}
// 即使失败也继续处理
process(data);
// 好的做法(fail-close)
try {
decrypt(data);
process(data); // 只有成功才处理
} catch (Exception e) {
log.error("decrypt failed, dropping message", e);
// 失败时丢弃消息,不继续处理
return;
}
// 原则:
// 安全相关的操作,失败时应该拒绝
// 而不是放行
// 这就是 fail-close vs fail-open
5.2 Tomcat 加固措施
// Tomcat 安全加固
// 1. 及时更新
// 升级到修复了 CVE-2026-34486 的版本
// 2. 限制集群端口访问
// 不要让 TCP 4000 暴露在公网
// 只允许集群节点之间通信
iptables -A INPUT -p tcp --dport 4000 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 4000 -j DROP
// 3. 禁用不必要的集群功能
// 如果不需要集群,就不要启用
// 4. 监控异常流量
// 监控 TCP 4000 端口的连接
// 特别是来自公网的连接
// 5. 网络分段
// 集群通信放在内网
// 不要和公网直接通信
六、SRC 审计启示录
6.1 中间件安全审计清单
在 SRC 挖洞过程中,针对中间件的审计清单:
| # | 检查项 | 检测方法 |
|---|---|---|
| 1 | 是否有暴露的管理端口 | nmap 扫描常见端口 |
| 2 | 集群端口是否暴露公网 | 检查 4000、8009 等端口 |
| 3 | 是否启用了不安全的反序列化 | 发送序列化测试 payload |
| 4 | 加密机制是否正确实现 | 检查 fail-open / fail-close |
| 5 | 是否有已知 CVE 未修复 | 对比版本号和 CVE 列表 |
6.2 常见中间件漏洞
// 常见中间件漏洞
// 1. 反序列化漏洞
// WebLogic、WebSphere、JBoss、Tomcat
// 发送恶意序列化对象执行代码
// 2. 管理接口未授权
// Tomcat Manager、JMX Console
// 未授权访问部署 WAR 包
// 3. 集群通信漏洞
// 就是本次漏洞的类型
// 集群消息没有正确验证
// 4. 信息泄露
// 错误页面泄露版本信息
// 帮助攻击者精确匹配 CVE
6.3 fail-open vs fail-close
| 场景 | fail-open 风险 | fail-close 正确做法 |
|---|---|---|
| 解密失败 | 原始数据直接通过 | 丢弃消息,拒绝处理 |
| 认证失败 | 允许访问 | 返回 403 |
| 权限检查异常 | 默认允许 | 默认拒绝 |
| 输入验证异常 | 接受输入 | 拒绝输入 |
七、防护建议
- 及时更新:升级到修复了 CVE-2026-34486 的 Tomcat 版本
- 端口限制:TCP 4000 集群端口不要暴露公网,只允许内网访问
- 最小化服务:不需要集群功能就不要启用
- 监控告警:监控集群端口的异常连接和流量
- 网络分段:集群通信放在安全内网段
- 漏洞扫描:定期扫描服务器上的中间件版本和已知漏洞
八、总结
CVE-2026-34486 是一个非常有教育意义的漏洞------它告诉我们:安全是关于控制流的。一行代码的位置变化,就可能从 fail-close 变成 fail-open,从安全变成不安全。在 SRC 挖洞实践中,"补丁回归"是一个高价值的审计方向:看看最近修复的漏洞,修复代码是否引入了新的问题?特别是异常处理、控制流这些细节,往往藏着最严重的安全隐患。
