Блог

CVE-2026-34486: как заплатка открыла новую дыру в Apache Tomcat

CVE-2026-34486-apache-tomcat
Apache / CVE / Безопасность

CVE-2026-34486: как заплатка открыла новую дыру в Apache Tomcat

Представьте: вы получили уведомление об уязвимости в Apache Tomcat, быстро обновились до версии 9.0.116, закрыли задачу в трекере и выдохнули. А потом через месяц выяснилось, что именно этот патч и создал новую дыру — на этот раз даже более опасную, чем исходная.

CVE-2026-34486 — классическая регрессия: разработчики исправляли CVE-2026-29146, padding oracle-атаку на Tomcat Tribes, но допустили логическую ошибку, которая открыла путь для unauthenticated RCE через десериализацию. Apache оценил как Important, NVD присвоил CVSS 7.5 — и это тот случай, когда цифра не передаёт реальной опасности: предыдущая уязвимость с тем же компонентом требовала часов работы, эта эксплуатируется одним пакетом. Затронуты версии 9.0.116, 10.1.53 и 11.0.20. PoC опубликован исследователем Bartlomiej Dmitruk из striga.ai.

Патч вышел 9 апреля 2026 года — коммит 776e12b3 в официальном репозитории. Если вы обновлялись конкретно ради закрытия CVE-2026-29146 и встали на 9.0.116 — вы до сих пор уязвимы. Нужен 9.0.117.

ЧТО ТАКОЕ APACHE TOMCAT TRIBES

Apache Tribes — это внутренняя шина сообщений кластера Tomcat. Когда у вас несколько инстансов за балансировщиком, им нужно синхронизировать состояние сессий, иначе пользователь, попавший на другой узел после реплейса, будет разлогинен. Tribes занимается именно этим: передаёт сообщения между узлами кластера через отдельный порт (по умолчанию 4000), недоступный снаружи периметра.

Звучит как закрытая внутренняя магистраль — и именно поэтому её безопасности часто уделяют меньше внимания, чем веб-интерфейсу. Зря. Попасть на порт Tribes можно тремя путями: скомпрометировать любой хост в корпоративной сети, найти наименее защищённый сервер в самом кластере и использовать его как плацдарм, или — что встречается чаще чем кажется — через SSRF: если веб-приложение делает HTTP-запросы по URL из пользовательского ввода, атакующий просто указывает внутренний адрес и порт 4000.

КАК РАБОТАЕТ ОШИБКА

В CVE-2026-29146 проблема была в том, что EncryptInterceptor использовал CBC-шифрование без аутентификации сообщения. Это классический padding oracle: перехватывая зашифрованные пакеты и анализируя реакцию сервера на ошибки паддинга, атакующий мог методично восстановить открытый текст. Трудоёмко, но работает.

При исправлении разработчики переработали метод messageReceived() в классе EncryptInterceptor — но вынесли вызов super.messageReceived() за пределы блока try/catch. Вот как выглядит уязвимый код:

@Override
public void messageReceived(ChannelMessage msg) {
    try {
        byte[] data = msg.getMessage().getBytes();
        data = encryptionManager.decrypt(data);
        XByteBuffer xbb = msg.getMessage();
        xbb.clear();
        xbb.append(data, 0, data.length);
    } catch (GeneralSecurityException gse) {
        log.error(sm.getString("encryptInterceptor.decrypt.failed"), gse);
    }
    // Десериализация выполнится в любом случае — даже после ошибки расшифровки
    super.messageReceived(msg);
}

Логика здесь сломана фундаментально. Если атакующий пришлёт сырые данные без шифрования, вызов decrypt() упадёт с BadPaddingException или IllegalBlockSizeException. Исключение перехватывается блоком catch, ошибка пишется в лог — и выполнение продолжается дальше. Вызов super.messageReceived(msg) уже вне блока try, поэтому он отработает в любом случае, передав оригинальные (нерасшифрованные) данные атакующего вниз по стеку.

Дальше данные попадают в GroupChannel, который передаёт их в XByteBuffer.deserialize(). Это стандартный ObjectInputStream Java — а это значит, что при десериализации автоматически вызываются методы вроде readObject() и hashCode() любых объектов, описанных в присланном payload.

КАК ЭТО ЭКСПЛУАТИРУЕТСЯ

Десериализация в Java опасна не сама по себе — опасны классы, которые есть в classpath сервера. Apache Commons Collections 3.x — практически стандартная зависимость для Java-приложений, и в ней давно известны цепочки гаджетов, ведущие прямо к выполнению произвольного кода.

Цепочка вызовов при использовании Commons Collections выглядит так:

HashSet.readObject()
  → HashMap.put(key, ...)
    → TiedMapEntry.hashCode()
      → LazyMap.get("poc")
        → ChainedTransformer.transform()
          → ConstantTransformer → Runtime.class
          → InvokerTransformer → Runtime.getMethod("getRuntime")
          → InvokerTransformer → Runtime.getRuntime()
          → InvokerTransformer → runtime.exec(cmd)

Инструмент ysoserial содержит восемь готовых вариаций для Commons Collections под разные версии библиотеки, плюс цепочки для Spring, Groovy, Commons BeanUtils и встроенных классов JDK. Задача атакующего — подобрать подходящую цепочку под конкретное окружение и отправить один пакет на порт Tribes.

Сравните с исходной CVE-2026-29146: там нужно было перехватывать трафик, методично зондировать сервер, анализировать ответы на ошибки паддинга. Здесь — один запрос, без аутентификации, без предварительного знания ключа шифрования.

РЕАЛЬНАЯ ЦЕПОЧКА АТАКИ

