Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
cve-2026-22732-poc — Proof-of-concept, демонстрирующий 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

Proof-of-concept, демонстрирующий CVE-2026-22732 — уязвимость Spring Security, при которой setIntHeader("Content-Length") удаляет все заголовки безопасности, с уязвимой и исправленной сборками.

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Репозиторий
11720 дней назадЕщё не проверено
Поделиться

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, достигается одним изменением <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        # терминал 1 — собирает и запускает на :8080 (фиксирует JDK 17)
./exploit.sh    # терминал 2 — обходит все эндпоинты, сравнивает заголовки

run.sh фиксирует JAVA_HOME, потому что Spring Boot 2.7.x не может работать на JDK 25, который mvn выбирает по умолчанию на этой машине. Переопределите через 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

Только /vuln/content-length/account меняет состояние при исправлении CVE, поэтому только по нему exploit.sh выносит свой вердикт. Остальные — контрольные.

CVE — setIntHeader("Content-Length", n) → полный обход

Три строки обычного на вид кода контроллера снимают каждый заголовок, обещанный 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. Сравните с /safe/account, который несёт все шесть. Ответ может быть встроен во фрейм с любого origin, подвержен MIME-sniffing и кэшируем — при этом отдавая номер карты.

Случай по замыслу — установленный приложением заголовок кэша → подавление кэширования

Исправлено: более ранняя версия этого README называла это «эксплойтом 2» и утверждала, что это условие, документированное в advisory. Это не часть CVE-2026-22732, и никакое обновление это не исправляет. CacheControlHeadersWriter побайтово идентичен в 5.7.11, 5.7.14-0.cgr.2, 6.5.8 (последняя уязвимая) и 6.5.9 (первая исправленная) — проверено сравнением sources jar. Его Javadoc прямо указывает на это поведение: «Inserts headers to prevent caching if no cache control headers have been specified.»

Это всё равно стоит демонстрировать, потому что утечка реальна и остаточный риск сохраняется после патча. CacheControlHeadersWriter прекращает работу, если Cache-Control, Expires или Pragma уже присутствует, поэтому установка любого одного из трёх подавляет все директивы no-store от Spring Security. Достаточно одной благонамеренной строки:

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 считается «присутствующим» в таблице выше, но с дружественным атакующему значением, выбранным приложением — Expires: 0 от Spring Security был заменён, а не просто отброшен. Данные держателей карт теперь могут храниться каждым браузером и общим прокси на пути до 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 ответ действительно фиксируется внутри контроллера, но заголовки всё равно приходят:

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

OnCommittedResponseWrapper переопределяет flushBuffer() и записи в output-stream, поэтому он выпускает свои заголовки раньше этих фиксаций. Один лишь порядок фиксации не является багом; путь с объявленным Content-Length — является. Сам Spring advisory никогда не поддерживает историю о порядке фиксации.

Сохранение этих двух эндпоинтов делает PoC опровержимым: он показывает, что не воспроизводится, так же ясно, как и то, что воспроизводится, и оба остаются зелёными на протяжении всего патча, что и делает значимым единственный эндпоинт, который действительно переключается.

Переход от уязвимого к исправленному

Одна строка в pom.xml, и ничего больше. Ни изменения исходников, ни изменения свойств, ни мажорного обновления Spring Boot:

Скачать инструмент