
CVE-2026-40859の再現コード — Apache Camel camel-netty-http / camel-vertx-http プロデューサー側でのHTTP応答ボディの安全でないデシリアライゼーション(RCE)
このプロジェクトは、Apache Camelの camel-netty-http および camel-vertx-http コンポーネントにおける Java デシリアライゼーションの脆弱性 を実証するもので、CVE-2026-40859 として追跡されています。プロデューサーエンドポイントが transferException=true(またはコンポーネントレベルの allowJavaSerializedObject=true)で構成されている場合、バックエンドの HTTP レスポンス が失敗ステータスで Content-Type: application/x-java-serialized-object を持つと、そのボディが生の java.io.ObjectInputStream によって ObjectInputFilter なしで デシリアライズされます。Camelプロデューサーが通信するバックエンドを制御できる攻撃者(侵害されたサービス、またはプレーンHTTP接続上の中間者)は、細工されたシリアライズオブジェクトを返すことができ、ガジェットチェーンがクラスパス上にあれば、Camelホスト上で リモートコード実行 を達成できます。
アドバイザリ:https://camel.apache.org/security/CVE-2026-40859.html
| プロパティ | 値 |
|---|---|
| コンポーネント | camel-netty-http、camel-vertx-http(プロデューサー側) |
| 影響を受けるクラス | org.apache.camel.component.netty.http.NettyHttpHelper#deserializeJavaObjectFromStream(および VertxHttpHelper#deserializeJavaObjectFromStream) |
| CWE | CWE-502:信頼できないデータのデシリアライゼーション |
| 影響 | リモートコード実行 (RCE) |
| 前提条件 | transferException=true(または allowJavaSerializedObject=true)+ throwExceptionOnFailure=true(デフォルト)+ 攻撃者が制御するバックエンド |
| 影響を受けるバージョン | 4.0.0 から 4.14.8 未満、4.15.0 から 4.18.3 未満、4.19.0 から 4.20.0 未満 |
| 修正バージョン | 4.14.8、4.18.3、4.20.0 |
| JIRA | CAMEL-23324 |
| 報告者 | Venkatraman Kumar (Securin) |
デフォルト構成では悪用不可 —
transferExceptionのデフォルトはfalseです。PoCではこれを有効にしていますが、これはリモート例外伝播を必要とするアプリケーションを想定しています。
2xx以外のレスポンスで、netty-httpプロデューサー(throwExceptionOnFailure=true(デフォルト))は populateNettyHttpOperationFailedException を介してレスポンスから例外を構築します。transferException が有効で、レスポンスがシリアライズオブジェクトのコンテンツタイプを持つ場合、ボディをデシリアライズします。
// NettyHttpHelper.populateNettyHttpOperationFailedException(...) - affected version
if (transferException) {
String contentType = response.headers().get(NettyHttpConstants.CONTENT_TYPE);
if (NettyHttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(contentType)) { // application/x-java-serialized-object
InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, response);
if (is != null) {
Object body = deserializeJavaObjectFromStream(is); // <-- sink
if (body instanceof Exception) {
return (Exception) body;
}
}
}
}
// NettyHttpHelper.deserializeJavaObjectFromStream(InputStream) - affected version
ObjectInputStream ois = new ObjectInputStream(is); // NO ObjectInputFilter
answer = ois.readObject(); // gadget fires here
ガジェットは readObject() 内で実行され、instanceof Exception チェック より前 に行われるため、ペイロードは例外である必要すらありません。camel-vertx-http も VertxHttpHelper.deserializeJavaObjectFromStream に同一のシンクがあります。
from("direct:call")
.to("netty-http://backend-host:PORT/path?transferException=true");
// throwExceptionOnFailure defaults to true
バックエンドが 5xx + application/x-java-serialized-object で応答するプロデューサー呼び出しはすべてシンクをトリガーします。
被害者 はCamelプロデューサー(デシリアライズを実行する側)です。攻撃者 はそれが呼び出すバックエンドを制御します。この自己完結型PoCでは、両方の役割が同じJVM/コンテナ内で実行されます。組み込みのrawソケットHTTPサーバー(MaliciousBackend)が攻撃者制御のバックエンドを演じ、Camelルート(VictimRoute)が被害者となります。
CVE-2026-40859/
├── pom.xml # camel-netty-http 4.18.2 + commons-collections 3.2.1 (ガジェット)
├── Dockerfile # アプリケーションを実行(--add-opens はガジェット構築にのみ必要)
├── docker-compose.yml
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── VictimRoute.java # 被害者: netty-http プロデューサー, transferException=true
│ ├── MaliciousBackend.java # 攻撃者バックエンド: 500 + シリアライズオブジェクトボディ (:9999)
│ ├── Gadget.java # CommonsCollections6 ガジェット, readObject() で発火
│ └── ExploitController.java # /exploit/attack がプロデューサー呼び出しを駆動
└── resources/
└── application.properties
実際の攻撃では、シリアライズバイトは攻撃者によってオフラインで生成され(例:ysoserial を使用)、被害者 のクラスパスにのみガジェットチェーンが必要です。このPoCでは利便性のためにプロセス内でガジェットを構築しており、そのためJVMが
--add-opens java.base/java.util=ALL-UNNAMEDで起動されています。このフラグはガジェット構築の詳細であり、脆弱性そのものとは無関係です。
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
# -> producer threw ...NettyHttpOperationFailedException (予想通り)
#
# >>> RCE の証拠 — /tmp/pwned が存在: true
docker exec cve-2026-40859 ls -la /tmp/pwned
docker compose down
transferException=true(または allowJavaSerializedObject=true)で構成され、攻撃者が制御・傍受可能なバックエンドと通信する、あらゆる camel-netty-http / camel-vertx-http プロデューサー:
http://)プロデューサー接続上の 中間者攻撃 がレスポンスをすり替える。transferException=true(またはコンポーネント allowJavaSerializedObject=true)のプロデューサー。throwExceptionOnFailure=true(デフォルト)。5xx + application/x-java-serialized-object を返す、攻撃者が制御・傍受可能なバックエンド。commons-collections:3.2.1)。4.14.8 / 4.18.3 / 4.20.0 にアップグレードしてください。修正では、両方のヘルパーに対してデフォルトの ObjectInputFilter 許可リスト(java.**;javax.**;org.apache.camel.**;!*)が適用され、新しい deserializationFilter エンドポイントオプションまたはJVM全体の -Djdk.serialFilter システムプロパティでカスタマイズ可能です。
アップグレードまでの対策:
transferException=true / allowJavaSerializedObject=true を 有効にしない。https) を使用し、レスポンスが転送中にすり替えられないようにする。-Djdk.serialFilter=java.**;org.apache.camel.**;!*。この再現プログラムは、セキュリティ研究および許可されたテスト のために、公開済みで修正済み の脆弱性に対して提供されています。明示的な許可なくシステムに対して使用しないでください。