
PHP2Shell(CVE-2026-109618、CVE-2026-109619)発生後のWordPressサイト向け読み取り専用トリアージスクリプト。侵害の痕跡をチェックし、サイトごとにレポートを書き出します。
PHP2Shell(CVE-2026-109618 および CVE-2026-109619)——2026年10月21日に公表された PHP エンジンにおける一対の重大なリモートコード実行脆弱性——の後に、WordPress サイトを対象とした読み取り専用のトリアージスクリプトです。
PHP にパッチを適用すれば扉は閉じられますが、Amazon のハニーポットは 10月17日 に悪用の試行を記録しています。これは公開開示の4日前であり、脆弱性がまだエンバーゴ下にあった時期です。これは誰かが公開した中で最も早い活動であり、攻撃が始まった日ではありません。したがって、これを下限として扱ってください。その期間中に未パッチの PHP で到達可能だったサイトは、すでにバックドアを仕込まれている可能性があり、PHP を更新してもそれは除去されません。このスクリプトは、1つまたは複数の WordPress サイトを対象に、コード実行権限を持つ攻撃者が通常残していく痕跡を検査し、各サイトについて Markdown レポートを出力します。
3つ目の脆弱性 CVE-2026-115560 は10月23日に公表されました。これにはまだ修正版ビルドがありません。10月のポイントリリース(8.2.35、8.3.36、8.4.27、8.5.12)ではカバーされておらず、修正は11月の次回定期リリースに間に合う予定です。それまでの間、php.net は全員に対し php.ini または PHP-FPM プールで session.upload_progress.enabled=0 を設定するよう求めています。このスクリプトは、それが実施されているかを検査します。
このスクリプトは決して何も変更・削除しません。一覧化し、検証し、報告するだけです。検出結果をどう扱うかはあなた次第です。
| 領域 | 検査内容 |
|---|---|
| PHP | インストール済み CLI バージョンと修正版リリース(8.2.35、8.3.36、8.4.27、8.5.12)との比較。サポート終了ブランチ(php.net からリリースが出ていない)を検出します。詳細は 古い PHP ブランチ を参照。 |
| アップロード進捗 | CVE-2026-115560 の緩和策である session.upload_progress.enabled=0 が、重要な箇所で設定されているか:PHP-FPM 自身のビュー(php-fpm -i)、読み取り可能なすべての php.ini、conf.d、FPM プール、Apache 設定。サイトレベルの .user.ini と .htaccess は報告されますが、評価対象には含まれません。php.net によれば、この設定にはこれらは機能しないためです。 |
| コア整合性 | wordpress.org に対する wp core verify-checksums |
| プラグイン整合性 | wp plugin verify-checksums --all、および検証できないプレミアム/カスタムプラグインの一覧化 |
| アップロード | wp-content/uploads 配下の PHP 実行可能ファイル(「Silence is golden」スタブは無視) |
| 最近の変更 | 期間開始(デフォルト 2026-10-17)以降に変更された PHP および設定ファイル。WordPress 翻訳パック(wp-content/languages/**/*.l10n.php)は、厳格な形状テストに合格した場合に別途カウントされ、不合格のものはフラグが立てられます。 |
| プリペンドフック | .user.ini、.htaccess、php.ini 内の auto_prepend_file / auto_append_file |
| Must-use プラグイン | wp-content/mu-plugins の内容(毎リクエスト実行され、通常のプラグイン一覧には表示されない) |
| コードパターン | wp-content 内の古典的なウェブシェルシグネチャ(eval(base64_decode(...))、$_POST[...]() など) |
| ユーザー | すべての管理者、および期間内に登録されたアカウント |
| データベース | siteurl/home、インジェクトされたスクリプトや難読化コードを含むオプションおよび投稿 |
| Cron | WP-Cron のスケジュール済みイベント |
| ログ | アクセスログ:期間中の日別 5xx レスポンス数と、その前最大7日間のベースラインとの比較、および期間中に 5xx を返した URL。エラーログおよび PHP-FPM ログ:ワーカーのクラッシュ(セグメンテーション違反/バスエラー)。 |
| サーバー | サイトユーザーの crontab および SSH authorized_keys |
| Docker | php バイナリを持つすべての実行中コンテナ:コンテナ内の PHP バージョン、およびコンテナがディスク上のイメージより古いビルドを依然として実行しているか(プル済みだが再作成されていない) |
WP-CLI は --skip-plugins --skip-themes 付きで実行されるため、バックドア入りのプラグインやテーマが検査中に実行されることはありません。
検査対象の各マシン上で:
bash 3.2+、find、grep、sort -V(Linux ホストでは標準。市販の macOS ワークステーションからも動作します)phpリモートサイトの場合:スクリプトを実行するマシンからの SSH キーアクセス。スクリプトは SSH 経由で自身を実行するため、リモートホストには上記以外何もインストールする必要はありません。
curl -O https://raw.githubusercontent.com/brentmartinmiller/php2shell-check/main/php2shell-check.sh
chmod +x php2shell-check.sh
# One site
./php2shell-check.sh /var/www/example.com
less php2shell-reports/*.md
# Every site on the box
printf '%s\n' /var/www/*/public_html > sites.txt
./php2shell-check.sh sites.txt
cat php2shell-reports/SUMMARY.md
可能な場合は、サイトファイルを所有するユーザーとして実行してください。root でも動作します(WP-CLI には --allow-root が付与されます)が、その場合 crontab と SSH キーのチェックは Web ユーザーではなく root のものを検査します。
git clone https://github.com/brentmartinmiller/php2shell-check.git
cd php2shell-check
cp sites.txt.example sites.txt # edit: one site per line
./php2shell-check.sh sites.txt
各 user@host:path エントリについて、スクリプトは SSH で接続し、そこで自身を実行し、レポートをあなたのマシン上の php2shell-reports/ に持ち帰ります。リモートホストには何もインストールされません。SSH は BatchMode=yes で実行されるため、パスワードプロンプトなしで動作する鍵ベースのアクセスが必要です。
ほとんどの共有ホスト(Hostinger、SiteGround、Bluehost など)は SSH を提供していますが、wp がパス上にないことがよくあります。which wp で確認してください。見つからない場合は、WP-CLI を一度ホームディレクトリにダウンロードし、スクリプトがそれを指すように設定します:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
WP="php $HOME/wp-cli.phar" ./php2shell-check.sh domains/example.com/public_html
WP-CLI がなくてもファイルシステムのチェックは実行され、レポートにはスキップされた内容が記録されます。いずれの場合も、アカウントレベルのチェックでは確認できないため、ホストに直接パッチ適用済み PHP のロールアウト時期を尋ねてください。
sites.txt は 1 行につき 1 サイトで、ローカルパスまたは user@host:path のいずれかです。相対リモートパスは SSH ユーザーのホームディレクトリからの相対パスとなります。# で始まる行は無視されます。sites.txt.example を参照してください。このファイルは .gitignore に含まれているため、クライアントのホスト名がコミットに含まれることはありません。
非標準の SSH ポート、鍵、ジャンプホストは ~/.ssh/config に記述します:
Host client-a
HostName 203.0.113.10
Port 2222
User deploy
IdentityFile ~/.ssh/client-a
次に、そのサイトを client-a:/var/www/html としてリストします。
Docker ホスト上で php -v を実行してもコンテナについては何も分かりませんし、固定イメージを pull しても、そのイメージからコンテナを再作成するまで何もパッチは適用されません。あるチームのサイトはまさにその理由で、pull 後 3 日間 8.3.35 のままでした。
スクリプトが docker ps の動作するホスト上で実行されると、レポートには Docker コンテナ セクションが追加され、php バイナリを持つすべての実行中コンテナ、その中の PHP バージョン、およびコンテナが古くなっているかどうか(新しいイメージビルドがディスク上にあるのにコンテナが再作成されていない)が一覧表示されます。docker compose up -d で再作成するか、コンテナを停止・削除して再度実行してください。
コンテナ内で完全なチェックを実行するには、それをターゲットとしてリストします:
docker:wordpress-php-1:/var/www/html
スクリプトは docker exec にパイプで渡されてそこで実行されるため、コンテナ内には何もインストールされません。コンテナ内に bash が必要です。公式の php:* および wordpress:* の Debian ベースイメージには含まれていますが、Alpine ベースのもの(-alpine)には含まれていないため、先に docker exec NAME apk add bash を実行するか、docker exec NAME php -v で妥協してください。データベースは通常別のコンテナにあるため、PHP コンテナから到達できない限り、ユーザー、オプション、cron のチェックはスキップされます。
これらを環境変数として設定します:
| 変数 | デフォルト | 意味 |
|---|---|---|
SINCE | 2026-10-17 | 「最近変更された」および「最近登録された」チェックの対象期間の開始点。これは最初に確認された悪用の試行であり、最初の攻撃ではありません。重要なサイトではより早い日時に設定してください |
WP | wp | ターゲット上の WP-CLI コマンド。共有ホストでは例えば WP="php $HOME/wp-cli.phar" |
LOGS | 自動 | スキャン対象となるアクセスログ、エラーログ、PHP-FPM ログのスペース区切りのパスまたは glob。例: LOGS="/var/log/nginx/*.log* /var/log/php8.3-fpm.log*"。デフォルトではスクリプトはサイトの隣(../logs)、~/logs、~/access-logs、~/domains/*/logs、そして /var/log 以下を探します。ローテーションされた .gz ファイルも読み込まれます |
OUT | ./php2shell-reports | レポートの書き込み先 |
WP="php $HOME/wp-cli.phar" SINCE=2026-10-15 ./php2shell-check.sh sites.txt
php2shell-reports/SUMMARY.md は、サイトごとに1行を持つテーブルです:
| サイト | CLI PHP | アップロード進捗 | ⚠️ 問題 | 🔎 レビュー | レポート |
|---|---|---|---|---|---|
| client-a:/var/www/html | 8.3.36 | off | 0 | 2 | client-a_var_www_html.md |
アップロード進捗 は、サーバーレベルで session.upload_progress.enabled=0 が確認された場合は off、確認されなかった場合は on、PHP設定が一切読み取れなかった場合は ? となります。
各サイトのレポートは、チェックごとに1つのセクションを持つMarkdownファイルで、以下を使用します:
⚠️ の項目から始め、次に 🔎 を確認してください。
試行は侵害ではありません。 インターネット上のほぼすべてのPHPサイトは現在スキャンされています。アクセスログに記録された悪用の試行は、サイトが標的にされたことを示すものであり、侵害されたことを示すものではありません。それを示すのはファイルとデータベースのチェックです。
5xxのバーストは、単一のログシグナルとして最良のものです。 これらはメモリ破壊のバグであるため、失敗または部分的な試行はPHPワーカーをクラッシュさせる傾向があり、訪問者はアクセスしたURLで500または502を受け取ります。プロキシログを全期間にわたって調査し、5xxがゼロだったチームは、それが見た中で最も安心できるものだったと述べました。トラフィックの多いサイトでは、期間前のベースラインが必要です。そうしないと通常のエラーがシグナルを埋め尽くしてしまいます。そのため、レポートには期間と並べて SINCE の最大7日前までが表示されます。同じURLへの安定した少量の発生は壊れたプラグインです。ランダムなURLに広がるバーストは、PHP-FPMログのセグメンテーション違反の行とともに、注意深く調べる価値があります。
チェックサムはタイムスタンプに勝ります。 攻撃者はファイルの変更時刻を任意の値に設定できます。クリーンな「最近変更された」リストは安心材料ですが、クリーンな verify-checksums の結果は証拠です。これはここでは通常以上に重要です:10月17日にこれを悪用していた者は、誰も調査する前に4日間の静かな期間があり、ファイルの日付を遡らせてログを削るには十分な時間でした。
一部のヒットは正常です。