The Jerusalem PostNetanyahu received three warnings from Egypt about Gaza including in days before October 7 - reportESPNFollow live: Rams hold lead over scoreless Broncos in first halfESPN DeportesCosta Rica fracasa ante Haití y pone en riesgo Copa Oro y Liga APunchS’Court judgment: Parties insist candidates safeCNN TürkAKARYAKIT FİYATLARI ZAM GELDİ Mİ? 28 Eylül Benzin, Motorin, LPG'ye Zam Var Mı Yok Mu? Akaryakıta Ne Zaman Zam Gelecek? İstanbul, Ankara, İzmir Akaryakıt FiyatlarıInquirerMarcos greets longtime family lensman Tata Willy on 89th birthday한겨레세종대 탄수화물소재연구소, 제13회 정기 학술심포지엄 개최 “AI 기반 소재 설계부터 효소 공학·기능성 탄수화물 개발까지 융합 연구 성과 공유”BillboardLISA Pays Tribute to Thailand With High-Octane ‘SaWaDiKa’ Performance at VMAs 2026South China Morning PostChickens in Malaysia are producing fewer eggs. Is haze a leading cause?UOLRavens dominam Maracanã no terceiro jogo da NFL no Brasil경향신문입주 시작한 ‘모아주택 1호’ 찾은 오세훈 “2031년까지 4만가구 공급”Daily MailBBC faces backlash over story claiming viral bare fingernails trend is 'racist and classist'
The Daily Newsstand · Free, Always
Monday, September 28, 2026

Разбор CVE-2026-83557 в jackson-databind: почему не всех CVE надо бояться

Translate

Утро четверга. Вы открываете таск-трекер, а там тикет от сканера зависимостей: «jackson-databind, CVE-2026-83557, критично, патчить». Ссылка на GitHub-бюллетень, CVSS, слово «полиморфная десериализация», и через двадцать минут у вас уже согласован hotfix на прод в пятницу в 18:00.

Стоп. Я проделал обратное: взял описание, собрал работающий эксплойт и выполнил его на уязвимой и пропатченной версиях. Рассказываю, почему для большинства проектов тут хватает тикета «в ближайший релиз», без всякого горения. И заодно о том, почему не каждой CVE из сканера стоит верить на слово.

Что вообще случилось

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

public class Holder {    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)    public Comparable<?> value;
}
{"value": ["java.io.File", "/etc/passwd"]}

Это формат WRAPPER_ARRAY: первый элемент массива служит именем класса, второй содержит данные, из которых Jackson соберет экземпляр.

Понятно, что нельзя принимать произвольные имена классов из непроверенного JSON, поэтому в Jackson есть механизм PolymorphicTypeValidator, который решает, какой подтип допустим для данного базового типа. Валидатор по умолчанию называется DefaultBaseTypeLimitingValidator. Задумка у него такая: держать черный список «небезопасных» базовых типов, с полиморфизмом на которых Jackson решил бороться. Позиций в списке девять, от банальных Object и Serializable до javax.sql.DataSource. Все, что в список не попало, одобряется - проверять подтип валидатор не умеет вообще.

Вот и вся CVE: в списке забыли java.lang.Comparable, а зря. Comparable реализует куча JDK-классов, от численных оберток до String и File. Если есть полиморфное свойство с базовым типом Comparable, атакующий подсунет type id почти любого из них, и Jackson молча создаст экземпляр.

Версия jackson-databind у вас в проекте с огромной вероятностью попадет под диапазон уязвимых (это 2.11-2.18.9, 2.19.0-2.21.5 и 2.22.0-2.22.1). Поэтому не будем тратить время на версию, а сразу посмотрим на два реальных барьера в вашем коде.

Барьер первый: включенный флаг защиты

DefaultBaseTypeLimitingValidator подключается только при включенном MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES. По умолчанию этот флаг выключен.

Что происходит без флага? Для обычного свойства с @JsonTypeInfo(Id.CLASS) Jackson использует LaissezFaireSubTypeValidator, который разрешает все. Но это не CVE-2026-83557. Это давно известное и документированное поведение Id.CLASS.

Получается парадокс под ударом оказались только те, кто явным образом включал защитный флаг. Включить его и не перепроверить руками список базовых типов - вот и весь сценарий.

Барьер второй: у вас должно быть Comparable-свойство

Валидатор смотрит на объявленный тип свойства, и вот тут черный список работает: Object и Serializable он честно блокирует, payload с ними отваливается еще при построении десериализатора. То есть CVE касается только свойств, объявленных как Comparable<?> или сырой Comparable, с аннотацией @JsonTypeInfo.

Посмотрите на свои DTO: конкретные типы, коллекции, бизнес-модели. Полиморфное поле типа Comparable, которому подсовывают произвольный type id, встретишь разве что в проекте из серии «мы сериализуем сортируемые значения любых типов и радостно разбираем их обратно». Это крайне редкий паттерн, которого в большинстве сервисов просто нет.

Допустим, все сошлось. Что получит атакующий?

