
Preuve de concept exécutable reproduisant la CVE-2026-88789, démontrant XXE et SSRF dans Apache Camel Quarkus camel-quarkus-support-xalan via le XSLT TransformerFactory.
TransformerFactory Xalan forcé annule les restrictions d'accès externe JAXPReproducteur de preuve de concept exécutable pour la vulnérabilité Apache Camel Quarkus où l'extension de support XSLT
(camel-quarkus-support-xalan) fournit son propre TransformerFactory basé sur Xalan au composant xslt
et l'enregistre comme implémentation par défaut JAXP. Xalan-J 2.7.x est antérieur à JAXP 1.5 et ne peut pas honorer
javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD ni ACCESS_EXTERNAL_STYLESHEET — setAttribute() lève
IllegalArgumentException pour les deux — de sorte que les restrictions d'accès externe qu'Apache Camel applique au
TransformerFactory qu'il crée n'ont jamais été effectives.
| Runtime | Répertoire | Stack |
|---|---|---|
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.36.0 (Quarkus 3.36.0, Camel 4.20.0) |
Camel Quarkus uniquement. Le code vulnérable est une extension Camel Quarkus, pas un composant Camel. Le Camel standard et Camel Spring Boot utilisent le
TransformerFactorydu JDK, qui honore les deux attributs, il n'y a donc rien à reproduire là-bas — ce dépôt n'a par conséquent pas de variantecamel-spring-boot/.
Un attaquant qui fournit le document XML en cours de transformation peut lire des fichiers locaux ou atteindre des emplacements réseau internes via une déclaration d'entité externe dans ce document.
cd camel-quarkus
mvn clean package
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
Sortie attendue sur une version affectée (abrégée — le pilote exécute six sondes, voir
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: ...
Vérifié également contre Camel Quarkus 3.40.0 : chaque sonde qui fuit devient silencieuse, les points de terminaison
internes ne reçoivent aucune requête du tout, et le pilote affiche NOT reproduced.
Sur le chemin du composant xslt, seuls les corps qui atteignent le transformateur déjà en tant que
javax.xml.transform.Source sont affectés. Les corps d'autres types — String, byte[], InputStream — sont
convertis par Apache Camel en un SAXSource avec les entités externes et le chargement de DTD externe désactivés, et ne
sont pas affectés. La sonde 4 dans le reproducteur est ce chemin sûr, côte à côte avec le chemin non sûr.
Parce que la fabrique est également enregistrée comme implémentation par défaut JAXP (l'extension de support fournit
META-INF/services/javax.xml.transform.TransformerFactory), tout autre code de l'application qui obtient
une fabrique via TransformerFactory.newInstance() perd les mêmes restrictions, sans erreur. C'est
pourquoi l'avis liste des extensions qui ne transforment jamais rien elles-mêmes :
| Extension | Exposition |
|---|---|
camel-quarkus-xslt | le chemin du composant xslt et l'implémentation par défaut JAXP |
camel-quarkus-xslt-saxon | l'implémentation par défaut JAXP |
camel-quarkus-tika | l'implémentation par défaut JAXP |
camel-quarkus-xmlsecurity | l'implémentation par défaut JAXP |
| Propriété | Valeur |
|---|---|
| Composant | camel-quarkus-support-xalan (extension de support XSLT) |
| CWE | CWE-611 (Restriction inappropriée de la référence d'entité externe XML) |
| Sévérité | Élevée |
| Vecteur d'attaque | Une entité externe ou une DTD externe déclarée dans le document XML en cours de transformation, où le corps atteint le point de terminaison xslt déjà en tant que javax.xml.transform.Source |
| Impact | Lecture de fichiers locaux ; émission de requêtes vers des emplacements réseau internes (SSRF) |
| Versions affectées | De 3.2.0 avant 3.33.3, de 3.34.0 avant 3.40.0 |
| Versions corrigées | 3.33.3 (flux LTS), 3.40.0 |
| Issue GitHub | apache/camel-quarkus#9115 |
| Crédit | Découvert par analyse interne, à l'aide de Claude Security Tool |
Avis : https://camel.apache.org/security/CVE-2026-88789.html
XalanTransformerFactory applique désormais les restrictions lui-même au lieu de s'appuyer sur des attributs que Xalan ne peut pas
honorer :
XMLReader qui ne résout ni les entités générales externes ni
les entités paramètres externes et ne charge pas les DTD externes — la même configuration que celle utilisée par
XmlConverter.createSAXParserFactory() d'Apache Camel pour les corps que camel-xslt convertit lui-même en SAXSource.
Un SAXSource portant un XMLReader configuré par l'appelant est utilisé tel quel, et DOMSource et StAXSource
sont déjà analysés.document() sont refusées à moins que le
URIResolver propre à l'application ne les résolve, et la restriction est installée sur chaque point d'entrée qui remet quelque chose
avec quoi transformer, y compris les points d'entrée push SAX dont les transformateurs ne reçoivent pas la copie du résolveur de fabrique
par Xalan. Les applications qui définissent leur propre résolveur — camel-xslt le fait sur chaque échange — continuent de
le remplacer comme avant.Corrigé sur main dans
9a570b64 et
9dd11779,
rétroporté vers 3.33.x dans
ad9c5236 et
3d886769.
javax.xml.transform.Source construit à partir d'une entrée non fiable dans un point de terminaison xslt. Laissez le
corps du message en String, byte[] ou InputStream afin qu'Apache Camel le convertisse d'abord en un SAXSource avec
les entités externes désactivées.convertBodyTo sur un corps Source existant n'est pas une solution de contournement : cette conversion effectue une transformation
identité via la même fabrique. La sonde 5 dans le reproducteur est cette transformation identité, et elle fuit.com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl,
plutôt que de s'appuyer sur TransformerFactory.newInstance(). La sonde 6 est ce contrôle, et elle refuse la lecture.