
नैतिक, नेटवर्क-पृथक Docker लैब जो CVE-2026-26030 को पुनरुत्पादित करता है — Semantic Kernel इन-मेमोरी वेक्टर स्टोर फ़िल्टर eval() RCE (1.39.4 में पैच किया गया)
eval() RCE (लैब)CVE-2026-26030 को रिप्रोड्यूस करने वाली एक सेल्फ-कंटेन्ड लैब: Microsoft Semantic Kernel (Python, < 1.39.4) में इन-मेमोरी वेक्टर स्टोर सर्च फ़िल्टर के माध्यम से प्रॉम्प्ट-इंजेक्टेबल रिमोट कोड एक्ज़ीक्यूशन।
केवल नैतिक लैब। समर्पित virtualenvs में आइसोलेटेड; पेलोड हानिरहित हैं (हेडलेस PoC में
touchएक मार्कर फ़ाइल; UI डेमो मेंopen -a Calculator) और आपके अपने अनप्रिविलेज्ड यूज़र के रूप में चलते हैं।
./setup.sh # दो आइसोलेटेड venvs बनाता है (1.39.3 कमजोर, 1.39.4 पैच्ड)
इसे python3.13 चाहिए (SK की numpy/scipy डिपेंडेंसी के लिए व्हील्स)। PYTHON= से ओवरराइड करें।
./run.sh # दोनों venvs पर एक ही पेलोड चलाता है
कमजोर आउटपुट RCE CONFIRMED पर समाप्त होता है; पैच्ड आउटपुट उसी पेलोड को '__subclasses__' ... is not allowed के साथ अस्वीकार कर देता है।
OpsBot, एक आंतरिक इंजीनियरिंग नॉलेज-बेस एजेंट (Semantic Kernel के माध्यम से Groq-होस्टेड Llama), एक search_runbooks(team) टूल एक्सपोज़ करता है। एक हमलावर एक दुर्भावनापूर्ण team वैल्यू प्रॉम्प्ट-इंजेक्ट करता है; एजेंट टूल को कॉल करता है, कमजोर फ़िल्टर चलता है, और TextEdit में एक "रैन्सम नोट" खुल जाता है — जबकि एजेंट रनबुक परिणाम रिपोर्ट करता रहता है, अनजाने में। पेलोड (open -e <note>) नॉन-ब्लॉकिंग और हानिरहित है; नोट /tmp/PWNED_by_CVE-2026-26030.txt पर प्री-स्टेज्ड है।
यह टूल असली कमजोर फ़िल्टर eval को एक साफ़ सबप्रोसेस (run_filter.py) में चलाता है। यह एक macOS आवश्यकता है, कोई चीट नहीं: सर्वर का अपना fork() async LLM/httpx थ्रेड्स + numpy/scipy से पॉइज़न हो जाता है, इसलिए उस प्रोसेस से GUI लॉन्च चुपचाप नो-ऑप कर जाता है। सबप्रोसेस बिल्कुल CVE कोड पाथ है (_parse_and_validate_filter → एक रिकॉर्ड पर lambda चलाना)।
नोट: क्लीन-सबप्रोसेस स्प्लिट इस macOS डेमो हार्नेस की एक विचित्रता है, न कि कमजोरी की। Linux-डिप्लॉयड एजेंट पर इन-प्रोसेस
os.systemसीधे फायर होता है — किसी सबप्रोसेस की ज़रूरत नहीं।
echo 'GROQ_API_KEY=gsk_...' > .env # console.groq.com/keys से फ्री की
./demo-ui/run.sh # http://127.0.0.1:8000
हमलावर का संदेश इनपुट बॉक्स में प्री-लोडेड है; Send दबाएँ।
एक एजेंट InMemoryCollection द्वारा समर्थित एक सर्च टूल एक्सपोज़ करता है। LLM बातचीत से एक फ़िल्टर एक्सप्रेशन स्ट्रिंग उत्सर्जित करता है (lambda x: x.team == 'platform')। वह स्ट्रिंग हमलावर-प्रभावित होती है — यूज़र प्रॉम्प्ट के माध्यम से, या रिट्रीव्ड/टूल कंटेंट में इंजेक्टेड टेक्स्ट के माध्यम से — और _parse_and_validate_filter में पहुँचती है, जो इसे compile() और eval() करता है (connectors/in_memory.py:383):
code = compile(tree, filename="<filter>", mode="eval")
func = eval(code, {"__builtins__": {}}, {}) # nosec
__builtins__ खाली किया जाता है, और पहले एक AST allowlist लागू की जाती है — इसलिए यह एक सैंडबॉक्स बायपास है, न कि कोई मिसिंग गार्ड। दो गैप इसे बायपास करने योग्य बनाते हैं:
ast.Attribute एक्सेस अनरेस्ट्रिक्टेड है — कोई डंडर ब्लॉकलिस्ट नहीं, इसलिए ().__class__.__base__.__subclasses__ डंडर-वॉकिंग की अनुमति है।ast.Call नाम-जाँच केवल func को तब इंस्पेक्ट करती है जब वह Name या Attribute हो। जब func एक Subscript होता है (जो allowlist में है), func_name None ही रहता है और अनुमत-फ़ंक्शन जाँच पूरी तरह स्किप हो जाती है।इसलिए किसी भी कॉलेबल को [obj.method][0](https://github.com/inertfluid/sk-cve-2026-26030-lab/blob/HEAD/args) के रूप में लपेटकर कुछ भी कॉल किया जा सकता है। चेन:
object.__subclasses__()[i] -> BuiltinImporter.load_module('os') -> os.system(cmd)
lambda x: [[[().__class__.__base__.__subclasses__][0]()[107].load_module][0](https://github.com/inertfluid/sk-cve-2026-26030-lab/blob/HEAD/%27os%27).system][0]('touch /tmp/pwned_by_filter')
हर कॉल का func एक Subscript है; हर ट्रैवर्सल स्टेप सादा एट्रिब्यूट एक्सेस है। इंडेक्स (107) object.__subclasses__() में BuiltinImporter की पोज़िशन है — यह Python बिल्ड के अनुसार बदलती है, इसलिए exploit.py और डेमो इसे रनटाइम पर कंप्यूट करते हैं।
पैच allowlist के ऊपर एक खतरनाक-एट्रिब्यूट ब्लॉकलिस्ट जोड़ता है, इसलिए डंडर वॉक eval से पहले ही अस्वीकार कर दी जाती है:
Access to attribute '__subclasses__' is not allowed in filter expressions.
This attribute could be used to escape the filter sandbox.
एजेंट-सुरक्षा थीसिस का सबसे स्पष्ट संभव कथन: "डेटा" और "इंस्ट्रक्शंस" के बीच कोई सीमा नहीं है। एक फ़िल्टर जो मॉडल यूज़र की रनबुक लुकअप से लिखता है, os.system बन जाता है। सैंडबॉक्स मौजूद था — एक allowlist और खाली __builtins__ — और फिर भी वह एक एट्रिब्यूट-ट्रैवर्सल + सब्स्क्रिप्ट-कॉल बायपास के आगे गिर गया। पोस्ट में बनाने लायक मिटिगेशन पदानुक्रम: मॉडल आउटपुट को eval बिल्कुल न करें; यदि करना ही हो, तो एक क्लोज़्ड ग्रामर पर रेस्ट्रिक्ट करें, न कि ओपन एट्रिब्यूट एक्सेस वाली नोड allowlist पर; और वर्कर को आइसोलेट करें (seccomp / नो नेटवर्क / अनप्रिविलेज्ड) ताकि कोड एक्ज़ीक्यूशन गेम ओवर न हो।
| फ़ाइल | उद्देश्य |
|---|
setup.sh | दो आइसोलेटेड venvs बनाता है (कमजोर + पैच्ड) |
run.sh | दोनों venvs पर हेडलेस PoC |
exploit.py | एंड-टू-एंड: असली कलेक्शन + सर्च फ़िल्टर -> RCE |
find_sink.py | इंस्टॉल्ड पैकेज में eval/compile सिंक का पता लगाता है |
probe.py | बायपास की न्यूनतम वैलिडेटर-ओनली पुष्टि |
demo-ui/app.py | FastAPI + SK + Groq एजेंट; कमजोर search_runbooks टूल |
demo-ui/run_filter.py | असली फ़िल्टर eval को साफ़ सबप्रोसेस में चलाता है (सर्वर fork() पॉइज़न है) |
demo-ui/index.html | चैट UI; RCE पर TextEdit में रैन्सम नोट खोलता है |