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

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

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

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

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

Категории

Все категории
Loading categories
Инструменты/GitHubGitHub/moderneinc/rewrite-cve-2026-22732
Статический анализАнализ уязвимостейАнализ КодаВеб-безопасностьDevSecOps
GitHubmoderneinc/rewrite-cve-2026-22732

rewrite-cve-2026-22732

Рецепт OpenRewrite, который обнаруживает и исправляет подавление заголовков Spring Security (CVE-2026-22732), выявляя неправильное использование заголовка Content-Length и генерируя конфигурацию для немедленной записи заголовков.

Популярное

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

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

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

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

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

rewrite-cve-2026-22732

OpenRewrite-рецепт, который обнаруживает код, подверженный CVE-2026-22732 — дефекту Spring Security, при котором установка Content-Length через один из трёх методов ответа обходит OnCommittedResponseWrapper в Spring Security. Поскольку обёртка никогда не видит заголовок, onResponseCommitted() не срабатывает, и лениво добавляемые заголовки безопасности (X-Frame-Options, X-Content-Type-Options, Cache-Control и т. д.) молча отбрасываются.

Что он находит

Фактические триггеры, подтверждённые на уязвимой версии Spring Security 6.4.12 с Spring Boot 3.4.3 / встроенным Tomcat:

  1. Servlet Content-Length через перегрузки, обходящие обёртку

root@kitploit:~
response.setHeader("Content-Length", "42");
response.setIntHeader("Content-Length", 42);
response.addIntHeader("Content-Length", 42);

