
CVE-2020-2551 POC इंटरनेट पर उपयोग करने के लिए
टेस्ट POC (इंटरनेट परीक्षण के लिए उपलब्ध)
python CVE-2020-2551.py [HOST] [IP]
codebase को संशोधित करके दूरस्थ क्लास लोडिंग का प्रयास (विफल)
python CVE-2020-TEST.py [HOST] [IP]
(पहली बार Anquanke पर प्रकाशित, मूल लिंक)
इस भेद्यता को सीखने के लिए कुछ पूर्वापेक्षित ज्ञान आवश्यक है, जैसे CORBA और RMI।
संक्षेप में एक अवलोकन:
CORBA, OMG द्वारा विकसित एक तकनीकी मानक है, जिसका उपयोग वितरित अनुप्रयोगों के लिए किया जाता है। इसमें क्रॉस-भाषा समर्थन के लिए IDL का उपयोग होता है, और क्लाइंट तथा सर्वर के बीच संचार IIOP प्रोटोकॉल के माध्यम से होता है।
RMI एक अन्य वितरित अनुप्रयोग तकनीक है; Java में JNDI का उपयोग करके इसे सरल बनाया जा सकता है। क्लाइंट और सर्वर JRMP प्रोटोकॉल के माध्यम से संचार करते हैं, हालाँकि weblogic में RMI T3 प्रोटोकॉल का उपयोग करता है। इस संबंध में पहले भी कई भेद्यताएँ सामने आ चुकी हैं।
RMI-IIOP RMI और CORBA दोनों की अपनी-अपनी खूबियों को जोड़ता है और IIOP प्रोटोकॉल के माध्यम से RMI अनुप्रयोगों को तैनात करता है।
आधिकारिक दस्तावेज़ में भी उल्लेख है:
RMI सर्वर ऑब्जेक्ट IIOP प्रोटोकॉल का उपयोग कर सकते हैं और किसी भी भाषा में लिखे गए CORBA क्लाइंट ऑब्जेक्ट के साथ संचार कर सकते हैं।
फिलहाल weblogic पर ध्यान केंद्रित न करते हुए, पहले देखें कि RMI-IIOP उदाहरण कैसे लिखा जाता है:
क्लाइंट कोड के लिए, Java में RMI, JNDI, LDAP, JRMP, JMX, JMS वाली बातें (भाग 1) में दिए गए परीक्षण प्रोजेक्ट को देख सकते हैं। आप HelloClient और HelloServer को स्वयं कंपाइल कर सकते हैं, या परीक्षण प्रोजेक्ट में पहले से कंपाइल किए गए बाइनरी का उपयोग कर सकते हैं।
कमांड लाइन पर नेम सर्वर (Java के साथ आता है) प्रारंभ करें:
start orbd -ORBInitialPort 1050
कमांड लाइन से HelloServer सर्वर प्रारंभ करें और रिमोट डिबगिंग कॉन्फ़िगर करें। IDEA से रिमोट डिबगिंग कैसे करें, इसके लिए यहाँ के आरंभ में बताई गई विधि देखें।
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 HelloServer
बेशक, रिमोट डिबगिंग के बिना भी सीधे परिणाम देख सकते हैं; सीधे चलाएँ:
java HelloServer
कमांड लाइन से क्लाइंट प्रारंभ करें:
Java HelloClient
इस समय एक कैलकुलेटर खुलेगा। यदि रिमोट डिबगिंग सफल होती है, तो आप निम्न कॉल स्टैक देख सकते हैं:
EvilMessage.readObejct() में कमांड निष्पादित होता है

एक अलग बात: weblogic की स्थापना और डिबगिंग पर लेख
तो weblogic में RMI-IIOP कैसा है? Java में RMI-IIOP के बारे में लेख में weblogic RMI-IIOP के शोषण का उल्लेख है; उसी के आधार पर कुछ शोध किए गए। WebLogic के IIOP पर RMI का उपयोग करना में weblogic द्वारा RMI-IIOP क्लाइंट का उपयोग करने के कई तरीके बताए गए हैं, जिनमें शामिल हैं:
पहले दो तरीकों के बीच अंतर केवल JNDI_FACTORY सेटिंग में प्रतीत होता है।

पहले जब weblogic T3 डिसीरियलाइज़ेशन का अध्ययन किया, तो weblogic पर एक Helloserver एप्लिकेशन तैनात किया था, जिसमें sayhello() विधि का उपयोग किया जा सकता है। दोनों JNDI_FACTORY सेटिंग्स आज़माने के बाद, दूसरी JNDI_FACTORY से sayHello() विधि सफलतापूर्वक कॉल हुई।

