WordPress wp2shell 対応|更新確認とアクセスログ点検の手順

  • URLをコピーしました!
目次

はじめに

2026 年 7 月 17 日(金)、WordPress のセキュリティリリース 6.8.6、6.9.5、7.0.2 が公開されました。未認証のリモートコード実行(RCE)につながる攻撃チェーン「wp2shell」への対応で、深刻度の高さから WordPress.org は自動更新システム経由の強制更新を有効化しています。日本では直後に三連休(7/18〜7/20)に入ったため、更新の適用状況を確認できないまま週明けを迎えた運用者も少なくないと思われます。

連休中に状況は動きました。攻撃チェーンの詳細と概念実証(PoC)コードが公開され、複数のセキュリティ企業が実際の悪用の兆候を報告しています。つまり現時点の関心事は「更新すべきかどうか」ではなく、管理下のすべてのサイトに更新が実際に届いたか、そして更新が届く前に侵害を受けていないかへ移っています。

この記事でわかること
  • wp2shell を構成する 2 件の CVE の役割分担と、バージョン別の影響範囲
  • 強制自動更新が実際に適用されたかを確認する手順
  • アクセスログから初期侵入の痕跡を点検する際の着眼点
  • WAF による暫定遮断をいつ解除するかの判断基準
  • CVSS スコアだけで優先度を決めた場合に生じるずれ

結論から述べると、当面の焦点は 2 点です。1 つは、強制更新が届かない構成が存在しうるため、バージョンを実測で確認すること。もう 1 つは、更新完了後も更新前の期間について、/batch/v1へのリクエストがアクセスログに残っていないかを確認することです。更新はあくまで脆弱なコードの解消であり、更新前に侵入を許していた場合の痕跡までは消してくれません。

参考: WordPress 7.0.2 Release
“it is recommended that you update your sites immediately”
(サイトを直ちに更新することが推奨されます)
https://wordpress.org/news/2026/07/wordpress-7-0-2-release/

wp2shell の要点整理(更新対象と影響範囲)

wp2shell は単一の脆弱性ではなく、性質の異なる 2 件の欠陥を連鎖させた攻撃手法の呼称です。この構造を押さえておくと、後述するバージョン別の影響範囲やスコアのばらつきが理解しやすくなります。

2 件の CVE の役割分担

CVE-2026-60137 は、WP_Queryauthor__not_inパラメーターにおける SQL インジェクションです。この処理は WordPress 6.8 系から存在しますが、6.8 系の標準構成では外部から当該パラメーターへ値を渡す経路がなく、悪用にはプラグインやテーマ側が未信頼の値を渡す実装であることが前提となります。

CVE-2026-63030 は、REST API のバッチエンドポイント /batch/v1におけるルート混同です。バッチ内のサブリクエストと、それを処理するハンドラーの対応がずれることで、本来のバリデーションや権限チェックを迂回できます。WordPress 6.9 で作り込まれたもので、この欠陥が「認証を必要とする SQL インジェクション」を「未認証で到達可能な SQL インジェクション」へ変えた点が要点です。

単体では限定的な 2 件が、連鎖することで未認証 RCE に至るという構造のため、対応の優先度は「どちらの CVE の影響を受けるか」ではなく「両方の影響を受けるか」で判断することになります。

参考: GHSA-ff9f-jf42-662q(WordPress Security Advisory)
“WordPress versions 6.9 and higher are vulnerable to a REST API batch-route confusion weakness”
(WordPress 6.9 以降は REST API のバッチルート混同の脆弱性の影響を受けます)
https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-ff9f-jf42-662q

バージョン別の影響範囲と更新先

GitHub Security Advisory およびリリースノートに基づき、バージョンごとの影響を整理します。

WordPress のバージョンCVE-2026-60137CVE-2026-63030wp2shell による未認証 RCE更新先
6.8 未満対象外対象外成立しない最新の安定版
6.8.0〜6.8.5影響あり対象外成立しない6.8.6
6.9.0〜6.9.4影響あり影響あり成立する6.9.5
7.0.0〜7.0.1影響あり影響あり成立する7.0.2
7.1 Beta 1 まで影響あり影響あり成立する7.1 Beta 2

複数サイトを管理している場合、対応の優先順位は 6.9 系・7.0 系が先で、6.8 系がその次になります。ただし 6.8 系を「対応不要」と読み替えないよう注意が必要です。6.8 系でも SQL インジェクション自体は残るため、プラグインやテーマの実装次第で悪用の余地が生じます。

