CVE-2026-63030 · CVE-2026-60137
WordPress REST API Batch Route Confusion & Unauthenticated RCE Chain ("WP2Shell")
CVSS 9.8 Critical
Patched
Exploit Active
CWE CWE-436
GHSA GHSA-ff9f-jf42-662q
TL;DR
WordPress 6.9で導入されたREST APIバッチエンドポイント(/wp-json/batch/v1)におけるルート混同(route confusion)の脆弱性。検証ループのインデックスズレにより、あるリクエストが別のリクエストのハンドラで実行される。CVE-2026-60137(SQLi)と連鎖することで、認証なしのRemote Code Executionに至る。2026年7月17日に公開され、同日中に実攻撃が観測され始めた。影響バージョン(6.9.0〜7.0.1)は即時のアップデートが必要。
タイムライン
2025-11 (WP 5.6)
REST APIバッチフレームワーク導入。/wp-json/batch/v1 エンドポイントがデフォルトで有効化。
2025-12 (WP 6.9)
脆弱なコード導入。バッチ処理の制御フローにルート混同の脆弱性が混入。
2026-07-17
脆弱性公開。Adam Kues (Searchlight Cyber) がHackerOne経由で報告。WordPressが6.9.5、7.0.2をリリース。強制自動アップデート有効化。Searchlightがwp2shell.comで公開チェッカーを公開。
2026-07-17 17:03 UTC
WAFルールデプロイ。Cloudflareが全プラン向けに2つの新WAFルールを自動デプロイ。
2026-07-17 ~19:00 ET
実攻撃の観測開始。PatchStackが攻撃を観測報告。
2026-07-17
Wordfence対応。Premiumユーザー向けにファイアウォールルールを即時配信。
根本原因
wp-includes/rest-api/class-wp-rest-server.php の serve_batch_request_v1() メソッド内、バッチ検証ループの制御フローに脆弱性があった。不正なpathを含むサブスクリプトがWP_Errorとして処理される際、$matches配列への追加が省略され、$matches配列と$validation配列のインデックスがズレた。
インデックスズレのメカニズム
wp-includes/rest-api/class-wp-rest-server.php — 脆弱な検証ループ
foreach ( $requests as $i => $single_request ) {
if ( is_wp_error( $single_request ) ) {
$has_error = true;
- $validation[] = $single_request;
- // ⚠️ $matches への追加が欠落 → インデックスズレ
continue;
}
// ... ルートマッチング ...
$matches[] = $handler;
$validation[] = $validation_result;
}
実行ループでは $matches[$i] を参照するため、インデックスが1つズレた状態で別ハンドラが実行される:
インデックスズレの例
requests: [error, request A, request B]
validation: [error, result A, result B]
- $matches: [match A, match B] ← index 0欠落
+ (修正後) $matches: [error, match A, match B] ← ダミーで維持
i=1: request A → $matches[1] = match B (本来はrequest B用!)
i=2: request B → $matches[2] = out of bounds
修正後の実装
修正コミット fa72c12 では、$matchesにもダミーエントリーを追加してインデックスの整合性を維持。さらに、rest_api_loaded() と WP_REST_Server::serve_request() に is_dispatching() チェックを追加し、バッチ処理中に新たなRESTサイクルが開始されることを防ぐ。
wp-includes/rest-api/class-wp-rest-server.php — 修正後のコード
if ( is_wp_error( $single_request ) ) {
$has_error = true;
+ $matches[] = $single_request;
$validation[] = $single_request;
continue;
}
wp-includes/rest-api.php — 追加の防御層
// Short-circuit before define()/die() if a REST dispatch is already in flight.
+ if ( isset( $GLOBALS['wp_rest_server'] )
+ && $GLOBALS['wp_rest_server'] instanceof WP_REST_Server
+ && $GLOBALS['wp_rest_server']->is_dispatching()
+ ) {
+ return;
+ }
攻撃フロー(47Cid/wp2shell-lab の分析に基づく)
1
攻撃者が POST /wp-json/batch/v1 に3つのリクエストを送信(うち1つは不正path)
↓
2
Loop 1 (validate): 不正path → WP_Error(matchesに追加されずインデックスズレ)
↓
3
Loop 2 (dispatch): postsリクエストがbatch_handlerで処理 → ネストバッチとして再帰
↓
4
ネストバッチ: categoriesリクエストがposts_handlerで処理 → author_excludeがサニタイズされずWP_Queryに到達
↓
5
CVE-2026-60137: author__not_in経由でSQLインジェクション実行
↓
6
MySQLから管理者パスワードハッシュ窃取 → クラック → ログイン → プラグインアップロード → RCE
⚠️ 重要な技術的ポイント: バッチエンドポイントはWordPress 5.6(2020年11月)から存在するが、ルート混同は6.9(2025年12月)で導入された。Cloudflareの分析によれば、永続オブジェクトキャッシュが無効な環境でRCEパスに到達可能(デフォルト設定)。respond_to_request()は依然としてpermission_callbackを呼び出すため、この脆弱性は「認証バイパス」ではなく「ルート混同」— 公開読み取り可能なエンドポイントを経由してSQLiに到達する。
影響バージョン
| バージョンブランチ |
影響範囲 |
修正バージョン |
影響 |
| WordPress 6.9.x |
6.9.0 – 6.9.4 |
6.9.5 |
RCE chain |
| WordPress 7.0.x |
7.0.0 – 7.0.1 |
7.0.2 |
RCE chain |
| WordPress 7.1 beta |
7.1 beta1 |
7.1 beta2 |
RCE chain |
| WordPress < 6.9 |
— |
— |
影響なし(バッチルート混同は6.9で導入) |
検出方法
ブラックボックステスト
バッチエンドポイント到達性確認
# テストA: 標準RESTパス
curl -sX POST "https://target.com/wp-json/batch/v1" \
-H "Content-Type: application/json" \
-d '{"requests":[]}'
# 期待応答: {"code":"rest_missing_callback_param","message":...}
# テストB: クエリパラメータパス
curl -sX POST "https://target.com/?rest_route=/batch/v1" \
-H "Content-Type: application/json" \
-d '{"requests":[]}'
ホワイトボックステスト
修正確認
# is_dispatching() チェックの存在確認
grep -n "is_dispatching" wp-includes/rest-api.php
grep -n "is_dispatching" wp-includes/rest-api/class-wp-rest-server.php
# $matches[] = $single_request の存在確認
grep -n 'matches\[\].*single_request' wp-includes/rest-api/class-wp-rest-server.php
# 結果が存在すれば修正済み
WAFルール (Cloudflare)
| ルール | Managed Ruleset ID | Free Ruleset ID | アクション |
| WordPress - Unauthenticated RCE | 7dfb2bd4... | ebd3f2df... | Block |
対策
WordPressを修正バージョンにアップデート(6.9.5 / 7.0.2)
Critical
Cloudflare WAFルールの有効化を確認(無料プラン含む)
High
Wordfenceファイアウォールルールの適用を確認(Premiumは即時、Freeは8/16)
High
パッチ適用不可の場合、/wp-json/batch/v1 と ?rest_route=/batch/v1 をWAFでブロック
Critical
データベースアクセスログの監視 — 不審なSQLクエリパターンをアラート設定
Medium