
एंड-टू-एंड Docker लैब जो Apache log4j2 #4255 को पुनः प्रस्तुत करता है — Log4j 2.26.1 / JDK 17 पर java.rmi.MarshalledObject के माध्यम से FilteredObjectInputStream allowlist बाईपास (अनफ़िल्टर्ड डिसीरियलाइज़ेशन → RCE)
FilteredObjectInputStream अनुमति-सूची बाईपास java.rmi.MarshalledObject के माध्यम सेएक स्व-निहित, कंटेनरीकृत लैब जो Apache log4j2 समस्या #4255 को आधिकारिक Log4j 2.26.1 आर्टिफैक्ट्स के विरुद्ध JDK 17 पर अंत-से-अंत तक पुनरुत्पादित करती है, जिसमें सकारात्मक नियंत्रण, एक कठोर ओरेकल, और एक सत्यापित शमन उपाय शामिल है।
⚠️ जिम्मेदार उपयोग
इस लैब में एक वर्तमान में अनपैच किए गए Log4j समस्या (#4255 लेखन के समय OPEN /
waiting-for-maintainerहै; कोई CVE निर्धारित नहीं) के विरुद्ध एक कार्यशील डिसीरियलाइज़ेशन RCE शामिल है। यह पूरी तरह से आपकी अपनी मशीन पर डिस्पोज़ेबल Docker कंटेनरों के अंदर चलता है और आधिकारिक जार डाउनलोड करने के लिए Maven Central के अलावा किसी भी बाहरी चीज़ से कनेक्ट नहीं होता — किसी भी लक्ष्य से संपर्क नहीं किया जाता।
- मूल रिपोर्टर (U-Sec / Wujie Security) फिक्स आने तक अपना PoC रोके हुए है। यह एक स्वतंत्र पुनरुत्पादन है जो डिफेंडर सत्यापन और डिटेक्शन इंजीनियरिंग के लिए बनाया गया है।
Log4j का FilteredObjectInputStream (FOIS) एक resolveClass-आधारित डिसीरियलाइज़ेशन अनुमति-सूची है।
इसकी अनुमति-सूची में java.rmi.MarshalledObject शामिल है। एक MarshalledObject अपने पेलोड को एक अपारदर्शी byte[] के रूप में संग्रहीत करता है, और MarshalledObject.get() उस पेलोड को एक नए, अनफ़िल्टर्ड ObjectInputStream पर डिसीरियलाइज़ करता है — इसलिए अनुमति-सूची आंतरिक ग्राफ़ का निरीक्षण कभी नहीं करती।
Log4j इसे स्वयं ट्रिगर करता है: Log4jLogEvent$LogEventProxy (एक LogEvent का सीरियलाइज़्ड वायर रूप, 2.8 से) इवेंट Message को एक MarshalledObject के अंदर ले जाता है और डिसीरियलाइज़ेशन के दौरान स्वचालित रूप से .get() कॉल करता है (readResolve() → message())। कोई भी एप्लिकेशन जो FOIS के माध्यम से एक सीरियलाइज़्ड LogEvent पढ़ता है, इसलिए हमलावर बाइट्स का अनफ़िल्टर्ड डिसीरियलाइज़ेशन करता है — और क्योंकि message() परिणामी अपवाद को निगल जाता है और SimpleMessage पर वापस आ जाता है, रिसीवर एक सौम्य इवेंट लॉग करता है और चलता रहता है। शोषण मौन है।
rel/2.26.1 के विरुद्ध सत्यापित)| # | स्थान | दोष |
|---|---|---|
| 1 | log4j-api …/util/internal/SerializationUtil.java | REQUIRED_JAVA_CLASSES में java.rmi.MarshalledObject शामिल है |
| 2 | log4j-api …/util/FilteredObjectInputStream.java | केवल resolveClass() को ओवरराइड करता है; MarshalledObject का objBytes पेलोड इसके लिए अदृश्य है |
| 3 | log4j-core …/impl/Log4jLogEvent.java | LogEventProxy.marshalledMessage एक MarshalledObject<Message> है |
| 4 | log4j-core …/impl/Log4jLogEvent.java | message() marshalledMessage.get() (फ़िल्टर-रहित) कॉल करता है और सभी अपवादों को निगल जाता है |
अप्रचलित log4j-samples ObjectInputStreamLogEventBridge के लिए एक विश्वसनीय स्टैंड-इन: एक अनप्रमाणित TCP रिसीवर जो प्रति कनेक्शन FOIS के माध्यम से एक सीरियलाइज़्ड LogEvent पढ़ता है।
हमलावर एक एकल सीरियलाइज़्ड ऑब्जेक्ट भेजता है; ओरेकल एक प्रूफ फ़ाइल है जो एक निर्देशिका में लिखी जाती है जो केवल रिसीवर कंटेनर में बाइंड-माउंटेड होती है, इसलिए इसका दिखना साबित करता है कि डिसीरियलाइज़ेशन के माध्यम से रिसीवर के अंदर कोड चला।
| # | परिदृश्य | पीड़ित क्लासपाथ | jdk.serialFilter | अपेक्षित |
|---|---|---|---|---|
| S1 | अस्वीकृत गैजेट टॉप-लेवल भेजा गया | + गैजेट | कोई नहीं | अस्वीकार — FOIS अपनी अनुमति-सूची लागू करता है |
| S2 | वही गैजेट एक LogEvent में लपेटा गया | + गैजेट | कोई नहीं | rce — स्वचालित-ट्रिगर (पीड़ित पर गैजेट क्लास की आवश्यकता है) |
| S3 | कच्चा CommonsCollections6 टॉप-लेवल | log4j + cc-3.2.1 | कोई नहीं | अस्वीकार — FOIS CC को ब्लॉक करता है |
| S4 | CC6 को MarshalledObject में जोड़ा गया | log4j + cc-3.2.1 केवल | कोई नहीं | rce — पीड़ित पर कोई हमलावर क्लास नहीं |
| S5 | S4 पेलोड | log4j + cc-3.2.1 | !java.rmi.MarshalledObject | अस्वीकार — शमन |
| S6 | S4 पेलोड | log4j + cc-3.2.1 | maxdepth=5;maxbytes=1000000 | मौन — आंतरिक स्ट्रीम फ़िल्टर को इनहेरिट करती है; CC6 बहुत गहरा |
| S7 | S4 पेलोड | log4j + cc-3.2.2 | कोई नहीं | मौन — 3.2.2 असुरक्षित फंक्टर डिसेर को अक्षम करता है |
rce = कोड निष्पादित। reject = FOIS ने बाहरी स्ट्रीम पर अपवाद फेंका। silent = बाहरी ऑब्जेक्ट संसाधित, कोई कोड नहीं चला (गहराई में अवरुद्ध, या गैजेट संस्करण सुरक्षित है)।
./run.sh # या: make run
आवश्यकताएँ: केवल Docker (एक JDK को eclipse-temurin:17-jdk के रूप में खींचा जाता है)। स्क्रिप्ट आधिकारिक जार डाउनलोड करती है और उपयोग से पहले उन्हें Maven Central SHA-1 के विरुद्ध सत्यापित करती है। कमजोर सीमा में एक अलग Log4j संस्करण पिन करें: LOG4J_VERSION=2.20.0 ./run.sh।
LogEvent का एकल अनप्रमाणित ~2.8 KB TCP लेखन। यह एक स्ट्रिंग लॉग करवाकर ट्रिगर करने योग्य नहीं है (Log4Shell के विपरीत) — इसे सॉकेट ब्रिज तक पहुँचने वाले कच्चे सीरियलाइज़्ड बाइट्स की आवश्यकता होती है।LogEvent बनाएं → Log4j का writeReplace()/writeObject() Message को एक MarshalledObject में लपेटता है → गैजेट उसके अपारदर्शी byte[] के अंदर छिप जाता है → रिसीवर पर, readResolve() → message() → MarshalledObject.get() एक नई अनफ़िल्टर्ड स्ट्रीम खोलता है → गैजेट → Runtime.exec। src/attacker/Attacker.java (poc2) एक शुद्ध-CommonsCollections6 ग्राफ़ को MarshalledObject के objBytes में जोड़ता है, इसलिए पीड़ित पर किसी हमलावर क्लास की आवश्यकता नहीं है।-Djdk.serialFilter='!java.rmi.MarshalledObject' (S5)।
चेतावनी: यह वैध सीरियलाइज़्ड LogEventProxy ऑब्जेक्ट्स को भी ब्लॉक करता है (वे भी MarshalledObject का उपयोग करते हैं) — यह प्रभावी है लेकिन सीरियलाइज़्ड-लॉग परिवहन के लिए पारदर्शी नहीं है।maxdepth/maxbytes फ़िल्टर। प्रक्रिया-व्यापी फ़िल्टर आंतरिक MarshalledObject स्ट्रीम में प्रसारित होता है और गहराई वहाँ पुनः शुरू होती है, इसलिए maxdepth=5 CC6 (S6) को ब्लॉक करता है लेकिन एक उथला गैजेट पास हो जाएगा। चेन-गहराई-निर्भर, एक सीमा नहीं।MarshalledObject को अनुमति-सूची से हटाएं और मार्शल किए गए संदेश को Log4j के फ़िल्टर्ड writeWrappedObject/readWrappedObject में स्थानांतरित करें।LogEvent रिसीवर उजागर करता है और उसके क्लासपाथ पर एक उपयोग योग्य गैजेट संस्करण हो। सामान्य Log4j तैनातियाँ ऐसा कोई रिसीवर नहीं चलाती हैं।net.server.TcpSocketServer को 2017 में log4j-core से बाहर ले जाया गया; 2.9.0 से अनुपस्थित)। आधुनिक रिसीवर एप्लिकेशन/नमूना कोड हैं, जिन्हें यह लैब मॉडल करता है।uid=0 कंटेनर रूट है — कोई Docker एस्केप नहीं है; RCE रिसीवर प्रक्रिया के रूप में चलता है।MarshalledObject एक resolveClass फ़िल्टर को पराजित करना) ज्ञात पूर्व कला है; Apache चर्चा देखें #4168 ("Log4j 2.x deserialization hardening")। Log4j-विशिष्ट स्वचालित-ट्रिगर #4255 का योगदान है।run.sh पोर्टेबल रनर (चेकसम-पिन्ड, कठोर ओरेकल)
Makefile make build | run | clean
src/victim/Receiver.java FOIS लॉग रिसीवर (ObjectInputStreamLogEventBridge स्टैंड-इन)
src/attacker/Attacker.java पेलोड बिल्डर: नियंत्रण, PoC-1, PoC-2 (CC6 + बाइट-स्प्लाइस)
src/attacker/EvilMessage.java PoC-1 स्व-निहित गैजेट
docs/RESULTS.md साक्ष्य मैट्रिक्स + विश्लेषण
कमजोरी की रिपोर्ट U-Sec (Wujie Security) द्वारा Apache log4j2 #4255 में की गई। यह रिपॉजिटरी रक्षात्मक अनुसंधान और डिटेक्शन इंजीनियरिंग के लिए एक स्वतंत्र पुनरुत्पादन/सत्यापन लैब है।