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
防御工具漏洞分析漏洞利用安全虚拟化Web安全学习与教育
GitHubdylan-chainguard/cve-2026-22732-poc

cve-2026-22732-poc

演示 CVE-2026-22732 的概念验证,这是 Spring Security 的一个缺陷,其中 setIntHeader("Content-Length") 会丢弃所有安全头,包含易受攻击和已修补的构建版本。

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
查看仓库
2小时52分前尚未审核

CVE-2026-22732 — 概念验证

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,只需修改一处 <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        # 终端 1 — 构建并在 :8080 上启动(固定 JDK 17)
./exploit.sh    # 终端 2 — 驱动每个端点,对比响应头差异

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 会从 target/*.jar 中读取实际的 spring-security-web 和 spring-boot 版本,因此其横幅始终报告真正运行的版本,而非硬编码字符串。

环境配置

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

只有 /vuln/content-length/account 在 CVE 被修复后状态发生变化,因此它是 exploit.sh 得出判定结论的唯一端点。其余均为对照组。

该 CVE — setIntHeader("Content-Length", n) → 完全绕过

三行看似普通的控制器代码就剥离了 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。对比 /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 验证。其 Javadoc 明确说明了该行为:“在未指定任何缓存控制头的情况下,插入响应头以防止缓存。”

它仍然值得演示,因为泄漏是真实存在的,且残余风险在打补丁后依然存在。CacheControlHeadersWriter 在 Cache-Control、Expires 或 Pragma 已存在时会直接退出,因此设置三者中任意一个都会抑制 Spring Security 的所有 no-store 指令。一行善意的代码就能做到:

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,却对此毫无影响。

两个刻意保留的负面结果

若干广泛流传的分析文章 — 包括一个公开的复现仓库 — 将 response.getOutputStream() 和 response.flushBuffer() 列为触发条件,解释说“响应在 Spring Security 能注入其响应头之前就已提交”。在 Spring Security 5.7.11 上这是错误的。 两个端点都交付了全部六个响应头。

/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 的公告本身从未认可提交顺序这一说法。

保留这两个端点使 PoC 可被证伪:它展示了什么不会复现,与什么会复现同样清晰,且两者在补丁前后都保持绿色,这正是使那个确实翻转的端点具有意义的原因。

从受影响版本到修复版本的过渡

pom.xml 中改一行,仅此而已。 无需修改源码、无需修改属性、无需升级 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>

这会通过父 POM 的 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,只有一个文件重要。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(第一个修复版本)时,同一代码块逐字出现,因此这是官方修复的向后移植,而非重新实现。Chainguard 构建添加了两个上游 6.5.9 没有的空值保护(String 重载上的 value != null,以及 append 中的 (csq != null) ? csq.length() : 4)。

其他缓解措施

Spring Security 5.7.x 在上游已停止维护;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 个严重)。所有这些都在 9.0.118 或更低版本中修复,而 9.0.118 是最新的 9.0.x 版本 — 因此一个属性即可清除全部:

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 会在运行时因 servlet 类型上的 NoClassDefFoundError 而失败。

修补其他所有内容,仍保持在各自大版本线内

同样的机制应用于 Boot 2.7.18 管理的其余依赖。没有大版本升级,也没有 Spring Boot 升级:

这些覆盖会与修复后的父 POM 交互,因此演示前请了解它们的作用。 在 2.7.18-0.cgr.3 上,父 POM 已提供 tomcat.version 9.0.118、logback.version 1.2.13 和 snakeyaml.version 1.33 — 这三行成为完全重复项,可删除而不改变任何内容。jackson-bom.version 和 log4j2.version 两行仍然有实际作用:修复后的父 POM 保留 Boot 的原始 2.13.5 / 2.17.2,因此覆盖生效,这两个依赖解析为普通上游构建而非 Chainguard 构建。spring-framework.version 被有意注释掉,这正是让父 POM 的 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 上启动,所有六个端点返回 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 2.x 的 ServiceLoader 提供程序替换了 SLF4J 1.7 的 StaticLoggerBinder,而 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 发现(2 个 Medium、4 个 Low)无法修复。

剩余的 43 个

组件为何无法在大版本内修复

剩余三个严重漏洞中有两个值得认真阅读,而非仅看评分:

  • CVE-2016-1000027(spring-web,修复版本 6.0.0)— 通过 HttpInvokerServiceExporter 的反序列化。此应用不使用 HTTP Invoker,因此在此处不可达。
  • CVE-2024-38821(spring-security-web,修复版本 5.7.13)— WebFlux 中的静态资源认证绕过。这是一个 servlet 应用,因此同样不可达。它确实可以在大版本内修复(5.7.13/5.7.14 在 Central 上),保留它只是为了将本节固定在 5.7.11。修复后的父 POM 作为副作用清除了它,因为 5.7.14-0.cgr.2 已超过修复版本。
  • CVE-2026-22732 — 在受影响状态下是刻意的;由父 POM 升级清除。

残余问题是结构性的:Spring Framework 5.3.x 和 Spring Security 5.7.x 都已停止维护。这才是迁移到 Boot 3.x 的真正理由,而非 Tomcat 或 Jackson。

目录结构

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 分析
  • HeroDevs 分析
  • 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 (设计如此)
exploit.sh 判定VULNERABLEPATCHED
属性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;15 个 webmvc 发现中有 14 个根本没有修复,且 grype 引用的 5.3.42 仅限商业版
logback-core (6)需要 SLF4J 2.x,见上文
spring-security-* (8)已停止维护的线;CVE-2026-22732 是刻意保留的
spring-boot / -autoconfigure (3)2.7.x 没有发布修复
snakeyaml (1)CVE-2022-1471 仅在 2.0 中修复