Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
cve-2026-22732-poc — Proof-of-Concept für CVE-2026-22732, eine Spring-Security-Schwachstelle, bei der setIntHeader("Content-Length") alle Sicherheits-Header entfernt, mit verwundbaren und gepatchten Builds. | Kitploit
Tools/GitHubGitHub/dylan-chainguard/cve-2026-22732-poc
DefensivwerkzeugeSchwachstellenanalyseExploitationSicherheitsvirtualisierungWebsicherheitLernen & Bildung
GitHubdylan-chainguard/cve-2026-22732-poc

cve-2026-22732-poc

Proof-of-Concept für CVE-2026-22732, eine Spring-Security-Schwachstelle, bei der setIntHeader("Content-Length") alle Sicherheits-Header entfernt, mit verwundbaren und gepatchten Builds.

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Repository anzeigen
vor 2h 52mNoch nicht geprüft
Teilen

CVE-2026-22732 — Proof of Concept

Spring Security verwirft still und heimlich HTTP-Response-Sicherheitsheader. Nur für Demo-/Bildungszwecke; führe es gegen nichts außer dieser lokalen App aus.

CVECVE-2026-22732 (CWE-425), veröffentlicht am 2026-03-19
CVSS 3.19.1 CRITICAL — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Direkte Abhängigkeitspring-boot-starter-web + spring-boot-starter-security 2.7.18
Verwundbare Komponentespring-security-web / -config / -core 5.7.11 — nur transitiv, nie in pom.xml genannt
Behobene Komponentespring-security-web 5.7.14-0.cgr.2, erreicht durch eine einzige <version>-Änderung — siehe den Übergang
Verifiziert aufTomcat 9.0.118, JDK 17.0.18, macOS arm64

Betroffene Bereiche: 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 pinnt Spring Security 5.7.11, genau innerhalb des ersten Bereichs:

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

Ausführen

root@kitploit:~
./run.sh        # Terminal 1 — baut und startet auf :8080 (pinnt JDK 17)
./exploit.sh    # Terminal 2 — fährt jeden Endpunkt an, vergleicht die Header

run.sh pinnt JAVA_HOME, weil Spring Boot 2.7.x nicht auf dem JDK 25 laufen kann, das mvn auf dieser Maschine standardmäßig auflöst. Überschreibe mit JAVA_HOME_17=/path/to/jdk17.

Es baut außerdem standardmäßig mit -s settings-chainguard.xml, weil das gepatchte Parent nicht auf Maven Central liegt. Setze MAVEN_SETTINGS=/path/to/your/settings.xml, um woanders hinzuzeigen, oder MAVEN_SETTINGS=, um rein von Central zu bauen — was nur für das unveränderte 2.7.18 funktioniert.