2026 年 7 月 21 日時点の状況

公開直後は攻撃の再現性が不明でしたが、連休中に前提が変わりました。現時点で確認できる状況は次のとおりです。

IoC が公表されていない状況は、「痕跡を探す必要がない」ことを意味しません。むしろ、公開された IoC に頼れない以上、自サイトのアクセスログを起点に手元で確認する必要があります。その具体的な手順を、次章以降で扱います。

なお、攻撃の仕組みそのものについては、GMO Flatt Security による解説記事(https://blog.flatt.tech/entry/wp2shell_wordpress)が修正差分まで踏み込んで整理しており、本記事では仕組みの再解説は行いません。本記事は、更新後の運用者が実施する確認作業に絞って扱います。

強制自動更新が「実際に届いたか」の確認手順

WordPress.org は今回、対象バージョンに対して自動更新システム経由の強制更新を有効化しました。ただし、この仕組みが到達しない構成も存在するため、通知や前提ではなく実測でバージョンを確認することを推奨します。ここでは、管理画面・WP-CLI・外部からの 3 経路と、自動更新を抑止する設定の洗い出し方を整理します。

管理画面・WP-CLI・HTTP レスポンスからのバージョン確認

もっとも確実なのは、サーバー内部で実際のファイルを読む方法です。WP-CLI が利用できる環境であれば、次のコマンドでバージョンを取得できます。

# 現在のバージョンを表示
wp core version

# データベースリビジョン等を含めて表示
wp core version --extra

# 更新可能なバージョンがあるかを Version Check API で確認
wp core check-update

wp core check-updateが「Success: WordPress is at the latest version.」を返せば、少なくとも当該ブランチの最新版が適用されています。複数サイトを 1 台のサーバーに収容している場合は、--pathでドキュメントルートを指定してループ処理すると一覧化できます。

for d in /var/www/*/public_html; do
  printf '%s\t' "$d"
  wp core version --path="$d" --skip-plugins --skip-themes
done

更新の適用に加えて、コアファイルが改変されていないかも同時に確認しておくと、後続のログ点検の判断材料になります。WP-CLI には、WordPress.org が公開するチェックサムと現在のファイルを照合するコマンドが用意されています。

wp core verify-checksums
wp core verify-checksums --locale=ja

参考: wp core verify-checksums(WP-CLI Command)
“Verifies WordPress files against WordPress.org’s checksums”
(WordPress のファイルを WordPress.org のチェックサムと照合します)
https://developer.wordpress.org/cli/commands/core/verify-checksums/

日本語環境では、--locale=jaを指定しないと差分として報告される場合があります。また、このコマンドはコアファイルのみを対象とするため、wp-content配下に設置されたファイルの検知には使えない点に注意してください。「チェックサムが一致した」ことはコアの完全性を示すのみで、侵害がなかったことの証明にはなりません

管理画面から確認する場合は、ダッシュボードの「更新」画面で現在のバージョンと更新の有無を確認します。

外部から確認する方法としては、HTML の generatorメタタグ、readme.html、REST API のルートエンドポイント(/wp-json/)などがありますが、いずれもバージョン情報を隠蔽するプラグインやテーマの設定で出力を抑止できます。外部からバージョンが見えないことは、更新済みであることを意味しません。管理下のサイトについては、外部確認で済ませず内部で実測することを推奨します。

自動更新を無効化していたサイトで確認すべき点

自動更新は、wp-config.phpの定数、mu-plugins のフィルター、ファイル書き込み権限、バージョン管理システムの検出など、複数の要因で抑止されます。いずれかに該当するサイトでは、今回の強制更新が届いていない可能性があります。

参考: Upgrading WordPress(WordPress Advanced Administration Handbook)
“To completely disable all types of automatic updates, core or otherwise”
(コアを含むすべての種類の自動更新を完全に無効化するには)
https://developer.wordpress.org/advanced-administration/upgrade/upgrading/

主な抑止要因は次のとおりです。

設定・条件影響範囲確認場所
AUTOMATIC_UPDATER_DISABLEDtrueコアを含むすべての自動更新が無効wp-config.php
WP_AUTO_UPDATE_COREfalseコアの開発版・マイナー・メジャー更新が無効wp-config.php
DISALLOW_FILE_MODStrueファイル変更全般が禁止され、更新も抑止wp-config.php
automatic_updater_disabledフィルターすべての自動更新が無効mu-plugins・プラグイン
allow_minor_auto_core_updatesfalseマイナー更新(セキュリティリリース)が無効mu-plugins・プラグイン
.git.svn等の検出バージョン管理下と判定され自動更新が抑止ドキュメントルートおよび親ディレクトリー

複数サイトを管理している場合、まず設定ファイルを横断的に検索して該当サイトを絞り込む方法が扱いやすくなります。

# wp-config.php 側の定数を確認
grep -rnE "AUTOMATIC_UPDATER_DISABLED|WP_AUTO_UPDATE_CORE|DISALLOW_FILE_MODS" \
  /var/www/*/public_html/wp-config.php

# mu-plugins 側のフィルターを確認
grep -rnE "automatic_updater_disabled|allow_minor_auto_core_updates|auto_update_core" \
  /var/www/*/public_html/wp-content/mu-plugins/

該当したサイトは、強制更新の対象外となっている前提で扱い、バージョンを個別に実測することを推奨します。更新が適用されていない場合は、wp core updateによる手動更新か、管理画面からの更新で対応します。

なお、レンタルサーバーやマネージドホスティングでは、WordPress 本体の設定とは別に、コントロールパネル側で自動更新の可否を制御している場合があります。ホスティング側で無効化されていると、wp-config.phpに何も定義されていなくても更新が届きません。自社管理外の環境については、提供元の告知と管理画面の設定をあわせて確認することをおすすめします。

アクセスログで初期侵入痕を点検する

更新が完了しても、更新前の期間に攻撃を受けていた可能性は残ります。ここでは、Web サーバーのアクセスログから初期侵入の痕跡を探す際の着眼点を整理します。なお、初期侵入以降の悪用痕については、攻撃手順の指南につながるため本記事では扱いません。

点検を始める前に、まずログの保全を行うことを推奨します。レンタルサーバーでは、アクセスログの保持期間が数日から数週間に設定されていることがあり、調査を進める間に対象期間のログが失われる可能性があります。ローテーション設定を確認し、7 月 17 日以降のログを別領域へ退避してから作業に入ると安全です。

対象となる 2 つのリクエスト形式とサブディレクトリー設置時の注意

wp2shell の攻撃チェーンは、REST API のバッチルート /batch/v1へのリクエストを起点とします。このルートには複数の到達経路があり、片方だけを検索すると見落とします。

WordPress の構成ログに現れるパス
パーマリンクが有効/wp-json/batch/v1
rest_routeパラメーターを使用/?rest_route=/batch/v1
サブディレクトリーに設置/<設置先>/wp-json/batch/v1

/wp-json/配下だけを検索対象にすると、クエリ文字列経由のリクエストが漏れます。WordPress をサブディレクトリーに設置している場合は、そのプレフィックスも検索対象に加えます。

_method / X-HTTP-Method-Override による POST 隠蔽への対応

バッチルートは POST を受け付けるエンドポイントですが、ログ検索を「メソッドが POST のリクエスト」に限定すると取りこぼす可能性があります。WordPress の REST API は、HTTP メソッドの上書きに対応しているためです。

参考: Global Parameters(WordPress REST API Handbook)
“This can be passed either via a _method parameter or the X-HTTP-Method-Override header”
(これは _method パラメーターまたは X-HTTP-Method-Override ヘッダーのいずれかで渡せます)
https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/

したがって、検索はメソッドを限定せず、パスとクエリ文字列を軸に行うことを推奨します。Nginx や Apache の combined 形式を対象とした例を示します。

# バッチルートへのリクエストを、メソッドを限定せずに抽出
grep -aiE "batch/v1" /var/log/nginx/access.log*

# クエリ文字列経由の到達経路を個別に確認
grep -aiE "rest_route=(/)?batch/v1" /var/log/nginx/access.log*

# メソッド上書きの併用が記録されていないか確認
grep -aiE "_method=|X-HTTP-Method-Override" /var/log/nginx/access.log*

X-HTTP-Method-Overrideはリクエストヘッダーのため、標準の combined 形式には記録されません。ヘッダーをログに含めていない環境では、この経路の有無は判断できない点を前提に扱う必要があります。

ログだけでは成否が判定できない理由(207 Multi-Status)

バッチ API は、複数のサブリクエストの結果をまとめて返す仕様上、バッチ全体の応答に 207 Multi-Status を用い、個々のサブリクエストの結果は JSON 本文に格納します。アクセスログのステータスコードが 207 であっても、内部のサブリクエストが成功したか失敗したかは判別できません

実務上の判断としては、次のように整理すると扱いやすくなります。

  • 該当リクエストが 1 件も記録されていない: 少なくともログの保持期間内に、この経路からの試行はなかったと判断できる
  • 該当リクエストが記録されている: 成否は不明。更新完了時刻との前後関係を確認し、更新前のものであれば追加調査の対象とする
  • レスポンスボディまでログに保持している: JSON 本文から個々のサブリクエストの結果を確認できる場合がある

侵害の可能性が否定できない場合は、コアファイルの整合性確認に加えて、管理者権限を持つユーザーの棚卸しと、想定外のプラグインが追加されていないかの確認が次の手順になります。バックアップからの復旧を選択肢に入れる段階では、復旧要件の考え方を関連記事『ランサムウェア対策の基本と全体像』で整理していますので、あわせて参照してください。

WAF での暫定遮断と解除の判断基準

更新が完了していないサイトが残っている場合、暫定的にバッチエンドポイントへの未認証アクセスを遮断する方法があります。ただし、これは時間を稼ぐための措置であり、脆弱なコードそのものは残ります。

Cloudflare を利用している場合の確認事項

Cloudflare は、日本時間 7 月 18 日未明(2026 年 7 月 17 日 17:03 UTC)に両 CVE に対応するマネージドルールを展開しており、無料プランを含むすべてのプランで、同社の WAF を経由しているトラフィックが対象となります。既定のアクションは Block です。

確認しておきたいのは、ルールが実際に有効になっているかという点です。マネージドルールセットに対して「すべてのルールを Block から Log へ変更する」といったルールセットレベルのオーバーライドを設定している場合、新しいルールも記録のみで通過します。あわせて、Security Events で該当ルールにマッチしたリクエストが発生していないかを確認しておくと、攻撃試行の有無を把握できます。

参考: Cloudflare WAF protects WordPress applications from two high-severity vulnerabilities
“they are not a substitute for patching”
(これらはパッチ適用の代替にはなりません)
https://blog.cloudflare.com/wordpress-vulnerabilities/

遮断ルールの設計上の注意点

自前で遮断ルールを実装する場合、次の点を満たしていないと迂回の余地が残ります。

  • URL デコードと正規化を行った後の値で判定する
  • 受信時の HTTP メソッドで対象を限定しない
  • サブディレクトリーに設置している場合は、そのパスも対象に含める
  • オリジンサーバーへの直接接続を制限し、WAF を迂回できない構成にする
  • 例外を設ける場合は、認証を検証できる利用者か、厳密に管理した送信元のみに限定する

Nginx での実装例を示します。rest_routeパラメーター側は ifディレクティブで判定していますが、returnのみを伴う用法であれば副作用は限定的です。

server {
    # パーマリンク経由の到達経路
    location ~* ^/wp-json/batch/v1 {
        return 403;
    }

    # クエリ文字列経由の到達経路
    if ($arg_rest_route ~* "^/batch/v1") {
        return 403;
    }
}

Apache(mod_rewrite)での実装例は次のとおりです。

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule ^wp-json/batch/v1 - [F,L]
    RewriteCond %{QUERY_STRING} (^|&)rest_route=/batch/v1 [NC]
    RewriteRule ^ - [F,L]
</IfModule>

WAF そのものの検出方式や提供形態の違いについては、関連記事『WAF とは|仕組みと種類、選定のポイント』で整理しています。

更新完了後にルールを残すか外すかの判断

暫定遮断はバッチ API の正規利用を巻き込みます。ヘッドレス構成、モバイルアプリ連携、外部サービスとの API 連携などでバッチエンドポイントを使用している場合、遮断したままでは機能が停止します。解除の判断は、次の順序で行うと整理しやすくなります。

  1. 管理下の全サイトが修正版へ更新済みであることを実測で確認する
  2. バッチ API を利用する連携機能の有無を洗い出す
  3. 連携がある場合は遮断を解除し、ルールを Log 動作へ切り替えて可視性のみ残す
  4. 連携がない場合は、遮断を維持しても実害が少ないため、当面の残置も選択肢とする

遮断ルールを外す判断は、「更新が完了したか」ではなく「更新が完了したことを確認できたか」で行うことを推奨します。前章で扱ったバージョンの実測が、この判断の前提になります。

CVSS スコアだけで優先度を決めた場合の落とし穴

wp2shell は、脆弱性管理の運用そのものを見直す材料にもなります。この件では、深刻度の評価が評価元によって割れており、スコアを機械的に閾値へ当てはめる運用では対応の優先度を見誤る余地があるためです。

連鎖の実効リスクは単体スコアに表れにくい

CVE-2026-63030 は、単体で見れば REST API のルート混同であり、CWE-436(解釈の競合)に分類されています。この分類のとおり、単体ではリクエストの解釈が想定と異なるという問題にとどまり、コード実行そのものを引き起こすわけではありません。

参考: Emerging Threat: WordPress Core Unauthenticated RCE via wp2shell(CyCognito)
“carries a CVSS v3.1 base score of 7.5 (High)”
(CVSS v3.1 の基本値 7.5、深刻度 High が付与されています)
https://www.cycognito.com/blog/emerging-threat-cve-2026-63030-cve-2026-60137-wordpress-core-unauthenticated-rce-via-wp2shell/

一方、SQL インジェクションである CVE-2026-60137 には、より高いスコアが付与されています。ここで生じるのが、「RCE の起点となった側の CVE のほうが、スコアは低い」という逆転です。単体評価では、データベースへ直接到達する SQL インジェクションのほうが影響が大きく算定される一方、未認証化という決定的な役割を果たしたルート混同は、単独では解析上の問題として扱われるためです。

評価元によってスコアが割れている

2026 年 7 月 21 日時点で確認できる評価は次のとおりです。

評価元CVE-2026-63030 の扱い
WordPress(GitHub Security Advisory)Critical
CVE レコードに基づく分析(CyCognito)7.5(High、CWE-436)
NSFOCUS CERT連鎖全体として 9.8

同一の脆弱性に対して High と Critical が併存している状態で、いずれか 1 つだけを参照すると、判断が大きく変わります。

脆弱性管理プロセスへの示唆

この件から導ける実務的な論点を整理します。

スコア閾値のみの運用では取りこぼしが生じる

「CVSS 9.0 以上を即時対応」という運用の場合、7.5 と評価された CVE-2026-63030 は即時対応の対象から外れます。連鎖を前提としたリスクは、単体スコアの閾値では捕捉できません

深刻度ラベルは評価元とセットで扱う

開発元の Advisory、CVE レコード、ベンダー独自評価は、それぞれ算定の前提が異なります。社内基準でどれを一次情報とするかを事前に決めておくと、判断のぶれを抑えられます

スキャナーの検出結果を CVE 単位で読まない

6.8 系は CVE-2026-60137 のみを検出しますが、これは RCE の影響下にあることを意味しません。逆に、CVE-2026-63030 のみを検出する構成では、SQL インジェクション側の未修正を見落とす可能性があります

悪用状況を優先度に組み込む

今回のように、公開から数日で PoC の公開と悪用報告が進む事例では、スコアよりも悪用の観測状況のほうが緊急度の判断材料として機能します

スコアは優先度を決めるための入力の 1 つであり、それ単体で運用判断を確定させるものではありません。連鎖を前提とした攻撃が一般化している以上、単体の数値と実際の攻撃成立条件を分けて評価する運用が現実的です。

まとめ

wp2shell への対応は、更新の適用でひと区切りとなりますが、運用者の作業はそこで終わりません。強制自動更新が届かない構成が存在し、更新前の期間には攻撃の機会が残っていたためです。更新の到達確認と、アクセスログによる初期侵入痕の点検までを一連の作業として扱うことをおすすめします。

  • 更新の適用は通知ではなく実測で確認
  • 自動更新は定数・フィルター・VCS 検出で抑止される
  • コアの整合性確認には wp core verify-checksumsが有効
  • 点検対象は /wp-json/batch/v1rest_route経由の 2 経路
  • 207 応答のため、ログのみでは攻撃の成否は判定できない
  • WAF の暫定遮断は更新確認後に解除を判断
  • 単体の CVSS スコアだけでは連鎖のリスクを捕捉できない

以上、最後までお読みいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

関西を拠点に活動する、現役インフラエンジニア。経験20年超。

大手通信キャリアにて、中〜大規模インフラ(ネットワーク・サーバ・クラウド・セキュリティ)の設計・構築およびプロジェクトマネジメントに従事。現場で直面した技術課題への対処や、最新の脆弱性情報への実務対応を、一次情報として発信しています。

保有資格
CCIE Lifetime Emeritus(取得から20年以上)/ VCAP-DCA / Azure Solutions Architect Expert

▶ 運営者プロフィール(詳細)

目次