तो हमने weblogic T3 प्रोटोकॉल के POC को संशोधित किया — वास्तव में केवल RMI को IIOP में बदला। देखा कि jtaTransactionManager शोषण श्रृंखला सफलतापूर्वक निष्पादित हुई और स्थानीय jrmplisten को एक jrmp अनुरोध भेजा।

ट्रैफ़िक देखें; जब remove() विधि कॉल होती है, तो remove__java_lang_Object अनुरोध भेजा जाता है। ट्रैफ़िक में दुर्भावनापूर्ण डेटा है, लेकिन aced मैजिक हेडर नहीं मिला।

अनुमान है कि सर्वर पक्ष पर विशेष पार्सिंग के बाद डेटा का डिसीरियलाइज़ेशन होता है। कॉल स्टैक देखने पर पता चलता है कि बाद वाला भाग पहले वाले नेटिव RMI-IIOP निष्पादन श्रृंखला के समान है। पहले यह CDRInputStream.read_value() से ट्रिगर होता था; यहाँ यह weblogic में IIOPInputStream.read_value() से ट्रिगर होता है। (यह read_value बिंदु 2019 की प्रस्तुति में भी उल्लिखित है।)

यहाँ अनुरोध पहले clusterableServerRef.invoke() को दिया जाता है, और विभिन्न invoker के अनुसार this.invoker.invoke() कॉल होता है। फिर यहाँ Mejb_dj5nps_HomeImpl_WLSkel.invoke() कॉल होता है। चूँकि यह "remove" है, यह case 6 ब्रांच में जाता है और IIOPInputStream.readObject() को कॉल करता है; read_value() विधि में IIOPInputStream डेटा पार्स होकर डिसीरियलाइज़ेशन ट्रिगर होता है। यह remove() विधि का उपयोग करने वाला POC है।
Lucifaer के [विश्लेषण लेख](https://lucifaer.com/2020/02/25/WebLogic WLS核心组件RCE分析(CVE-2020-2551)/?from=timeline&isappinstalled=0#2-2-Weblogic解析流程) में bind() विधि के माध्यम से शोषण का उल्लेख है; यह इंटरनेट पर मुख्यधारा का शोषण तरीका भी है। कॉल स्टैक का अनुसरण करें।

पहले जैसे ही, यहाँ अनुरोध पहले clusterableServerRef.invoke() को दिया जाता है, और विभिन्न invoker के अनुसार this.invoker.invoke() कॉल होता है। यहाँ CobraServerRef.invoke() कॉल होता है, फिर _NamingContextAnyImplBase._invoke() में, चूँकि va1 "bind_any" है, यह case 0 ब्रांच में जाता है और IIOPInputStream.read_any() विधि कॉल करता है; आगे भी IIOPInputStream.read_value() कॉल होकर डिसीरियलाइज़ेशन ट्रिगर करता है। पहले कहा गया था कि ट्रैफ़िक में aced मैजिक हेडर नहीं दिखा, इसका कारण यह है कि IIOPInputStream में एक पार्सिंग विधि है। IIOPInputStream का hex-value रूप निम्न है, जिसमें क्लास नाम और फ़ील्ड जानकारी शामिल होती है:

अंततः दुर्भावनापूर्ण क्लास की readObejct() विधि कॉल होती है।

पैच देखने पर पता चला कि यह 2015 में T3 डिसीरियलाइज़ेशन शोषण के पैच के समान स्थान पर है।

WebLogic CVE-2020-2551 भेद्यता विश्लेषण के परीक्षण में देखा गया कि CVE-2020-2551 में फ़िल्टर की गई क्लास का स्थान भी weblogic.iiop.Utils क्लास में है।

लेकिन स्थानीय परीक्षण के दौरान, weblogic 10.3.6 पर 2015 का पैच लगाने पर isBlacklisted() फ़ंक्शन ट्रिगर नहीं हुआ (हालाँकि MsgAbbrevInputStream और InboundMsgAbbrev दोनों में ब्लैकलिस्ट सत्यापन के लिए isBlacklisted() कॉल होता है, अजीब है...।)

इस बार CVE-2020-2551 के पैच में weblogic.iiop.Utils.LoadClass() में फ़िल्टरिंग हेतु verifyclassermitted() विधि जोड़ी गई।

ब्लैकलिस्ट दुर्भावनापूर्ण क्लासों को फ़िल्टर करती है, जिनमें JtaTransactionManager की पैरेंट क्लास com.bea.core.repackaged.springframework.transaction.support.AbstractPlatformTransactionManager भी शामिल है। यह क्लास weblogic के साथ आती है और बहुत खतरनाक है। पैच देखते समय एक विचार आया: क्योंकि पंक्ति 606 पर सत्यापन LoadClass() के बाद होता है, यदि className लोड करते समय क्लास लोड होकर दुर्भावनापूर्ण स्टैटिक कोड ब्लॉक निष्पादित कर दे, तो क्या यह बचाव को बायपास नहीं कर देगा? इस पर बाद में चर्चा करेंगे।
JAVA प्रोग्राम में लिखे गए POC में नेटवर्क समस्या है — सीधे स्थानीय weblogic सेवा पर हमला करना ठीक है, लेकिन Docker कंटेनर या बाहरी नेटवर्क वाली मशीन पर हमला करना काम नहीं करता। इस समस्या का उल्लेख करने वाले विश्लेषण लेख:
Weblogic CVE-2020-2551 POC नेटवर्क समस्या को कदम-दर-कदम हल करना
WebLogic-CVE-2020-2551 पर अनौपचारिक चर्चा
अब POC को डिबग करते हैं। आप पहले वाले remove() विधि का संदर्भ ले सकते हैं, या Y4er का। पिछले दो लेखों में दो समाधान बताए गए हैं:
मैंने दोनों आज़माए। weblogic को फिर से पैकेज करने के बाद, java.lang.NoSuchMethodError:weblogic.security.subject.SubjectManager.installCESubjectManager त्रुटि आती है, लेकिन इसका कोई समाधान नहीं मिला।
इसलिए IIOP प्रोटोकॉल का अनुकरण करने का प्रयास किया; पहले POC पर ब्रेकपॉइंट लगाकर डिबग किया।

पाया कि new InitialContext(env) करते समय, EndPointImpl.sendReceive() में दो पैकेट भेजे और प्राप्त किए गए।

LocateReply में IOR जानकारी होती है। यहाँ IOR क्या है समझना आवश्यक है। इसका कार्य यह है कि जब RMI-IIOP क्लाइंट IIOP प्रोटोकॉल द्वारा सर्वर ऑब्जेक्ट के साथ इंटरैक्ट करता है, तो यह IIOP संचार के लिए आवश्यक host और port प्रदान करता है; इसके अलावा लाल बॉक्स में दिया गया Object_key सर्वर के विभिन्न ऑब्जेक्ट्स को अलग करने के लिए उपयोग होता है।

IIOP प्रोटोकॉल का अनुकरण करते समय मुख्य ध्यान Object_key पर होना चाहिए; host और IP वास्तव में मायने नहीं रखते। पहले जब परीक्षण शुरू किया, तो सभी पैकेट्स को सीधे फिर से रीप्ले किया, और resolve_any भेजने पर location forward लौटा।

GIOP के आधिकारिक दस्तावेज़ में कहा गया है कि location forward का अर्थ है कि Object_key बदल सकता है; विभिन्न अनुरोधों में लौटाया गया Object_key अलग हो सकता है (यहाँ Object_key का अर्थ डेटा पैकेट में key address है)। यह Object_key, जैसा पहले बताया गया है, IIOP प्रोटोकॉल में यह तय करता है कि किस ऑब्जेक्ट के साथ संचार करना है; इस मान को LocateReply से गतिशील रूप से प्राप्त करना आवश्यक है।

अंत में यहाँ remove() का अनुकरण नहीं चुना, बल्कि bind() विधि द्वारा भेजे गए IIOP अनुरोध का अनुकरण किया, क्योंकि उसमें अनुरोध कम होते हैं। स्थानीय सामान्य शोषण का डेटा पैकेट देखें:

LocateRequest भेजें, data प्राप्त करें, और रेगुलर एक्सप्रेशन से मिलान करके LocateReply में key address प्राप्त करें।

मैन्युअल रूप से दुर्भावनापूर्ण jrmp सर्वर (rmi://...) का पता सेट करें और हर 1 सेकंड में bind_any पैकेट भेजें। चूँकि यहाँ की शोषण श्रृंखला द्वारा भेजा गया jrmp अनुरोध DGCClient का उपयोग नहीं करता, यह JEP290 से प्रभावित नहीं होता और jrmplisten के माध्यम से शोषण किया जा सकता है।

POC Docker वातावरण में सफलतापूर्वक परीक्षित हुआ। vulhub वाले इस SSRF वातावरण का उपयोग करके, IP को होस्ट मशीन पर सेट करें; Docker ने सफलतापूर्वक होस्ट मशीन से jrmp अनुरोध प्राप्त किया। विशिष्ट कोड Github पर रखा गया है।

पहले बताया गया था कि codebase का उपयोग करके दूरस्थ कोड लोड करके डिटेक्शन को बायपास करने का विचार है। जिन्होंने JNDI हमलों का अध्ययन किया है, वे जानते होंगे कि codebase का उपयोग दूरस्थ क्लास का स्थान निर्दिष्ट करने के लिए किया जा सकता है। यदि codebase नियंत्रणीय है और प्रोग्राम दूरस्थ क्लास लोड करने की अनुमति देता है, तो दूरस्थ दुर्भावनापूर्ण क्लास लोड करके स्टैटिक कोड ब्लॉक में दुर्भावनापूर्ण कोड निष्पादित किया जा सकता है।
कोड पढ़ने से पता चलता है कि weblogic.iiop.Utils.lodaClass() का दूसरा पैरामीटर codebase को दर्शाता है। यह पैरामीटर IIOPInputStream.read_value() में पढ़ा जाता है, अर्थात var8 पैरामीटर। पंक्ति 1659 पर readIndirectingRepositoryId(var8) कॉल होता है, जो अंततः weblogic.iiop.Utils.lodaClass() को कॉल करेगा। पंक्ति 1644 और 1659 को निष्पादित करने के लिए (va4r & 1)=1 और (va4 & 6)=2 चाहिए, इसलिए var4 का मान 3 होता है।
readIndirectingRepositoryId से getClassFromId तक का कॉल स्टैक; अंत में पंक्ति 304 पर loadclass() निष्पादित होगा।

bind_any डेटा पैकेट देखें; यह वास्तव में GIOP Header और GIOP Request से मिलकर बना है। GIOP Request में key address (LocateteReply के समान), ServiceContextList और stub_data शामिल होते हैं। var4 का मान stub_data में \x7f\xff\xff\x02 है, इसलिए (va4r & 1)=0 और (va4 & 6)=2 होता है; अर्थात पंक्ति 1644 का कोड निष्पादित नहीं होगा, जो codebase सेट करता है।

हमने पहले बॉक्स में \x7f\xff\xff\x02 को \x00\x00\x00\x03 में बदला, और दूसरे बॉक्स में codebase की लंबाई और मान की जानकारी जोड़ी। विशिष्ट कोड Github पर है। देखा जा सकता है कि एक अलाइनमेंट (alignment) ऑपरेशन भी किया गया है; यह एक पेचीदा जगह है, क्योंकि आगे की क्लास जानकारी पढ़ने से पहले यह जाँचा जाता है कि अगले बाइट की स्थिति 4 की गुणज है या नहीं। यदि नहीं, तो कुछ बाइट्स को अनदेखा कर दिया जाता है। उदाहरण के लिए, यदि अगले बाइट की स्थिति 1 है, तो 3 बाइट्स छोड़कर सीधे स्थिति 4 वाले बाइट से पढ़ना शुरू होता है। यह स्थिति पूरे bind_any पैकेट के सापेक्ष है; यहाँ यदि बाइट 4 की गुणज नहीं है, तो शून्य (0) जोड़कर पैडिंग की जाती है।

एक और समस्या है। पहले वाले readIndirectingRepositoryId से getClassFromId तक के कॉल स्टैक को देखें — बीच में findClassInfo() फ़ंक्शन आता है। यहाँ, यदि कोई क्लास पहले लोड हो चुकी है, तो उसकी क्लास ID जानकारी सहेज ली जाती है; findClassInfo() सीधे क्लास जानकारी लौटा देता है और weblogic.iiop.Utils.getClassFromID() फ़ंक्शन में प्रवेश नहीं करता।

इसलिए परीक्षण करते समय हर बार क्लास का नाम बदलना पड़ता है।

खैर, अंत में IIOP प्रोटोकॉल का अनुकरण करके codebase मान को संशोधित करने में सफलता मिली, और weblogic.iiop.Utils.getClassFromId() फ़ंक्शन निष्पादित हुआ।
दुर्भाग्य से, RMIURLClassFinder प्राप्त करते समय NULL लौटा, और RMIEnvironment.getEnvironment().isNetworkClassLoadingEnabled() फ़ंक्शन false लौटाता है।

कारण यह है कि ServerMBeanImpl में _NetworkClassLoadingEnable पैरामीटर का मान False है।

यह देखना चाहता था कि किस weblogic कॉन्फ़िगरेशन फ़ाइल में यह पैरामीटर सेट है, लेकिन नहीं मिला...
वास्तव में, इस भेद्यता को सीखने के दौरान पता चला कि बहुत सारे पूर्वापेक्षित ज्ञान की आवश्यकता है, जैसे Java डिसीरियलाइज़ेशन, RMI, JNDI आदि। संबंधित अध्ययन के लिए इस लेख कॉलम को देख सकते हैं। हालाँकि अंत में codebase संशोधित करके शोषण असफल रहा, फिर भी काफी कुछ सीखने को मिला।