Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
cve-2026-22732-poc — Prueba de concepto que demuestra CVE-2026-22732, una falla de Spring Security donde setIntHeader("Content-Length") elimina todos los encabezados de seguridad, con compilaciones vulnerables y parcheadas. | Kitploit
Herramientas/GitHubGitHub/dylan-chainguard/cve-2026-22732-poc
Herramientas DefensivasAnálisis de VulnerabilidadesExplotaciónVirtualización de SeguridadSeguridad WebAprendizaje y Educación
GitHubdylan-chainguard/cve-2026-22732-poc

cve-2026-22732-poc

Prueba de concepto que demuestra CVE-2026-22732, una falla de Spring Security donde setIntHeader("Content-Length") elimina todos los encabezados de seguridad, con compilaciones vulnerables y parcheadas.

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Ver Repositorio
117hace 20 díasAún no revisado
Compartir

CVE-2026-22732 — Prueba de concepto

Spring Security descarta silenciosamente las cabeceras de seguridad de las respuestas HTTP. Solo para uso demostrativo/educativo; ejecútalo únicamente contra esta aplicación local.

CVECVE-2026-22732 (CWE-425), publicado el 2026-03-19
CVSS 3.19.1 CRÍTICO — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Dependencia directaspring-boot-starter-web + spring-boot-starter-security 2.7.18
Componente vulnerablespring-security-web / -config / -core 5.7.11 — solo transitivo, nunca declarado en pom.xml
Componente corregidospring-security-web 5.7.14-0.cgr.2, alcanzado con un solo cambio de <version> — véase la transición
Verificado enTomcat 9.0.118, JDK 17.0.18, macOS arm64

Rangos afectados: 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 fija Spring Security 5.7.11, justo dentro del primer rango:

$ 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

Ejecútalo

./run.sh        # terminal 1 — compila y arranca en :8080 (fija JDK 17)
./exploit.sh    # terminal 2 — recorre todos los endpoints, compara las cabeceras

run.sh fija JAVA_HOME porque Spring Boot 2.7.x no puede ejecutarse sobre el JDK 25 que mvn resuelve por defecto en esta máquina. Sobrescríbelo con JAVA_HOME_17=/path/to/jdk17.

También compila con -s settings-chainguard.xml por defecto, porque el parent parcheado no está en Maven Central. Establece MAVEN_SETTINGS=/path/to/your/settings.xml para apuntar a otro sitio, o MAVEN_SETTINGS= para compilar únicamente desde Central — lo cual solo funciona para el 2.7.18 original.

exploit.sh lee las versiones reales de spring-security-web y spring-boot desde target/*.jar, de modo que su banner siempre informa de lo que realmente se está ejecutando en lugar de una cadena codificada.

La configuración

SecurityConfig no aplica ninguna personalización de cabeceras en absoluto — los valores por defecto de Spring Security están en vigor, que es exactamente en lo que confía una aplicación consciente de la seguridad. Cada endpoint devuelve el mismo cuerpo sensible:

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

Lo único que varía es cómo el controlador escribe la respuesta.

Resultados medidos

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

Solo /vuln/content-length/account cambia de estado cuando se parchea la CVE, por lo que es el único endpoint del que exploit.sh deriva su veredicto. El resto son controles.

La CVE — setIntHeader("Content-Length", n) → bypass total

Tres líneas de código de controlador de aspecto corriente eliminan todas las cabeceras que Spring Security prometía:

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

Sin X-Frame-Options, sin X-Content-Type-Options, sin Cache-Control, sin Pragma, sin Expires, sin X-XSS-Protection. Compárese con /safe/account, que las lleva las seis. La respuesta es enmarcable desde cualquier origen, susceptible de sniffing de MIME y cacheable — mientras sirve un número de tarjeta.

El caso por diseño — cabecera de caché fijada por la aplicación → supresión de caché

Corregido: una versión anterior de este README llamaba a esto "exploit 2" y afirmaba que era la condición que documenta el aviso. No forma parte de CVE-2026-22732 y ninguna actualización lo corrige. CacheControlHeadersWriter es idéntico byte a byte en 5.7.11, 5.7.14-0.cgr.2, 6.5.8 (último vulnerable) y 6.5.9 (primero corregido) — verificado comparando los jars de fuentes. Su Javadoc declara el comportamiento sin rodeos: "Inserts headers to prevent caching if no cache control headers have been specified."

Aun así merece la pena demostrarlo, porque la fuga es real y el riesgo residual sobrevive al parcheo. CacheControlHeadersWriter se detiene si Cache-Control, Expires o Pragma ya están presentes, de modo que fijar una cualquiera de las tres suprime todas las directivas no-store de Spring Security. Una línea bienintencionada lo consigue:

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 cuenta como "presente" en la tabla anterior, pero con un valor favorable al atacante elegido por la aplicación — el Expires: 0 de Spring Security fue reemplazado, no simplemente descartado. Los datos del titular de la tarjeta ahora son almacenables por todos los navegadores y proxies compartidos en la ruta hasta 2099.

En la compilación parcheada /vuln/content-length/account pasa a NOT cacheable, mientras que /vuln/cache/account permanece exactamente como arriba. Solo el código de la aplicación o un proxy inverso lo corrige — que es lo útil que hay que decir en voz alta en una demo: actualizar la biblioteca cierra la CVE y deja esto intacto.

Dos resultados negativos, conservados a propósito

Varios artículos ampliamente difundidos — incluido un repositorio público de reproducción — listan response.getOutputStream() y response.flushBuffer() como desencadenantes, explicando que "la respuesta se confirma antes de que Spring Security pueda inyectar sus cabeceras". En Spring Security 5.7.11 eso es incorrecto. Ambos endpoints entregan las seis cabeceras.

/diag/committed muestra por qué la explicación no se sostiene. Tras una escritura de 12 KB la respuesta realmente está confirmada dentro del controlador, y aun así las cabeceras llegan:

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

OnCommittedResponseWrapper sobrescribe flushBuffer() y las escrituras del flujo de salida, de modo que emite sus cabeceras antes de esas confirmaciones. El orden de confirmación por sí solo no es el fallo; la ruta de Content-Length declarado sí lo es. El propio aviso de Spring nunca respalda la historia del orden de confirmación.

Mantener estos dos endpoints hace que la PoC sea falsable: muestra lo que no se reproduce con la misma claridad que lo que sí, y ambos permanecen en verde a través del parche, que es lo que hace que el único endpoint que sí cambia sea significativo.

La transición de vulnerable → parcheado

Una línea en pom.xml, nada más. Sin cambios en el código fuente, sin cambios de propiedades, sin salto de versión mayor de Spring Boot:

Descargar herramienta