Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/dylan-chainguard/cve-2026-22732-poc
防御ツール脆弱性分析エクスプロイトセキュリティ仮想化ウェブセキュリティ学習と教育
GitHubdylan-chainguard/cve-2026-22732-poc

cve-2026-22732-poc

CVE-2026-22732 を実証する概念実証。Spring Security の脆弱性で、setIntHeader("Content-Length") がすべてのセキュリティヘッダーを削除してしまう問題を、脆弱なビルドと修正済みビルドで示す。

リポジトリを見る
11620日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2026-22732 — Proof of Concept

Spring Security は HTTP レスポンスのセキュリティヘッダーを黙って破棄する。デモ/教育目的のみ。このローカルアプリ以外に対して実行しないこと。

CVECVE-2026-22732 (CWE-425)、公開 2026-03-19
CVSS 3.19.1 CRITICAL — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
直接依存spring-boot-starter-web + spring-boot-starter-security 2.7.18
脆弱なコンポーネントspring-security-web / -config / -core 5.7.11 — 推移的のみ、pom.xml には一切記載なし
修正済みコンポーネントspring-security-web 5.7.14-0.cgr.2、1 箇所の <version> 変更で到達 — 遷移を参照
検証環境Tomcat 9.0.118、JDK 17.0.18、macOS arm64

影響範囲: 5.7.0–5.7.21、5.8.0–5.8.23、6.3.0–6.3.14、6.4.0–6.4.14、6.5.0–6.5.8、7.0.0–7.0.3。 Spring Boot 2.7.18 は Spring Security 5.7.11 を固定しており、最初の範囲のまさに中にある:

$ mvn dependency:tree -Dincludes=org.springframework.security
+- org.springframework.security:spring-security-config:jar:5.7.11:compile
|  \- org.springframework.security:spring-security-core:jar:5.7.11:compile
\- org.springframework.security:spring-security-web:jar:5.7.11:compile

実行方法

./run.sh        # terminal 1 — builds and starts on :8080 (pins JDK 17)
./exploit.sh    # terminal 2 — drives every endpoint, diffs the headers

run.sh は JAVA_HOME を固定している。Spring Boot 2.7.x は、このマシンで mvn がデフォルトで解決する JDK 25 では動作しないためである。JAVA_HOME_17=/path/to/jdk17 で上書きできる。

また、デフォルトで -s settings-chainguard.xml を付けてビルドする。修正済みの親 POM が Maven Central に存在しないためである。別の場所を指定するには MAVEN_SETTINGS=/path/to/your/settings.xml を設定するか、MAVEN_SETTINGS= で Central のみからビルドする — これは素の 2.7.18 でのみ動作する。