Атакующий находится внутри корпоративной сети — через скомпрометированный хост сотрудника, незащищённый Wi-Fi-сегмент или точку опоры на соседнем сервере кластера. Альтернативный вход — SSRF на веб-приложении: если приложение делает HTTP-запросы по URL из пользовательского ввода, атакующий направляет его на внутренний адрес и порт 4000. Порт Tribes не слушает снаружи, но изнутри — доступен.

Дальше всё просто. Атакующий определяет версию Tomcat через HTTP-заголовки или страницу ошибки, убеждается что Tribes активен (кластерная конфигурация подразумевает элемент <Cluster> в server.xml), генерирует payload через ysoserial с цепочкой под зависимости приложения — и отправляет один raw-пакет на порт 4000. Никакой аутентификации. Никакого ключа шифрования. Сервер сам вызывает нужные методы при десериализации.

Результат зависит от того, как запущен Tomcat. Без изоляции через systemd — это полная компрометация сервера одним пакетом, с правами процесса Tomcat. Если настроены PrivateTmp, NoNewPrivileges и ограничение capabilities — радиус поражения ограничен процессом, но RCE всё равно состоялся, и атакующий уже внутри.

ТАЙМЛАЙН

Исходная уязвимость CVE-2026-29146 — padding oracle в EncryptInterceptor — была исправлена в версиях 9.0.116, 10.1.53 и 11.0.20. Именно в этих версиях появился коммит 0112ed22, который вынес super.messageReceived() за пределы try/catch — и тем самым создал новую проблему.

26 марта 2026 года, вскоре после выхода патча, исследователь Bartlomiej Dmitruk из striga.ai сообщил команде безопасности Apache Tomcat о регрессии. Уведомление, судя по срокам, последовало практически сразу после выпуска — что говорит о целенаправленном аудите патча, а не случайном обнаружении.

9 апреля 2026 года опубликована CVE-2026-34486. В тот же день вышли исправленные версии 9.0.117, 10.1.54 и 11.0.21 с коммитом 776e12b3, восстановившим правильную обработку ошибок в messageReceived(). Публичный PoC от striga.ai появился вскоре после раскрытия.

ПОЧЕМУ ЭТО ВАЖНО

Это не абстрактная теоретическая угроза. Apache Tomcat — один из самых распространённых Java-серверов в мире, он используется в банках, платёжных системах, корпоративных порталах. Clustering с Tribes применяется везде, где нужна горизонтальная масштабируемость — то есть именно там, где нагрузка и ценность данных наиболее высоки.

Ситуацию усугубляет то, что многие команды могут считать себя защищёнными — они же обновились до 9.0.116 ради CVE-2026-29146. Оказывается, нет. Это создаёт ложное чувство безопасности хуже, чем если бы патча не существовало вовсе.

Отдельный урок — о природе регрессий. Исправление безопасности, внесённое без полного понимания потока выполнения, может создать уязвимость хуже исходной. CVE-2026-29146 требовала перехвата трафика и методичного подбора. CVE-2026-34486 — один пакет без аутентификации.

ЧТО ДЕЛАТЬ

Первым делом — понять, на какой версии вы вообще стоите. Если Tomcat установлен через пакетный менеджер, версия не всегда очевидна. Проверить можно так:

find / -name "catalina.jar" 2>/dev/null | xargs -I{} sh -c 'unzip -p "{}" org/apache/catalina/util/ServerInfo.properties 2>/dev/null | grep server.number'

Или через HTTP, если включена страница статуса:

curl -s http://localhost:8080/ | grep -i tomcat

Дальше — обновление. Здесь есть неочевидный момент: репозитории Debian/Ubuntu часто отстают от upstream на несколько минорных версий. Не факт что apt install tomcat9 даст вам 9.0.117 — сначала проверьте:

apt-cache policy tomcat9

Смотрите строку Candidate:. Если там не 9.0.117 или новее — обновляйте вручную с официальной страницы загрузки (tomcat.apache.org/download-90.cgi для ветки 9.x): остановить инстанс, заменить bin/ и lib/, оставив webapps/ и conf/ нетронутыми, запустить. Если репозиторий актуален — просто:

apt update && apt install tomcat9

После обновления убедитесь что версия действительно изменилась:

sh /path/to/tomcat/bin/version.sh | grep "Server version"

Должно быть 9.0.117, 10.1.54 или 11.0.21 — в зависимости от ветки. Если версия не изменилась, обновление не применилось.

Если обновить прямо сейчас нельзя — хотя бы изолируйте порт. Сначала убедитесь что Tribes вообще активен:

grep -n "Cluster" /opt/tomcat/conf/server.xml
ss -tlnp | grep 4000

Если порт слушает — закройте его через nftables, оставив доступ только доверенным узлам кластера:

nft add rule inet filter input tcp dport 4000 ip saddr != { 10.0.1.10, 10.0.1.11 } drop

Это временная мера, не замена патчу. Если Tribes вообще не используется — проверьте что элемент <Cluster> закомментирован или отсутствует в server.xml, и выключите его.

ВЫВОДЫ

CVE-2026-34486 — пример того, как заплатка сложнее болезни. Разработчики исправили padding oracle, но в результате логической ошибки в потоке выполнения создали уязвимость для уnauthenticated RCE. При этом пострадавшие версии — именно те, что считались безопасными после предыдущего патча.

Проверьте версию Tomcat прямо сейчас. Если вы на 9.0.116 — обновляйтесь до 9.0.117. Ветки 10.x и 11.x: 10.1.53 → 10.1.54, 11.0.20 → 11.0.21. Это не может ждать до следующего окна обслуживания, если порт Tribes хоть как-то доступен изнутри сети.

Leave your thought here

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поддержать автора

Если вам понравилась статья — вы можете поддержать автора.

Crypto donation button by NOWPayments