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

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

CVE-2026-34486 — это кри­тичес­кая уяз­вимость уда­лен­ного выпол­нения кода (RCE) через десери­али­зацию в модуле Apache Tomcat Tribes. Она поз­воля­ет неав­торизо­ван­ному зло­умыш­ленни­ку зах­ватить сер­вер одним зап­росом. На деле это рег­рессия CVE-2026-29146: исправ­ляли одну уяз­вимость, но соз­дали новую. При этом новую уяз­вимость лег­че экс­плу­ати­ровать. Если в CVE-2026-29146 надо было перех­ватывать зашиф­рован­ные пакеты и методич­но зон­дировать сер­вер, что­бы по его реак­ции на ошиб­ки пад­динга вос­ста­новить откры­тый текст, то в CVE-2026-34486 дос­таточ­но отпра­вить один сырой зап­рос.

Apache Tomcat Tribes — модуль Apache Tomcat для обме­на сооб­щени­ями меж­ду сер­верами Tomcat, если их нес­коль­ко. Нес­коль­ко сер­веров нуж­ны для балан­сиров­ки наг­рузки в высоко­наг­ружен­ных про­ектах, ког­да один сер­вер не справ­ляет­ся с тра­фиком. Нап­ример, рас­пре­делен­ные про­екты ста­рают­ся раз­мещать сер­веры бли­же к поль­зовате­лям, что­бы сок­ратить вре­мя откли­ка, но есть и дру­гие вари­анты при­мене­ния.

В любом слу­чае, если сер­веров нес­коль­ко, воз­ника­ют проб­лемы с обес­печени­ем целос­тнос­ти дан­ных и бес­шовной работы при­ложе­ния. Если поль­зователь авто­ризо­вал­ся, о сес­сии дол­жны узнать все сер­веры. Ина­че при перек­лючении поль­зовате­лю при­дет­ся авто­ризо­вывать­ся сно­ва и сно­ва. Что­бы решить эти проб­лемы, и нужен Apache Tribes.

Tribes управля­ет внут­ренней кух­ней клас­тера сер­веров; его пор­ты недос­тупны сна­ружи. Есть три основных сце­нария ата­ки:

  1. Дос­туп к кор­поратив­ной сети. Зло­умыш­ленник, нап­ример, может взло­мать компь­ютер сот­рудни­ка или про­ник­нуть в незащи­щен­ный Wi-Fi.
  2. Ата­ка на сосед­ний сер­вер. Клас­тер сер­веров не всег­да находит­ся внут­ри кор­поратив­ной сре­ды. В этом слу­чае у хакера есть шанс най­ти наиме­нее защищен­ный сер­вер и исполь­зовать его как точ­ку опо­ры.
  3. Клас­сичес­кая SSRF (server-side request forgery). Если веб‑при­ложе­ние уяз­вимо, хакер может зас­тавить сер­вер сде­лать зап­рос к Tribes и отпра­вить вре­донос­ный пей­лоад.
 

Анатомия уязвимости

При исправ­лении CVE-2026-29146 раз­работ­чики допус­тили ошиб­ку в клас­се EncryptInterceptor, который дол­жен был обес­печивать защиту:

@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);
}

В безопас­ном коде каж­дый пакет дан­ных рас­шифро­выва­ет фун­кция encryptionManager.decrypt() с помощью клю­ча. Если хакер приш­лет сырые дан­ные или дан­ные, зашиф­рован­ные неп­равиль­ным клю­чом, код упа­дет с ошиб­кой BadPaddingException или IllegalBlockSizeException. Выпол­нение кода прек­ратит­ся.

При­мер безопас­ного метода messageReceived мож­но пос­мотреть в исходни­ках Tribes вер­сии 9.0.115 — в фай­ле EncryptInterceptor.java, на стро­ке 142.

Функция messageReceived в версии 9.0.115
Фун­кция messageReceived в вер­сии 9.0.115

