Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-90885 — Runnable proof-of-concept reproducer for CVE-2026-90885, an XXE (CWE-611) in Apache Camel camel-saxon XQuery, with Spring Boot and Quarkus builds and fix verification. | Kitploit
Tools/GitHubGitHub/oscerd/cve-2026-90885
Defensive ToolsVulnerability AnalysisExploitationWeb Application ExploitationLearning & Education
GitHuboscerd/cve-2026-90885

CVE-2026-90885

Runnable proof-of-concept reproducer for CVE-2026-90885, an XXE (CWE-611) in Apache Camel camel-saxon XQuery, with Spring Boot and Quarkus builds and fix verification.

View Repository
1 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-90885 — camel-saxon: XQuery over an already-built Source was parsed with Saxon's defaults (XXE)

Runnable proof-of-concept reproducer for the Apache Camel vulnerability where XQueryBuilder in camel-saxon — which evaluates expressions for both the xquery language and the xquery component — built its default Saxon Configuration with parse options carrying only a whitespace stripping rule, leaving the XML parser at its defaults: a DOCTYPE declaration is accepted and external entities are resolved.

RuntimeDirectoryStack
Camel Spring Bootcamel-spring-boot/Spring Boot 3.5.13 + camel-saxon 4.22.1
Camel Quarkuscamel-quarkus/Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0)

Why 4.22.1 for Spring Boot: the fix was backported to camel-4.22.x only after 4.22.1 had been cut, so 4.22.1 is the last affected release of that line and 4.22.2 is the first fixed one. Pinning 4.22.1 makes that boundary the point of the reproducer.

What it demonstrates

String, byte[] and InputStream bodies are turned into a javax.xml.transform.Source by Camel's own XML type converters, which configure the parser against external entities — those bodies were never affected. A body that was already a Source was handed to Saxon unchanged, and a stream-based Source still has to be parsed, so Saxon parsed it with that default Configuration.

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 on an affected build (identical on both 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 is the one that matters in practice. The sender posts plain XML text — the body shape that is not affected, as probe 4 shows. The route then does convertBodyTo(Source.class) before the expression runs, and that alone moves the body onto the affected path. No attacker-built Source, no unusual component, no non-default option: one ordinary conversion step in the route is the whole precondition. The advisory names this case explicitly, along with a component or processor earlier in the route setting the body to a stream-based Source.

Probes 1–3 are the same flaw reached by handing the Source in directly, which is what a component earlier in the route would do.

What the routes look like

Nothing is misconfigured. No configuration option, no parser settings, and the expression reads nothing:

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());

The attacker controls only the XML document being evaluated against.

Vulnerability Summary

PropertyValue
Componentcamel-saxon (XQueryBuilder, used by the xquery language and the xquery component)
CWECWE-611 (Improper Restriction of XML External Entity Reference)
SeverityHigh
Attack vectorA DOCTYPE with an external subset, or an external entity, in XML that reaches an XQuery expression as an already-built stream-based Source
ImpactRead files readable by the Camel process; make the process issue requests to hosts the document names
Affected VersionsFrom 4.0.0 before 4.18.5, from 4.19.0 before 4.22.2 (the code path predates 4.0 and was present in 2.x/3.x)
Fixed Versions4.18.5, 4.22.2, 4.23.0
JIRACAMEL-24691
CreditLeon Zlobecki

Advisory: https://camel.apache.org/security/CVE-2026-90885.html

Not the same issue as CVE-2026-85250, which concerned documentType=InputSource in camel-xpath and was fixed in 4.18.5, 4.22.1 and 4.23.0. Both are input paths that bypassed the default parser hardening, but they are different components and neither fix covers the other — in particular 4.22.1 resolves CVE-2026-85250 but not this issue.

The fix

XQueryBuilder now builds its default Configuration with parse options that enable disallow-doctype-decl and disable external-general-entities, external-parameter-entities and load-external-dtd, aligning a Source body with the hardened parser Camel's XmlConverter already applied to String, byte[] and InputStream bodies.

The change is visible and is recorded in the 4.23 upgrade guide: a body that reaches XQuery as an already-built Source and carries a DOCTYPE is now rejected with a parse error, while bodies without a DOCTYPE are unaffected.

Only the default Configuration is hardened. One supplied through the configuration option of the endpoint or language, or through XQueryBuilder.setConfiguration, is used as it is — so a deployment that genuinely needs DOCTYPE or external entity processing can keep it, and a deployment that supplies its own Configuration for any other reason has to apply the same parse options itself.

Fixed on main in apache/camel#26344 (9c23b686), backported to camel-4.22.x in #26728 (0d0ed8bb) and to camel-4.18.x in #26730 (ed513f3a).

Checking a fixed build

Set camel.version to 4.22.2 (Spring Boot), or both platform versions to a Camel Quarkus release carrying Camel 4.22.2 or later, and run it again. Verified against 4.22.1 → 4.22.2:

1) ... [FAILED: XPathException: SXXP0003   Error reported by XML parser: DOCTYPE is disallowed when the
        feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.]
2) ... same                        3) ... same
5) ... same

Requests the XML parser made to internal endpoints on its own: []

>>> NOT reproduced: external access was denied.
Download Tool