
CVE-2026-54350 के लिए PoC — Budibase में बिना प्रमाणीकरण के NoSQL ऑपरेटर इंजेक्शन (CVSS 10.0)। एक PUBLIC क्वेरी के माध्यम से किसी भी दस्तावेज़ संग्रह को पढ़ें/बड़े पैमाने पर लिखें।
Budibase में अप्रमाणित JSON/NoSQL ऑपरेटर इंजेक्शन। कोई भी अज्ञात आगंतुक, जो किसी प्रकाशित Budibase ऐप पर जाता है, जो एक दस्तावेज़ डेटास्रोत (MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, या JSON बॉडी के साथ REST) द्वारा समर्थित सार्वजनिक क्वेरी को उजागर करता है, एक ही अप्रमाणित HTTP अनुरोध के साथ समर्थित संग्रह के हर दस्तावेज़ को पढ़ सकता है और, यदि कोई सार्वजनिक लेखन क्वेरी मौजूद है, तो हर दस्तावेज़ को संशोधित कर सकता है।
| CVE | CVE-2026-54350 |
| Advisory | GHSA-8qv3-p479-cj62 |
| CVSS 3.1 | 10.0 — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N |
| CWE | CWE-943 (डेटा क्वेरी में विशेष तत्वों का अनुचित न्यूट्रलाइज़ेशन), CWE-89 |
| Auth | कोई नहीं (अप्रमाणित) |
| Affected | Budibase <= 3.39.0 (विक्रेता सलाहकार सीमा) |
| Fixed | 3.39.12 प्रति NVD; इंजेक्ट किए गए उद्धरण 3.39.1 से पहले ही निष्प्रभावी हो चुके हैं, और स्पष्ट फिक्स (processJsonStringSync) 3.39.9 में आता है |
| Verdict | पुष्टि की गई — 3.39.0 पर एंड-टू-एंड पुनरुत्पादित किया गया |
जब कोई क्वेरी निष्पादित करते हैं, Budibase उपयोगकर्ता मापदंडों को Handlebars के साथ क्वेरी के कच्चे JSON बॉडी में समृद्ध करता है, फिर परिणाम को JSON.parse करता है:
enrichContext() packages/server/src/sdk/workspace/queries/queries.ts
enrichedQuery.json = processStringSync('{"name":"{{ name }}"}', { name: <user> }, { noEscaping: true })
enrichedQuery.json = JSON.parse(enrichedQuery.json)
प्रभावित बिल्ड में पैरामीटर को JSON-एस्केपिंग के बिना इंटरपोलेट किया जाता है, इसलिए " वाला एक मान इच्छित स्ट्रिंग को बंद कर देता है और पार्स किए गए ऑब्जेक्ट में सहोदर कुंजियाँ इंजेक्ट करता है। एकमात्र इनपुट फ़िल्टर, validateQueryInputs() (api/controllers/query/index.ts), केवल Handlebars मार्करों ({{, }}) को अस्वीकार करता है — यह ", \, } या $ को स्पर्श नहीं करता है।
MongoDB find के लिए, पार्स किया गया ऑब्जेक्ट सीधे collection.find() (integrations/mongodb.ts) को भेजा जाता है। एक डुप्लिकेट name कुंजी जिसका मान एक ऑपरेटर ऑब्जेक्ट है, JSON.parse मर्ज को जीतता है, स्ट्रिंग समानता को {$exists: true} में बदल देता है और पूरे संग्रह को लौटा देता है। वही प्रिमिटिव एक updateMany क्वेरी के खिलाफ फ़िल्टर को हर दस्तावेज़ तक विस्तृत करता है।
एक्सेस कंट्रोल को बायपास कर दिया जाता है क्योंकि authorized() (middleware/authorized.ts:141) return next() के साथ शॉर्ट-सर्किट करता है जब क्वेरी की संसाधन भूमिका PUBLIC होती है, जिससे सत्र प्रमाणीकरण और CSRF दोनों को छोड़ दिया जाता है। क्वेरी तक पहुँचने के लिए आवश्यक x-budibase-app-id हेडर सार्वजनिक है — यह प्रत्येक प्रकाशित ऐप URL का हिस्सा है।
पूर्ण कोड-स्तरीय वॉकथ्रू और संस्करण-सीमा जांच के लिए ANALYSIS.md देखें।
क्वेरी बॉडी टेम्पलेट (बिल्डर में कॉन्फ़िगर किया गया):
{"name":"{{ name }}"}
name पैरामीटर के लिए इंजेक्ट किया गया मान:
zzz","name":{"$exists":true},"$comment":"cve-2026-54350
इंटरपोलेशन और JSON.parse के बाद (डुप्लिकेट-कुंजी मर्ज, अंतिम जीतता है):
{ "name": { "$exists": true }, "$comment": "cve-2026-54350" }
$comment एक निष्क्रिय MongoDB मेटा-ऑपरेटर है जो टेम्पलेट के अनुगामी "} को अवशोषित करता है ताकि पूरी बॉडी मान्य JSON बनी रहे।
exploit.py — केवल Python 3 मानक लाइब्रेरी, कोई निर्भरता नहीं।
# Dump the whole collection through a PUBLIC read (find) query
python3 exploit.py --url http://target --app-id app_<published> \
--query-id query_<...> --mode read
# Modify every document through a PUBLIC updateMany query
python3 exploit.py --url http://target --app-id app_<published> \
--query-id query_<...> --mode write
--app-id और --query-id प्रकाशित ऐप के स्वयं के API ट्रैफ़िक में देखे गए सार्वजनिक मान हैं। --field उस JSON कुंजी को ओवरराइड करता है जिससे पैरामीटर बाध्य है (डिफ़ॉल्ट --param है)।
Docker आवश्यक है। यह लैब --network host (कोई ब्रिज नेटवर्क नहीं माना गया) और mongo:4.4 (होस्ट में AVX नहीं है; MongoDB 5.0+ के लिए इसकी आवश्यकता है) का उपयोग करता है।
bash lab/setup.sh
# prints PROD_APP_ID / READ_QUERY / UPDATE_QUERY, then:
python3 exploit.py --url http://127.0.0.1 --app-id <PROD_APP_ID> \
--query-id <READ_QUERY> --mode read
lab/provision.py केवल वैध बिल्डर API चलाता है — यह किसी भी डिफ़ॉल्ट को कमजोर नहीं करता है। क्वेरी की एक्सेस भूमिका को PUBLIC चिह्नित करना एक प्रथम श्रेणी की बिल्डर सुविधा है (प्रकाशित ऐप इसी तंत्र के माध्यम से डेटा उजागर करते हैं)। पूर्ण कैप्चर किया गया रन EVIDENCE.txt में है।
NVD प्रभावित सीमा को < 3.39.12 के रूप में सूचीबद्ध करता है। अनुभवजन्य रूप से बेयर-कोट वेक्टर केवल <= 3.39.0 पर शोषणीय है, जो वास्तव में वह सीमा है जो विक्रेता का GHSA सलाहकार बताता है। 3.39.1 से टेम्प्लेटिंग लेयर पहले से ही इंजेक्ट किए गए उद्धरणों को एस्केप करता है (सत्यापित: 3.39.8 के खिलाफ वही अनुरोध निष्प्रभावी हो जाता है), और 3.39.9 स्पष्ट processJsonStringSync फिक्स प्रस्तुत करता है। इसलिए यह PoC 3.39.0 को लक्षित करता है, जो विक्रेता की प्रभावित सीमा के अंदर नवीनतम रिलीज़ है।
>= 3.39.12, या न्यूनतम >= 3.39.9 जहां processJsonStringSync इंटरपोलेटेड मापदंडों को JSON-एस्केप करता है) में अपग्रेड करें।PUBLIC है; जहाँ संभव हो उन्हें प्रमाणित भूमिकाओं तक सीमित करें।अप्रमाणित सत्रों से POST /api/v2/queries/<id> अनुरोधों की तलाश करें, जिनके parameters मानों में एक " होता है जिसके बाद एक $-उपसर्गित MongoDB ऑपरेटर ($exists, $ne, $gt, $where, $regex, ...) या एक डुप्लिकेट JSON कुंजी होती है। MongoDB पर, प्रोफाइलिंग सक्षम करें और सतर्क करें जब कोई क्वेरी पैरामीटर जो सामान्यतः एक स्ट्रिंग है, एक ऑब्जेक्ट के रूप में आता है।
शोध और PoC द्वारा Caio Fabrício (@BiiTts).