exploit.sh は実際の spring-security-web と spring-boot のバージョンを target/*.jar から読み取るため、そのバナーは常にハードコードされた文字列ではなく、実際に動作しているものを報告する。

セットアップ

SecurityConfig はヘッダーのカスタマイズを一切行わない — Spring Security のデフォルトが有効であり、これはまさにセキュリティを意識したアプリが依存するものである。すべてのエンドポイントは同じ機密性の高いボディを返す:

{"account":"4111-1111-1111-1111","holder":"D. Havelock","balance":"82914.55"}

異なるのは、コントローラーがレスポンスをどのように書き込むかだけである。

測定結果

BASELINE  standard Spring MVC return value
  /safe/account                    OK        all 6 headers delivered

CONTROL   getOutputStream(), body > 8 KB buffer
  /vuln/stream/account             OK        all 6 headers delivered

CONTROL   explicit response.flushBuffer()
  /vuln/flush/account              OK        all 6 headers delivered

EXPLOIT   setIntHeader("Content-Length", n)  <-- CVE-2026-22732
  /vuln/content-length/account     BYPASSED  ALL 6 security headers dropped

BY DESIGN application sets its own Expires header (NOT this CVE)
  /vuln/cache/account              PARTIAL   Cache-Control + Pragma dropped

CVE が修正されたときに状態が変わるのは /vuln/content-length/account だけであり、exploit.sh が判定を導き出すのはこのエンドポイントのみである。残りは対照群である。

CVE — setIntHeader("Content-Length", n) → 完全バイパス

一見普通のコントローラーコード 3 行が、Spring Security が約束したすべてのヘッダーを剥ぎ取る:

response.setContentType("application/json");
response.setIntHeader("Content-Length", body.length);
response.getOutputStream().write(body);
$ curl -sD - -o /dev/null http://localhost:8080/vuln/content-length/account
HTTP/1.1 200
Content-Type: application/json
Content-Length: 77
Date: Tue, 01 Sep 2026 00:53:18 GMT

X-Frame-Options なし、X-Content-Type-Options なし、Cache-Control なし、Pragma なし、Expires なし、X-XSS-Protection なし。6 つすべてを含む /safe/account と比較せよ。レスポンスは任意のオリジンからフレーム化可能で、MIME スニッフィング可能で、キャッシュ可能 — しかもカード番号を配信しながらである。

仕様通りのケース — アプリケーション設定のキャッシュヘッダー → キャッシュ抑制

訂正: この README の以前のバージョンではこれを「exploit 2」と呼び、アドバイザリが文書化している条件であると主張していた。これは CVE-2026-22732 の一部ではなく、アップグレードしても修正されない。CacheControlHeadersWriter は 5.7.11、5.7.14-0.cgr.2、6.5.8(最後の脆弱版)、6.5.9(最初の修正版)でバイト単位で同一である — ソース jar を diff して検証済み。その Javadoc はこの挙動を明言している: 「キャッシュ制御ヘッダーが指定されていない場合に、キャッシュを防ぐヘッダーを挿入する。」

それでもこれを実証する価値はある。リークは現実であり、残存リスクはパッチ適用後も生き残るからである。CacheControlHeadersWriter は Cache-Control、Expires または Pragma がすでに存在する場合に処理を中止するため、3 つのうちどれか 1 つを設定するだけで Spring Security の no-store ディレクティブすべてが抑制される。善意の 1 行でそれが起きる:

response.setHeader("Expires", "Thu, 01 Jan 2099 00:00:00 GMT");
/safe/account                  NOT cacheable  (Cache-Control: no-cache, no-store, max-age=0, must-revalidate)
/vuln/cache/account            CACHEABLE      (Cache-Control: absent / Expires: Thu, 01 Jan 2099 00:00:00 GMT)
/vuln/content-length/account   CACHEABLE      (Cache-Control: absent / Expires absent)

上の表では Expires は「存在する」とみなされるが、アプリが選んだ攻撃者に都合の良い値によって — Spring Security の Expires: 0 は単に破棄されたのではなく置き換えられた。カード会員データは今や、経路上のすべてのブラウザと共有プロキシによって 2099 年まで保存可能である。

修正済みビルドでは /vuln/content-length/account は NOT cacheable に反転するが、/vuln/cache/account は上記のまま変わらない。これを修正するのはアプリケーションコードかリバースプロキシのみである — これはデモで声に出して言うべき有用なことである: ライブラリをアップグレードすると CVE は解消されるが、これは手つかずのまま残る。

意図的に残した 2 つの否定的結果

広く出回っているいくつかの記事 — 公開された再現リポジトリを含む — は response.getOutputStream() と response.flushBuffer() をトリガーとして挙げ、「Spring Security がヘッダーを注入する前にレスポンスがコミットされる」と説明している。Spring Security 5.7.11 ではそれは誤りである。 両エンドポイントとも 6 つのヘッダーすべてを配信する。

/diag/committed はその説明が成り立たない理由を示す。12 KB の書き込み後、レスポンスは実際にコントローラー内でコミットされているが、それでもヘッダーは到着する:

>>> DIAG response.isCommitted() after 12048 byte write = true
    | wrapper class = org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterResponse

OnCommittedResponseWrapper は flushBuffer() と出力ストリームの書き込みをオーバーライドするため、それらのコミットより先にヘッダーを出せる。コミット順序だけがバグではない; 宣言された Content-Length の経路がそうである。Spring のアドバイザリ自体、コミット順序の話を一切支持していない。

これら 2 つのエンドポイントを残すことで PoC は反証可能になる: 何が再現しないかを、何が再現するかと同じくらい明確に示し、両方ともパッチを通じてグリーンのままであることが、反転する唯一のエンドポイントを意味あるものにしている。

脆弱 → 修正済みへの遷移

pom.xml の 1 行、それだけ。 ソース変更なし、プロパティ変更なし、Spring Boot のメジャーアップなし:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.7.18</version>            <!-- vulnerable -->
  <version>2.7.18-0.cgr.3</version>    <!-- patched   -->
</parent>

これにより親の spring-boot-dependencies を通じて spring-security.version が 5.7.11 から 5.7.14-0.cgr.2 に(そして spring-framework.version が 5.3.31 から 5.3.39-0.cgr.4 に)再固定される。測定結果:

2.7.182.7.18-0.cgr.3
/safe/accountOK 6/6OK 6/6
/vuln/stream/accountOK 6/6OK 6/6
/vuln/flush/accountOK 6/6OK 6/6
/vuln/content-length/accountBYPASSED 0/6OK 6/6
/vuln/cache/accountPARTIAL 4/6PARTIAL 4/6 (by design)
exploit.sh verdictVULNERABLEPATCHED

バックポートはアップストリームの修正である

spring-security-web のソース jar を diff すると、重要なファイルは 1 つだけである。5.7.11 → 5.7.14-0.cgr.2 は OnCommittedResponseWrapper に setHeader / setIntHeader / addIntHeader のオーバーライドを追加し、それぞれが Content-Length を setContentLength() 経由でルーティングする:

@Override
public void setIntHeader(String name, int value) {
    checkContentLengthHeader(name, value);   // <-- added
    super.setIntHeader(name, value);
}

修正前は addHeader だけがこれを行っていたため、setIntHeader("Content-Length", n) はラッパーの追跡長を 0 のままにし、onResponseCommitted() は決して発火せず、HeaderWriterFilter は Tomcat がレスポンスをコミットする前にヘッダーを書かなかった。

アップストリームの 6.5.8(最後の脆弱版)と 6.5.9(最初の修正版)を diff しても同じハンクがそのまま現れるため、これは再実装ではなく公式修正のバックポートである。Chainguard ビルドはアップストリーム 6.5.9 にはない 2 つの null ガードを追加している(String オーバーロードの value != null、および append の (csq != null) ? csq.length() : 4)。

その他の緩和策

Spring Security 5.7.x はアップストリームで EOL である; OSS の修正は 6.4.15 / 6.5.9 / 7.0.4+ にのみ入る。再ビルドされた 5.7.x が選択肢にならない場合:

  1. 5.7.x からのアップグレード — Spring Boot 3.x への移行。
  2. 回避策 — ObjectPostProcessor 経由で HeaderWriterFilter.shouldWriteHeadersEagerly = true を設定する。アドバイザリによればこれは挙動を変える: アプリケーションが書いたヘッダーは、Spring Security のヘッダーを抑制するのではなく、特定のヘッダーのみを上書きするようになる。これはまた /vuln/cache/account も修正するが、バージョンアップではそれは修正されない。
  3. 商用サポート — Tanzu Spring Enterprise による 5.7.x/5.8.x のバックポート。
  4. 多層防御 — リバースプロキシ/イングレスでヘッダーを設定し、アプリケーションヘッダーの破棄が唯一の制御にならないようにする。これは両エンドポイントをカバーする唯一の記載された選択肢である。
ツールをダウンロード