
Lauffähiger Proof-of-Concept-Reproducer für CVE-2026-88789, der XXE und SSRF in Apache Camel Quarkus camel-quarkus-support-xalan über die XSLT TransformerFactory demonstriert.
TransformerFactory hebt die JAXP-Beschränkungen für externen Zugriff aufLauffähiger Proof-of-Concept-Reproducer für die Apache-Camel-Quarkus-Schwachstelle, bei der die XSLT-Support-Erweiterung (camel-quarkus-support-xalan) ihre eigene Xalan-basierte TransformerFactory an die xslt-Komponente liefert und sie als JAXP-Standard registriert. Xalan-J 2.7.x stammt aus der Zeit vor JAXP 1.5 und kann weder javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD noch ACCESS_EXTERNAL_STYLESHEET berücksichtigen — setAttribute() wirft für beide IllegalArgumentException —, sodass die Beschränkungen für externen Zugriff, die Apache Camel auf die von ihm erzeugte TransformerFactory anwendet, nie wirksam waren.
| Laufzeitumgebung | Verzeichnis | Stack |
|---|---|---|
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.36.0 (Quarkus 3.36.0, Camel 4.20.0) |
Nur Camel Quarkus. Der verwundbare Code ist eine Camel-Quarkus-Erweiterung, keine Camel-Komponente. Reines Camel und Camel Spring Boot verwenden die
TransformerFactorydes JDK, die beide Attribute berücksichtigt, sodass es dort nichts zu reproduzieren gibt — dieses Repository hat daher keinecamel-spring-boot/-Variante.
Ein Angreifer, der das zu transformierende XML-Dokument liefert, kann über eine External-Entity-Deklaration in diesem Dokument lokale Dateien lesen oder interne Netzwerkadressen erreichen.
cd camel-quarkus
mvn clean package
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
Erwartete Ausgabe bei einem betroffenen Build (gekürzt — der Treiber führt sechs Probes aus, siehe 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: ...
Ebenfalls gegen Camel Quarkus 3.40.0 verifiziert: Jede leckende Probe wird still, die internen Endpunkte erhalten überhaupt keine Anfragen, und der Treiber gibt NOT reproduced aus.
Auf dem Pfad der xslt-Komponente sind nur Bodies betroffen, die den Transformer bereits als javax.xml.transform.Source erreichen. Bodies anderer Typen — String, byte[], InputStream — werden von Apache Camel in eine SAXSource konvertiert, bei der externe Entities und das Laden externer DTDs deaktiviert sind, und sind nicht betroffen. Probe 4 im Reproducer ist dieser sichere Pfad, Seite an Seite mit dem unsicheren.
Da die Factory außerdem als JAXP-Standard registriert wird (die Support-Erweiterung liefert META-INF/services/javax.xml.transform.TransformerFactory mit), verliert jeder andere Code in der Anwendung, der eine Factory über TransformerFactory.newInstance() bezieht, dieselben Beschränkungen, ohne einen Fehler. Deshalb listet der Advisory Erweiterungen auf, die selbst nie etwas transformieren:
| Erweiterung | Exposition |
|---|---|
camel-quarkus-xslt | der Pfad der xslt-Komponente und der JAXP-Standard |
camel-quarkus-xslt-saxon | der JAXP-Standard |
camel-quarkus-tika | der JAXP-Standard |
camel-quarkus-xmlsecurity | der JAXP-Standard |
| Eigenschaft | Wert |
|---|---|
| Komponente | camel-quarkus-support-xalan (XSLT-Support-Erweiterung) |
| CWE | CWE-611 (Improper Restriction of XML External Entity Reference) |
| Schweregrad | Hoch |
| Angriffsvektor | Eine im zu transformierenden XML-Dokument deklarierte externe Entity oder externe DTD, wobei der Body den xslt-Endpunkt bereits als javax.xml.transform.Source erreicht |
| Auswirkung | Lesen lokaler Dateien; Ausführen von Anfragen an interne Netzwerkadressen (SSRF) |
| Betroffene Versionen | Von 3.2.0 vor 3.33.3, von 3.34.0 vor 3.40.0 |
| Behobene Versionen | 3.33.3 (LTS-Stream), 3.40.0 |
| GitHub-Issue | apache/camel-quarkus#9115 |
| Danksagung | Entdeckt durch interne Analyse mit Claude Security Tool |
Advisory: https://camel.apache.org/security/CVE-2026-88789.html
XalanTransformerFactory wendet die Beschränkungen nun selbst an, anstatt sich auf Attribute zu verlassen, die Xalan nicht berücksichtigen kann:
XMLReader geparst, der weder externe allgemeine noch externe Parameter-Entities auflöst und keine externen DTDs lädt — dieselbe Konfiguration, die Apache Camels XmlConverter.createSAXParserFactory() für die Bodies verwendet, die camel-xslt selbst in eine SAXSource konvertiert. Eine SAXSource, die einen vom Aufrufer konfigurierten XMLReader mitführt, wird unverändert verwendet, und DOMSource und StAXSource sind bereits geparst.document()-Funktion abgerufen werden, werden verweigert, es sei denn, der eigene URIResolver der Anwendung löst sie auf, und die Beschränkung wird an jedem Einstiegspunkt installiert, der etwas zum Transformieren herausgibt, einschließlich der SAX-Push-Einstiegspunkte, deren Transformatoren Xalan den Factory-Resolver nicht aufkopiert. Anwendungen, die ihren eigenen Resolver setzen — camel-xslt tut dies bei jedem Exchange —, überschreiben ihn weiterhin wie zuvor.Behoben auf main in 9a570b64 und 9dd11779, zurückportiert auf 3.33.x in ad9c5236 und 3d886769.
javax.xml.transform.Source, die aus nicht vertrauenswürdiger Eingabe erstellt wurde, an einen xslt-Endpunkt. Belassen Sie den Message-Body als String, byte[] oder InputStream, damit Apache Camel ihn zuerst in eine SAXSource mit deaktivierten externen Entities konvertiert.convertBodyTo auf einem bestehenden Source-Body ist kein Workaround: Diese Konvertierung führt eine Identitätstransformation durch dieselbe Factory aus. Probe 5 im Reproducer ist diese Identitätstransformation, und sie leckt.com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl nennen, anstatt sich auf TransformerFactory.newInstance() zu verlassen. Probe 6 ist diese Kontrolle, und sie verweigert das Lesen.