exploit.sh liest die tatsächlichen spring-security-web- und spring-boot-Versionen aus target/*.jar, sodass sein Banner immer meldet, was wirklich läuft, statt eines fest kodierten Strings.

Das Setup

SecurityConfig wendet überhaupt keine Header-Anpassung an — Spring Securitys Standardeinstellungen sind in Kraft, genau worauf sich eine sicherheitsbewusste App verlässt. Jeder Endpunkt liefert denselben sensiblen Body zurück:

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

Das Einzige, was variiert, ist, wie der Controller die Response schreibt.

Gemessene Ergebnisse

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

Nur /vuln/content-length/account ändert seinen Zustand, wenn die CVE gepatcht wird, daher ist es der einzige Endpunkt, aus dem exploit.sh sein Urteil ableitet. Der Rest sind Kontrollen.

Die CVE — setIntHeader("Content-Length", n) → vollständiger Bypass

Drei Zeilen gewöhnlich aussehender Controller-Code entfernen jeden Header, den Spring Security versprochen hat:

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

Kein X-Frame-Options, kein X-Content-Type-Options, kein Cache-Control, kein Pragma, kein Expires, kein X-XSS-Protection. Vergleiche /safe/account, das alle sechs trägt. Die Response ist von jedem Origin framebar, MIME-sniffbar und cachebar — während sie eine Kartennummer ausliefert.

Der By-Design-Fall — anwendungsgesetzter Cache-Header → Cache-Unterdrückung

Korrigiert: Eine frühere Version dieses README nannte dies „Exploit 2" und behauptete, es sei die Bedingung, die der Advisory dokumentiert. Es ist nicht Teil von CVE-2026-22732 und kein Upgrade behebt es. CacheControlHeadersWriter ist byte-identisch in 5.7.11, 5.7.14-0.cgr.2, 6.5.8 (letzte verwundbare) und 6.5.9 (erste behobene) — verifiziert durch Diffen der Sources-Jars. Sein Javadoc nennt das Verhalten unverblümt: „Inserts headers to prevent caching if no cache control headers have been specified."

Es ist dennoch wert, demonstriert zu werden, weil das Leck real ist und das Restrisiko das Patchen überlebt. CacheControlHeadersWriter bricht ab, wenn Cache-Control, Expires oder Pragma bereits vorhanden ist, sodass das Setzen von irgendeinem einen der drei alle No-Store-Direktiven von Spring Security unterdrückt. Eine gut gemeinte Zeile genügt:

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 zählt in der obigen Tabelle als „vorhanden", aber mit einem angreiferfreundlichen Wert, den die App gewählt hat — Spring Securitys Expires: 0 wurde ersetzt, nicht bloß verworfen. Kartendaten sind nun von jedem Browser und geteilten Proxy auf dem Pfad bis 2099 speicherbar.

Auf dem gepatchten Build wechselt /vuln/content-length/account zu NOT cacheable, während /vuln/cache/account genau wie oben bleibt. Nur Anwendungscode oder ein Reverse Proxy behebt es — was das Nützliche ist, das man in einer Demo laut sagen sollte: Das Upgrade der Bibliothek schließt die CVE und lässt dies unberührt.

Zwei negative Ergebnisse, absichtlich behalten

Mehrere weit verbreitete Write-ups — einschließlich eines öffentlichen Reproduktions-Repos — listen response.getOutputStream() und response.flushBuffer() als Auslöser auf und erklären, dass „die Response committed wird, bevor Spring Security seine Header injizieren kann". Auf Spring Security 5.7.11 ist das falsch. Beide Endpunkte liefern alle sechs Header.

/diag/committed zeigt, warum die Erklärung nicht hält. Nach einem 12-KB-Schreibvorgang ist die Response wirklich innerhalb des Controllers committed, dennoch kommen die Header an:

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

OnCommittedResponseWrapper überschreibt flushBuffer() und die Output-Stream-Schreibvorgänge, sodass er seine Header vor diesen Commits herausbekommt. Commit-Reihenfolge allein ist nicht der Bug; der Pfad mit deklariertem Content-Length ist es. Der Spring-Advisory selbst befürwortet nie die Commit-Reihenfolge-Erzählung.

Diese beiden Endpunkte beizubehalten macht das PoC falsifizierbar: Es zeigt, was nicht reproduziert, genauso klar wie das, was es tut, und beide bleiben über den Patch hinweg grün, was den einen Endpunkt, der tatsächlich umschlägt, bedeutsam macht.

Der Übergang von verwundbar → gepatcht

Eine Zeile in pom.xml, nichts weiter. Keine Quellcode-Änderung, keine Property-Änderung, kein Spring-Boot-Major- Bump:

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>

Das pinnt spring-security.version von 5.7.11 auf 5.7.14-0.cgr.2 neu (und spring-framework.version von 5.3.31 auf 5.3.39-0.cgr.4) über die spring-boot-dependencies des Parents. Gemessen:

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

Der Backport ist der Upstream-Fix

Beim Diffen der spring-security-web-Sources-Jars ist nur eine Datei relevant. 5.7.11 → 5.7.14-0.cgr.2 fügt setHeader / setIntHeader / addIntHeader-Overrides zu OnCommittedResponseWrapper hinzu, die jeweils Content-Length durch setContentLength() leiten:

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

Vor dem Fix tat dies nur addHeader, sodass setIntHeader("Content-Length", n) die verfolgte Länge des Wrappers bei 0 beließ, onResponseCommitted() nie ausgelöst wurde und HeaderWriterFilter nie seine Header schrieb, bevor Tomcat die Response committed.

Derselbe Hunk erscheint wortgleich beim Diffen von Upstream 6.5.8 (letzte verwundbare) gegen 6.5.9 (erste behobene), sodass dies der offizielle Fix ist, backportiert, keine Neuimplementierung. Der Chainguard-Build fügt zwei Null-Guards hinzu, die Upstream 6.5.9 nicht hat (value != null bei der String-Überladung und (csq != null) ? csq.length() : 4 in append).

Weitere Mitigationen

Spring Security 5.7.x ist Upstream End-of-Life; OSS-Fixes landen nur in 6.4.15 / 6.5.9 / 7.0.4+. Wenn ein neu gebautes 5.7.x keine Option ist:

  1. Von 5.7.x weg upgraden — eine Spring Boot 3.x-Migration.
  2. Workaround — setze HeaderWriterFilter.shouldWriteHeadersEagerly = true über einen ObjectPostProcessor. Laut Advisory ändert dies das Verhalten: anwendungsgeschriebene Header überschreiben dann nur bestimmte Header, statt Spring Securitys zu unterdrücken. Dieses behebt auch /vuln/cache/account, was der Versions-Bump nicht tut.
  3. Kommerzieller Support — Tanzu Spring Enterprise Backports für 5.7.x/5.8.x.
  4. Defence in depth — setze die Header am Reverse Proxy / Ingress, damit ein verworfener Anwendungs-Header nicht die einzige Kontrolle ist. Dies ist die einzige aufgeführte Option, die beide Endpunkte abdeckt.

Nichts davon ist in dieses Projekt eingebaut, sodass das verwundbare Verhalten der Standard ist und der gepatchte Zustand durch die obige einzelne <version>-Änderung erreichbar ist.

Den eingebetteten Tomcat patchen, ohne Spring Boot zu upgraden

Boot 2.7.18 pinnt Tomcat 9.0.83, das grype . mit 34 CVEs (4 Critical) markiert. Alle davon sind bei 9.0.118 oder darunter behoben, und 9.0.118 ist die neueste 9.0.x-Version — also räumt eine Property die Menge:

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

spring-boot-dependencies deklariert jedes tomcat-embed-*-Artefakt über diese einzige Property, sodass das Überschreiben core, el und websocket zusammen neu pinnt. Verifiziert:

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

Einschränkung: Bleibe auf der 9.0.x-Linie. Tomcat 10+ verschob die Servlet-API nach jakarta.*, während Spring Framework 5.3 gegen javax.servlet kompiliert, sodass ein 10.x/11.x-Bump zur Laufzeit mit NoClassDefFoundError bei den Servlet-Typen fehlschlägt.

Alles andere patchen, weiterhin innerhalb jeder Major-Linie

Derselbe Mechanismus, angewendet auf den Rest von Boot 2.7.18s verwalteten Abhängigkeiten. Keine Major-Version-Bumps und kein Spring Boot-Upgrade:

PropertyBoot 2.7.18 defaultPinned hereCeiling reason
tomcat.version9.0.839.0.118latest 9.0.x; 10+ is jakarta.*
spring-framework.version5.3.315.3.39last OSS 5.3.x on Central
jackson-bom.version2.13.52.22.2latest 2.x
log4j2.version2.17.22.26.1latest 2.x
snakeyaml.version1.301.33last 1.x; fix for the remaining CVE is 2.0
logback.version1.2.121.2.13last 1.2.x — see below
spring-security.version5.7.11left aloneit is the subject of the demo

Diese Overrides interagieren mit dem gepatchten Parent, also wisse, was sie tun, bevor du demonstrierst. Auf 2.7.18-0.cgr.3 liefert der Parent bereits tomcat.version 9.0.118, logback.version 1.2.13 und snakeyaml.version 1.33 — diese drei Zeilen werden exakte Duplikate und können gelöscht werden, ohne etwas zu ändern. Die Zeilen jackson-bom.version und log4j2.version tun weiterhin echte Arbeit: Der gepatchte Parent behält Boots Standard 2.13.5 / 2.17.2, sodass die Overrides gewinnen und diese beiden Abhängigkeiten auf reine Upstream-Builds statt auf Chainguard-gebaute auflösen. spring-framework.version ist absichtlich auskommentiert, was das 5.3.39-0.cgr.4 des Parents durchlässt.

grype .-Verlauf

StateFindingsBreakdown
Stock Boot 2.7.18997C / 39H / 38M / 15L
+ Tomcat bump653C / 23H / 29M / 10L
+ all in-major bumps433C / 12H / 18M / 10L

Vollständig bereinigt: Tomcat (34), Jackson (7), log4j (1). Insgesamt 99 → 43, Highs 39 → 12.

Nach jedem Bump verifiziert: Die App bootet auf Tomcat/9.0.118, alle sechs Endpunkte liefern 200, und die CVE reproduziert byte-genau. Spring Boot ist weiterhin 2.7.18 und Spring Security weiterhin 5.7.11, sodass CVE-2026-22732 unberührt ist — was der Punkt dieses Abschnitts ist und auch seine ehrliche Grenze: Alles drumherum zu patchen tut nichts für die Application-Framework-CVE. Diese zu beheben erfordert den Parent-Bump, nicht eine Property.

Warum Logback bei 1.2.13 stoppt

Das neueste 1.x ist 1.6.3 — gleicher Major, also nominell im Rahmen. Es funktioniert nicht. Logback 1.3+ ersetzte den SLF4J 1.7 StaticLoggerBinder durch den SLF4J 2.x ServiceLoader-Provider, und Boot 2.7s LogbackLoggingSystem ruft StaticLoggerBinder direkt auf. Gemessen mit 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)

Dem zu entkommen erfordert SLF4J 2.x (ein Major-Bump) und ein Boot 3.x-Logging-System. 1.2.13 ist die echte Obergrenze und lässt 6 Logback-Findings (2 Medium, 4 Low) auf dieser Linie unbehebbar.

Die 43, die bleiben

ComponentWhy it can't be fixed in-major
spring-webmvc / -expression / -core / -context (25)5.3.39 is the last OSS 5.3.x; 14 of 15 webmvc findings have no fix at all, and the 5.3.42 grype cites is commercial-only
logback-core (6)needs SLF4J 2.x, see above
spring-security-* (8)EOL line; CVE-2026-22732 is deliberate
spring-boot / -autoconfigure (3)no fix published for 2.7.x
snakeyaml (1)CVE-2022-1471 is fixed only in 2.0

Zwei der drei verbleibenden Criticals sind es wert, richtig gelesen zu werden statt nach Score:

  • CVE-2016-1000027 (spring-web, Fix 6.0.0) — Deserialisierung via HttpInvokerServiceExporter. Diese App verwendet keinen HTTP Invoker, also ist es hier nicht erreichbar.
  • CVE-2024-38821 (spring-security-web, Fix 5.7.13) — Static-Resource-Auth-Bypass in WebFlux. Dies ist eine Servlet-App, also ebenfalls nicht erreichbar. Es ist in-major behebbar (5.7.13/5.7.14 sind auf Central) und wurde nur belassen, um diesen Abschnitt bei 5.7.11 zu pinnen. Der gepatchte Parent bereinigt es als Nebeneffekt, da 5.7.14-0.cgr.2 hinter der Fix-Version liegt.
  • CVE-2026-22732 — im verwundbaren Zustand beabsichtigt; bereinigt durch den Parent-Bump.

Der Rest ist strukturell: Spring Framework 5.3.x und Spring Security 5.7.x sind beide EOL. Das, nicht Tomcat oder Jackson, ist das echte Argument für eine Boot 3.x-Migration.

Layout

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

Auth ist permitAll und CSRF ist aus, damit curl unauthentifiziert funktioniert — beides ist nicht Teil dieser CVE.

Quellen

  • spring.io/security/cve-2026-22732 — offizieller Advisory
  • GHSA-mf92-479x-3373
  • NVD CVE-2026-22732
  • Broadcom / Tanzu write-up
  • HeroDevs analysis
  • semgrep/cve-2026-22732-demo — die Reproduktion, deren stream/flush-Behauptungen hier nicht hielten
  • Red Hat Bugzilla #2449306
Tool herunterladen