В ком­мите 0112ed22 раз­работ­чики вынес­ли вызов super.messageReceived() за пре­делы try/catch. Это логичес­кая ошиб­ка. Хакеру не нуж­но под­бирать ключ шиф­рования — наобо­рот, нуж­но выз­вать ошиб­ку рас­шифров­ки. Если хакер отпра­вит откры­тые дан­ные, сер­вер не упа­дет, а запишет ошиб­ку в лог и про­дол­жит работу.

Уязвимая версия функции messageReceived
Уяз­вимая вер­сия фун­кции messageReceived

Вре­донос­ные бай­ты идут пря­миком в ком­понент GroupChannel, который исполь­зует класс XByteBuffer для обра­бот­ки сооб­щения. Пер­вый этап обра­бот­ки — десери­али­зация с помощью XByteBuffer.deserialize(). Метод запус­кает стан­дар­тный механизм чте­ния объ­ектов через поток ObjectInputStream.

Коммит 0112ed22
Ком­мит 0112ed22

При десери­али­зации объ­екта Java авто­мати­чес­ки выпол­няет слу­жеб­ные методы вро­де readObject() или hashCode(), которые опи­саны в клас­се. Важ­но понимать, что ата­кующий не добав­ляет ничего нового, он толь­ко отправ­ляет объ­ект со зна­комой сер­веру струк­турой. Сер­вер зна­ет каж­дый вло­жен­ный объ­ект и может сопос­тавить его с кон­крет­ным клас­сом. Клас­сы про­писа­ны в его classpath адми­нами или раз­работ­чиками. Час­то это клас­сы из Apache Commons Collections или встро­енных биб­лиотек JDK. Это обще­извес­тные наборы клас­сов с хорошо иссле­дован­ными осо­бен­ностя­ми и сла­быми сто­рона­ми.

info

Commons Collections (часть про­екта Apache Commons) — биб­лиоте­ка для Java, которая допол­няет стан­дар­тный Java Collections Framework (JCF). В ней есть новые струк­туры дан­ных и инс­тру­мен­ты для работы с объ­екта­ми. Нап­ример, Trie — дре­вовид­ная струк­тура дан­ных для быс­тро­го поис­ка строк по пре­фик­су.

Пей­лоад — это цепоч­ка свя­зан­ных клас­сов (гад­жетов), конеч­ная цель которой — вызов опас­ной фун­кции, нап­ример java.lang.Runtime.getRuntime().exec(). Если опи­сать цепоч­ку гад­жетов в виде объ­екта, такой объ­ект выг­лядел бы так:

{
"HashSet": {
"HashMap": {
"TiedMapEntry": {
"LazyMap": {
"ChainedTransformer": [
{ "ConstantTransformer": "java.lang.Runtime.class" },
{ "InvokerTransformer": { "method": "getMethod", "args": ["getRuntime"] } },
{ "InvokerTransformer": { "method": "invoke", "args": [] } },
{ "InvokerTransformer": { "method": "exec", "args": ["whoami"] } }
]
}
}
}
}
}

Пол­ная цепоч­ка вызовов на при­мере Apache 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)

Раз­работ­чики не всег­да исполь­зуют Commons Collections, это прос­то популяр­ный набор. Есть и аль­тер­нативы: Spring, Groovy, Commons BeanUtils, JDK. Кро­ме того, вер­сия Commons Collections быва­ет раз­ной, и для каж­дой выс­тра­ивает­ся своя цепоч­ка. Нап­ример, в ysoserial вши­то восемь вари­аций для этой биб­лиоте­ки. Задача хакера — подоб­рать под­ходящую цепоч­ку гад­жетов.

 

Сборка тестового окружения

Соз­даем Dockerfile, который уста­новит и нас­тро­ит уяз­вимую вер­сию Apache Tomcat 9.0.116:

# Уязвимая версия Apache Tomcat
FROM tomcat:9.0.116-jdk8
RUN mv /usr/local/tomcat/webapps.dist/* /usr/local/tomcat/webapps/
RUN set -ex \
&& curl -fSL https://repo1.maven.org/maven2/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar \
-o /usr/local/tomcat/lib/commons-collections-3.2.1.jar
COPY server.xml /usr/local/tomcat/conf/server.xml

Об­рати вни­мание: для тес­та исполь­зуем commons-collections-3.2.1.jar.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии