Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
cve-2026-22732-poc — CVE-2026-22732 を実証する概念実証。Spring Security の脆弱性で、setIntHeader("Content-Length") がすべてのセキュリティヘッダーを削除してしまう問題を、脆弱なビルドと修正済みビルドで示す。 | Kitploit
ツール/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") がすべてのセキュリティヘッダーを削除してしまう問題を、脆弱なビルドと修正済みビルドで示す。

人気

すべて見る →

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

すべてのツールを探索

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

すべてのツールを見る →
共有
リポジトリを見る
2時間52分前未レビュー

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 を固定しており、最初の範囲のまさに中にある:

root@kitploit:~
$ 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

実行方法

root@kitploit:~
./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 のデフォルトが有効であり、これはまさにセキュリティを意識したアプリが依存するものである。すべてのエンドポイントは同じ機密性の高いボディを返す:

root@kitploit:~
{"account":"4111-1111-1111-1111","holder":"D. Havelock","balance":"82914.55"}

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

測定結果

root@kitploit:~
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 が約束したすべてのヘッダーを剥ぎ取る:

root@kitploit:~
response.setContentType("application/json");
response.setIntHeader("Content-Length", body.length);
response.getOutputStream().write(body);
root@kitploit:~
$ 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 行でそれが起きる:

root@kitploit:~
response.setHeader("Expires", "Thu, 01 Jan 2099 00:00:00 GMT");
root@kitploit:~
/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 の書き込み後、レスポンスは実際にコントローラー内でコミットされているが、それでもヘッダーは到着する:

root@kitploit:~
>>> 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 のメジャーアップなし:

root@kitploit:~
<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 に)再固定される。測定結果:

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

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

root@kitploit:~
@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. 多層防御 — リバースプロキシ/イングレスでヘッダーを設定し、アプリケーションヘッダーの破棄が唯一の制御にならないようにする。これは両エンドポイントをカバーする唯一の記載された選択肢である。

これらはいずれもこのプロジェクトには組み込まれていないため、脆弱な挙動がデフォルトであり、修正済み状態は上記の単一の <version> 変更で到達可能である。

Spring Boot をアップグレードせずに組み込み Tomcat にパッチを当てる

Boot 2.7.18 は Tomcat 9.0.83 を固定しており、grype . はこれを 34 件の CVE(4 件 Critical) で指摘する。それらはすべて 9.0.118 以下で修正されており、9.0.118 は最新の 9.0.x リリースである — つまり 1 つのプロパティでこのセットが解消される:

root@kitploit:~
<properties>
  <tomcat.version>9.0.118</tomcat.version>
</properties>

spring-boot-dependencies はすべての tomcat-embed-* アーティファクトをこの単一のプロパティ経由で宣言しているため、これを上書きすると core、el、websocket がまとめて再固定される。検証済み:

root@kitploit:~
$ mvn dependency:tree | grep tomcat-embed
tomcat-embed-core:jar:9.0.118:compile
tomcat-embed-el:jar:9.0.118:compile
tomcat-embed-websocket:jar:9.0.118:compile

制約: 9.0.x 系に留まること。Tomcat 10+ は Servlet API を jakarta.* に移行したが、Spring Framework 5.3 は javax.servlet に対してコンパイルされるため、10.x/11.x へのアップは実行時にサーブレット型で NoClassDefFoundError を起こして失敗する。

その他すべてにパッチを当てる、各メジャー系内で

同じ仕組みを Boot 2.7.18 の管理依存関係の残りに適用する。メジャーバージョンアップなし、Spring Boot アップグレードなし:

これらのオーバーライドは修正済みの親 POM と相互作用するため、デモ前にその動作を把握しておくこと。 2.7.18-0.cgr.3 では親がすでに tomcat.version 9.0.118、logback.version 1.2.13、snakeyaml.version 1.33 を提供している — これら 3 行は完全な重複となり、何も変えずに削除できる。jackson-bom.version と log4j2.version の行は依然として実際に機能する: 修正済みの親は Boot の標準 2.13.5 / 2.17.2 を保持するため、オーバーライドが優先され、これら 2 つの依存関係は Chainguard ビルドではなく素のアップストリームビルドに解決される。spring-framework.version は意図的にコメントアウトされており、これにより親の 5.3.39-0.cgr.4 が通る。

grype . の推移

状態検出数内訳
素の Boot 2.7.18997C / 39H / 38M / 15L
+ Tomcat アップ653C / 23H / 29M / 10L
+ メジャー内アップすべて

完全に解消: Tomcat (34)、Jackson (7)、log4j (1)。全体で 99 → 43、High は 39 → 12。

各アップ後に検証済み: アプリは Tomcat/9.0.118 で起動し、6 つのエンドポイントすべてが 200 を返し、CVE はバイト単位で再現する。Spring Boot は依然 2.7.18、Spring Security は依然 5.7.11 であるため、CVE-2026-22732 は手つかずである — これがこのセクションの要点であり、同時にその正直な限界でもある: 周囲のすべてにパッチを当てても、アプリケーションフレームワークの CVE には何の効果もない。それを修正するにはプロパティではなく親 POM のアップが必要である。

Logback が 1.2.13 で止まる理由

最新の 1.x は 1.6.3 である — 同じメジャーなので名目上は範囲内である。しかし動作しない。Logback 1.3+ は SLF4J 1.7 の StaticLoggerBinder を SLF4J 2.x の ServiceLoader プロバイダに置き換えたが、Boot 2.7 の LogbackLoggingSystem は StaticLoggerBinder を直接呼び出す。1.5.38 での測定:

root@kitploit:~
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder"
Caused by: java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder
    at org.springframework.boot.logging.logback.LogbackLoggingSystem.getLoggerContext(...:304)

これを回避するには SLF4J 2.x(メジャーアップ)および Boot 3.x のロギングシステムが必要である。1.2.13 が真の上限であり、この系では 6 件の Logback 検出(Medium 2、Low 4)が修正不能のまま残る。

残る 43 件

コンポーネント

残る 3 件の Critical のうち 2 件は、スコアではなくきちんと読む価値がある:

  • CVE-2016-1000027(spring-web、修正 6.0.0)— HttpInvokerServiceExporter 経由のデシリアライズ。このアプリは HTTP Invoker を使用しないため、ここでは到達不能である。
  • CVE-2024-38821(spring-security-web、修正 5.7.13)— WebFlux における静的リソースの認証バイパス。これはサーブレットアプリなので、これも到達不能である。これはメジャー内で修正可能(5.7.13/5.7.14 は Central にある)であり、このセクションを 5.7.11 に固定しておくためだけに残された。修正済みの親は 5.7.14-0.cgr.2 が修正バージョンを超えているため、副作用としてこれを解消する。
  • CVE-2026-22732 — 脆弱な状態では意図的; 親 POM のアップで解消される。

残存分は構造的である: Spring Framework 5.3.x と Spring Security 5.7.x はどちらも EOL である。Tomcat や Jackson ではなく、これが Boot 3.x 移行の本当の論拠である。

レイアウト

root@kitploit:~
pom.xml                     parent + 2 starters, nothing else. Flip the <version> to switch state.
run.sh                      build + run on JDK 17, via settings-chainguard.xml
exploit.sh                  header diff, cache impact, clickjacking check, CVE-scoped verdict
settings-chainguard.xml     Chainguard Libraries repo -- required for the patched parent
src/main/java/com/example/poc/
  PocApplication.java       @SpringBootApplication
  SecurityConfig.java       permitAll, zero header customisation
  AccountController.java    baseline, 2 exploits, 2 controls, 1 diagnostic

認証は permitAll、CSRF はオフで curl が未認証で動作するようにしている — どちらもこの CVE の一部ではない。

出典

  • spring.io/security/cve-2026-22732 — 公式アドバイザリ
  • GHSA-mf92-479x-3373
  • NVD CVE-2026-22732
  • Broadcom / Tanzu write-up
  • HeroDevs analysis
  • semgrep/cve-2026-22732-demo — ここでは stream/flush の主張が成り立たなかった再現
  • Red Hat Bugzilla #2449306
ツールをダウンロード
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
プロパティBoot 2.7.18 デフォルトここでの固定値上限の理由
tomcat.version9.0.839.0.118最新の 9.0.x; 10+ は jakarta.*
spring-framework.version5.3.315.3.39Central 上の最後の OSS 5.3.x
jackson-bom.version2.13.52.22.2最新の 2.x
log4j2.version2.17.22.26.1最新の 2.x
snakeyaml.version1.301.33最後の 1.x; 残存 CVE の修正は 2.0
logback.version1.2.121.2.13最後の 1.2.x — 下記参照
spring-security.version5.7.11そのままデモの主題である
43
3C / 12H / 18M / 10L
メジャー内で修正できない理由
spring-webmvc / -expression / -core / -context (25)5.3.39 が最後の OSS 5.3.x; webmvc の 15 件のうち 14 件には修正が一切なく、grype が挙げる 5.3.42 は商用専用
logback-core (6)SLF4J 2.x が必要、上記参照
spring-security-* (8)EOL 系; CVE-2026-22732 は意図的
spring-boot / -autoconfigure (3)2.7.x 向けの修正は公開されていない
snakeyaml (1)CVE-2022-1471 は 2.0 でのみ修正