Показать проще, чем объяснять. Вот весь «уязвимый» код приложения, больше для атаки ничего не нужно:

public class Holder {    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)    public Comparable<?> value;
}
// где-то в обработчике запроса
Holder h = mapper.readValue(requestBody, Holder.class);

Атакующий присылает в requestBody одну строчку:

{"value": ["java.io.File", "/etc/passwd"]}

И после парсинга в поле h.value лежит живой java.io.File. Jackson, по сути, выполнил за атакующего вот это:

Comparable<?> value = new File("/etc/passwd");

Библиотека взяла имя класса из JSON, нашла его в classpath приложения, сконструировала и записала в поле. Единственные ограничения: класс должен реализовывать Comparable и присутствовать в classpath. Этот примитив называется контролируемым инстанцированием: атакующий выбирает класс и данные, но исполняемого кода в конструкторе сам написать не может.

У меня на 2.19.4 эксплойт выдает:

[1] control: Serializable-typed property + payload {"value":["java.io.File","/etc/hosts"]}    -> rejected as expected (Serializable IS in the denylist)
[2] exploit: Comparable-typed property + same payload    -> VULNERABLE: accepted java.io.File under Comparable base type:       instantiated java.io.File, path = /etc/hosts, exists = true

Сравните прогоны [1] и [2]: payload один и тот же, различие только в объявленном типе свойства, а результат противоположный. Вся CVE видна в этих двух строчках вывода.

Файл создан, exists даже true. И… все. Автор бюллетеня честно пишет, что чистого эксплойта для выполнения кода не нашел. Я тоже не нашел и вот почему особенно не искал. Почему в denylist попали именно базовые типы вроде Runnable, logging.Handler, Referenceable или DataSource? Они исторически обвешаны опасным кодом, JNDI-лукапами и кредами. У Comparable конструкторы скучные: сравнение двух объектов само по себе никого не взломает.

Это не значит, что дыра безобидна в принципе: инстанцирование непроверенных классов может стать ступенью для будущей цепочки атаки, а оракул существования файлов сам по себе приятного мало. CVSS 5.6 (Moderate) оценивает это адекватно. Адекватность теряется не в бюллетене, а в корпоративной цепочке «сканер, тикет, срочно».

Бонус: патч не бесплатный

Я прогнал тот же PoC на 2.21.6. Comparable в denylist добавили; фикс вышел в 2.18.10, 2.21.6 и 2.22.2, выбирайте свою ветку. Но вот нюанс: базовый тип теперь запрещен целиком, вместе с легитимными подтипами. Мой кейс с собственным классом SafeThing implements Comparable<SafeThing> на пропатченной версии падает с InvalidDefinitionException. Если у вас есть легитимная полиморфия на Comparable, после обновления она отвалится, и ее придется выносить на явный BasicPolymorphicTypeValidator с белым списком.

Важная оговорка: ломается это только при включенном BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES. С выключенным флагом патч ничего не меняет, десериализатор ведет себя одинаково и до, и после обновления. Под раздачу снова попадают только те, кто защищался.

То есть принцип «пришла CVE, катим патч на все подряд» и здесь дает сбой: обновление может уронить функциональность там, где сама CVE никогда бы не сработала.

Пять минут на проверку

# 1. Включен ли защитный флаг (без него эта CVE к вам неприменима в принципе)
grep -rn "BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES" src/
# 2. Есть ли полиморфные Comparable-поля: файлы, где встречаются и @JsonTypeInfo, и Comparable<
grep -rln "@JsonTypeInfo" src/ | xargs grep -l "Comparable<"

Насчет второй команды: слово Comparable в живом проекте встречается часто (от компараторов до сортировок), поэтому скрипт выдает кандидатов на ручной просмотр, а не окончательный вердикт. Смотреть надо места, где @JsonTypeInfo стоит на свойстве, объявленном как Comparable.

Выключенный флаг или отсутствие Comparable-полей: любой из двух ответов закрывает тикет со спокойной совестью. Оба проверяются быстрее, чем пишется комментарий «фиксим, критично!!!».

Почему это важнее, чем одна CVE

Сканер зависимостей видит две вещи: версию библиотеки и номер CVE. Он не видит конфигурацию маппера и структуру ваших DTO. А именно эти вещи решают, существует ли уязвимость в реале. В случае CVE-2026-83557 на пути уязвимости стоят два конкретных барьера в коде и конфигурации.

Для контраста возьмем Log4Shell. Там условием был пользовательский ввод в лог. Логировать то, что присылает пользователь, делает почти каждое приложение, отдельно стараться не надо. Поэтому Log4Shell стреляла массово, а эта CVE - нет.

Jackson патчить надо, разумеется. Без драм, с тестами, в ближайший релиз. А если ваша корпоративная машина разводит пожар из каждой Moderate-CVE, лечить надо машину.

Все эксперименты из статьи воспроизводимы: PoC на сотню строк Java, версии 2.19.4 (уязвимая) и 2.21.6 (пропатченная), JDK 25.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.