Эти три перегрузки не переопределены в OnCommittedResponseWrapper. Последующие записи тела завершают объявленную длину, и контейнер фиксирует ответ без срабатывания ленивого записи заголовков.

  • WebFlux Content-Length через HttpHeaders

    root@kitploit:~
    serverHttpResponse.getHeaders().set("Content-Length", "42");
    serverHttpResponse.getHeaders().add("Content-Length", "42");
    serverHttpResponse.getHeaders().setContentLength(42L);
    
  • Безусловные фиксации ответа в WebFlux

    root@kitploit:~
    serverHttpResponse.writeWith(Mono.just(dataBuffer));
    serverHttpResponse.writeAndFlushWith(publisher);
    serverHttpResponse.setComplete();
    
  • Рецепт ограничен наличием Spring Security — он ничего не выдаёт в файлах, которые не ссылаются ни на один тип org.springframework.security.*, — а также затронутыми диапазонами версий Spring Security. Согласно рекомендации Spring, опубликованной 2026-03-19, затронутые диапазоны и исправленные версии:

    СерияЗатронутыеИсправленные
    5.7.x5.7.0 – 5.7.215.7.22 (Enterprise)
    5.8.x5.8.0 – 5.8.235.8.24 (Enterprise)
    6.3.x6.3.0 – 6.3.146.3.15 (Enterprise)
    6.4.x6.4.0 – 6.4.146.4.15 (Enterprise)
    6.5.x6.5.0 – 6.5.86.5.9 (OSS)
    7.0.x7.0.0 – 7.0.37.0.4 (OSS)

    Проекты, разрешающие версию Spring Security на уровне исправления или выше в своей серии (или в любой будущей серии после 7.0 / 6.5), считаются незатронутыми и не получают маркеров ни по отдельным точкам, ни по файлам. Проекты, где версию не удаётся разрешить, переходят к обычному обнаружению по шаблонам, чтобы сканер скорее сообщал о находке, которую не может опровергнуть. Таблица данных SpringSecurityVersionByProject по-прежнему записывает разрешённую версию и помечает каждый проект как затронутый или нет, чтобы вы могли проверить, что было отфильтровано.

    Что намеренно НЕ помечается

    Это выглядит опасно, но отслеживается обёрткой, поэтому заголовки безопасности записываются до фиксации ответа:

    КодПочему это безопасно
    response.setContentLength(int) / setContentLengthLong(long)Переопределено — обёртка записывает объявленную длину и вызывает onResponseCommitted(), когда тело завершается.
    response.flushBuffer()Переопределено — вызывает doOnResponseCommitted() до super.flushBuffer().
    response.getOutputStream().write(..) / flush() / close()Возвращает SaveContextServletOutputStream; каждая запись/сброс/закрытие вызывает doOnResponseCommitted() перед делегированием.
    response.getWriter().write(..) / print(..) / println(..) / flush() / close()Возвращает SaveContextPrintWriter; та же схема.
    response.addHeader("Content-Length", v)Особый случай в обёртке — направляется через setContentLength(long).

    Конечная точка /vuln/flush в демо Semgrep утверждает, что триггером является flushBuffer(), но на уязвимой версии Spring Security 6.4.12 ответ фактически возвращает все шесть заголовков безопасности. Настоящие триггеры в демо — это вызовы setIntHeader("Content-Length", ...) в /vuln/stream и /vuln/content-length.

    Обнаружение

    Запустите этот:

    РецептНазначение
    io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppressionВыполняет все проверки и формирует таблицу отчёта по версиям

    Строительные блоки (продвинутый уровень)

    Агрегатор выше состоит из двух более мелких рецептов. Вы можете вызывать их по отдельности, если нужна только одна проверка.

    РецептНазначение
    io.moderne.recipe.cve202622732.FindHttpResponseContentLengthHeaderПоток заражения для литерала "Content-Length", достигающего setHeader / setIntHeader / addIntHeader (servlet) или HttpHeaders.set / add (WebFlux)
    io.moderne.recipe.cve202622732.FindHttpResponseContentLengthOrFlushBufferБезусловные фиксации в WebFlux: writeWith, writeAndFlushWith, setComplete, HttpHeaders.setContentLength

    Исправление

    Запустите этот:

    РецептНазначение
    io.moderne.recipe.cve202622732.FixSpringSecurityHeaderSuppressionВыбирает самое дешёвое исправление, которое реально может применить каждый проект

    Он выполняет два шага по порядку.

    1. Переход на исправление в собственной серии проекта. Spring Security опубликовал исправление как 6.5.9 и 7.0.4 в Maven Central. Каждый переход ограничен предусловием FindAffectedSpringSecuritySeries, поскольку UpgradeDependencyVersion проверяет только, что целевая версия новее — при указании 7.0.4 он охотно перетащил бы проект на 5.8 через две мажорные версии.

    2. Добавление конфигурации упреждающей записи заголовков для того, что шаг 1 не смог исправить. Это генерирует один класс @Configuration на проект:

    root@kitploit:~
    @Bean
    public static BeanPostProcessor eagerHeaderWriterFilterBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof HeaderWriterFilter) {
                    ((HeaderWriterFilter) bean).setShouldWriteHeadersEagerly(true);
                }
                return bean;
            }
        };
    }
    

    Запись заголовков заранее делает неважным, наблюдает ли обёртка за фиксацией, поэтому это закрывает все точки в проекте сразу — включая те, до которых анализ заражения не может добраться, например поток через Map в разделе «Известные ограничения». BeanPostProcessor видит фильтры, созданные DSL HttpSecurity, потому что AutowireBeanFactoryObjectPostProcessor инициализирует их через фабрику бинов. Две независимо опубликованные исправления для этой CVE используют ровно такую форму (hmcts/idam-web-public, и форки Spinnaker от armory-io через эквивалентный ObjectPostProcessor).

    Шаг 2 покрывает проекты, которым шаг 1 не может помочь:

    СитуацияПочему переход не работает
    5.7, 5.8, 6.3, 6.4Исправление поставляется только подписчикам Spring Enterprise — его нет в Maven Central
    6.0 - 6.2Для этих серий исправление никогда не выпускалось
    Версия управляется импортированным BOMЛокально ничего не объявлено, что переход мог бы редактировать

    Последняя строка — не крайний случай. nla/bamboo разрешает 7.0.3 — серию с исправлением с открытым исходным кодом — полностью из BOM Spring Boot, поэтому проверка версии сама по себе пропустила бы его на обоих шагах и оставила бы уязвимым. Поэтому AddEagerHeaderWriterConfiguration откладывает переход только тогда, когда у проекта есть доступное исправление с открытым исходным кодом и он объявляет собственную версию.

    Сгенерированный класс размещается рядом с классом @EnableWebSecurity, если такой существует, с откатом на @SpringBootApplication, а затем на любой @Configuration, так что он всегда попадает туда, куда доходит компонентное сканирование. Проекты, которые уже записывают заголовки упреждающе или поставляют пропатченный OnCommittedResponseWrapper (как jogetworkflow/jw-community), не трогаются.

    Строительные блоки (продвинутый уровень)

    РецептНазначение
    io.moderne.recipe.cve202622732.UpgradeSpringSecurityToPatchedVersionПереход с ограничением по серии на 6.5.9 / 7.0.4
    io.moderne.recipe.cve202622732.AddEagerHeaderWriterConfigurationСгенерированная конфигурация, отдельно
    io.moderne.recipe.cve202622732.FindAffectedSpringSecuritySeriesПомечает проекты на затронутой серии; предусловие перехода

    Проверено на работающем сервере

    Применено к semgrep/cve-2026-22732-demo на уязвимой версии Spring Security 6.4.12, против встроенного Tomcat. Его HeaderVerificationTest утверждает, что заголовки безопасности отсутствуют, поэтому рабочее исправление заставляет его падать:

    Конечная точкаДоПосле
    /vuln/streamX-Content-Type-Options: nullnosniff
    /vuln/content-lengthX-Content-Type-Options: nullnosniff
    /safenosniffnosniff

    X-Frame-Options и Cache-Control следуют той же схеме.

    По 16 репозиториям корпуса, которые собираются (из 29 выявленных), исправление сгенерировало конфигурацию для трёх и корректно оставило остальные без изменений:

    РепозиторийРезультат
    semgrep/cve-2026-22732-demoСгенерировано в com/example/vuln, рядом с @EnableWebSecurity; компилируется, заголовки восстановлены
    nla/bambooСгенерировано в ui/src/bamboo, рядом с @SpringBootApplication; компилируется. Случай с управляемой через BOM версией 7.0.3, до которого переход не может добраться
    star-whale/starwhaleСгенерировано в ai/starwhale/mlops/configuration/security, рядом с @EnableWebSecurity; компилируется (JDK 11, его объявленная цель)
    hmcts/idam-web-publicОставлен без изменений — уже вызывает setShouldWriteHeadersEagerly
    okta/okta-idx-java (6.5.9), psi-probe (6.5.11), Ant-Media-Server (6.5.11)Оставлены без изменений — уже после исправления в своей серии
    apache/shenyu (6.3.1)Оставлен без изменений — только реактивный; за HeaderWriterFilter нет servlet API, а CVE касается только servlet
    brutusin/Brutusin-RPC (4.0.4)Оставлен без изменений — предшествует setShouldWriteHeadersEagerly (5.2)
    bootplus, template-app, front50, igor, rosco, spring-security, reportserverОставлены без изменений — не разрешена затронутая версия Spring Security

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

    Второй, более широкий корпус нацелен на популяцию, которую исправление фактически охватывает — любое затронутое servlet-приложение Spring Security, поскольку точка не требуется. Из 64 таких проектов, найденных поиском по коду, 52 собрались, 48 разрешили затронутую версию, и 38 были пропатчены; 35 из них компилируются (остальные три падают одинаково и без сгенерированного файла). Все 8 проектов на Spring Security ниже 5.2 были корректно пропущены. См. раздел 8 в SUSCEPTIBLE-REPOSITORIES.md.

    Ограничения

    • Обнаружения WebFlux — это другая опасность, не эта CVE. OnCommittedResponseWrapper extends jakarta.servlet.http.HttpServletResponseWrapper, поэтому CVE-2026-22732 касается только servlet, и реактивное приложение ей не подвержено. Находки из FindHttpResponseContentLengthOrFlushBuffer помечают аналогичный реактивный паттерн и всё ещё требуют ручной проверки, но исправление намеренно на них не действует. AddEagerHeaderWriterConfiguration пропускает любой модуль, который видит HeaderWriterFilter без servlet API за ним — фильтр расширяет OncePerRequestFilter, а spring-security-web несёт servlet API как нетранзитивную зависимость provided, поэтому генерация там падает с cannot access jakarta.servlet.Filter (наблюдалось на apache/shenyu).
    • Версии ниже Spring Security 5.2 обнаруживаются, но не исправляются. HeaderWriterFilter.setShouldWriteHeadersEagerly появляется в 5.2; на 4.0.4 у фильтра есть только конструктор и doFilterInternal. Обнаружение по-прежнему сообщает об EOL-версиях, но исправление удерживается, а не выдаёт вызов, который не может скомпилироваться.
    • Упреждающие заголовки записываются для каждого запроса, включая те, что позже заменяются диспетчеризацией ошибки. Это компромисс, которого избегает ленивое поведение Spring Security по умолчанию, и именно поэтому переход выполняется первым.

    Таблицы данных

    ТаблицаСтроки
    TaintFlowTable (из rewrite-program-analysis)По одной строке на каждое попадание заражения заголовка Content-Length
    HttpResponseDirectCommitTableПо одной строке на каждое структурное попадание WebFlux
    SpringSecurityVersionByProjectПо одной строке на проект с обнаруженной версией Spring Security

    Запуск

    Через Moderne CLI:

    root@kitploit:~
    mod run . --recipe io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression
    

    Через rewrite.yml:

    root@kitploit:~
    ---
    type: specs.openrewrite.org/v1beta/recipe
    name: com.example.DetectSpringSecurityHeaderSuppression
    displayName: Detect CVE-2026-22732
    recipeList:
      - io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression
    

    Воспроизведение оценочного корпуса

    repos.csv перечисляет 75 публичных репозиториев, на которых эти рецепты разрабатывались и измерялись, закреплённых за точным коммитом, на котором каждый оценивался. Некоторые активно поддерживаются и будут пропатчены выше по течению, поэтому колонка changeset — это то, что делает числа ниже воспроизводимыми, а не просто правдоподобными.

    root@kitploit:~
    mod git sync csv ./corpus repos.csv --with-sources
    mod build ./corpus
    mod run ./corpus --recipe=io.moderne.recipe.cve202622732.CveDevCenter
    mod devcenter ./corpus --last-recipe-run
    

    Синхронизация занимает около 20 секунд и 1,4 ГБ; сборка занимает примерно 15 минут и является единственным медленным шагом. mod devcenter записывает devcenter.html в corpus/.moderne/run/<runId>/, плюс по одному на подкаталог организации.

    Колонка org1 делит корпус на группы, отражающие причину присутствия каждого репозитория — Servlet Sinks и WebFlux Sinks для двух уязвимых форм вызовов, Patched для репозиториев, уже исправленных выше по течению, Reference для самого Spring Security и других не-потребителей, Verified для случая, проверенного на работающем сервере, Gradle для покрытия инструментов сборки и Wide для массовой выборки.

    Ожидайте на закреплённых коммитах:

    Результат
    Карточка обновления39 Major, 21 Minor, 6 Patch, 4 Completed (70 репозиториев)
    Карточка безопасности65 подверженных репозиториев
    Не применимо5 репозиториев не разрешают ни одной зависимости Spring Security

    Четыре из этих пяти действительно не используют Spring Security — spring-projects/spring-security это сама библиотека, JoeyBling/bootplus использует Apache Shiro, jenkinsci/stapler нацелен непосредственно на servlet API, а infofabrik/reportserver не имеет сборки Maven или Gradle для разрешения. Пятый, xtuer/template-app, объявляет spring-security-web:5.0.0.RELEASE, но его сборка Gradle вообще не разрешает зависимостей во время mod build, поэтому ни один рецепт не может увидеть версию. Считайте его неизмеренным, а не незатронутым.

    Чтобы применить исправление и проверить результат:

    root@kitploit:~
    mod run ./corpus --recipe=io.moderne.recipe.cve202622732.FixSpringSecurityHeaderSuppression
    mod git apply ./corpus --last-recipe-run
    mod exec ./corpus --last-recipe-run MODERNE_BUILD_TOOL_CHECK
    

    mod git apply записывает в рабочие копии на месте. Полная check по корпусу медленная и выявит сбои, не связанные с этим изменением — отсутствующие цепочки инструментов JDK, недоступные репозитории зависимостей, тесты, которые уже были красными, — поэтому значимым сигналом является дельта относительно той же команды, запущенной до применения.

    Что покрыто помимо буквального демо

    Анализ заражения из rewrite-program-analysis обрабатывает локальный поток данных и сводки по методам, поэтому эти паттерны обнаруживаются автоматически:

    • Имя заголовка Content-Length, распространённое через константу. String h = "Content-Length"; response.setIntHeader(h, 42); помечается — фреймворк отслеживает заражение литерала через локальное присваивание.
    • Хелпер, оборачивающий вызов — заражение проходит через возвращаемые значения посредством сводок методов.

    Известные ограничения

    • Поток через обобщённые типы контейнеров (Map, List, пользовательские коллекции). stash.put("k", "Content-Length") с последующим response.setIntHeader(stash.get("k"), 42) не обнаруживается — тождественность put/get непрозрачна для анализа.

    Лицензия

    Moderne Proprietary. Только для использования клиентами Moderne на условиях коммерческого контракта.

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