
PHP2Shell の露出を探索するためのリポジトリと関連分析
PHP2Shell(CVE-2026-109618 / CVE-2026-109619)向けのDefender XDRエクスポージャーハンティング。
v3.3 · 2026年10月22日
目的: PHP2Shellに露出している可能性がある、PHPを実行中のオンボード済みデバイスを検出する。
スコープ: Defender XDR Advanced Hunting · オンボード済みのWindowsおよびLinuxエンドポイント · デフォルトで30日間の遡及 · アクティブなPHPのみ。対象外: 休眠状態のインストールおよび非管理デバイス。Sentinelでは実行不可。インストール済みソフトウェア(TVM)テーブルはDefender XDRでのみクエリ可能なため。
| ハント | Windows | Linux | 確認内容 |
|---|---|---|---|
| 1 | windows/01-any-php.kql | linux/01-any-php.kql | PHPはあるか? デバイスごとにYes/No。 |
| 2 | windows/02-evidence-breakout.kql | linux/02-evidence-breakout.kql | バージョン付きのエビデンス内訳。 |
| 3 | windows/03-safety-net.kql | linux/03-safety-net.kql | セーフティネット: Webポートでサービス提供中のデバイス、リッスン中のプロセス、PHPの状態。 |
すべてのイベントチェックは、各ハントの先頭にある単一の遡及ウィンドウ let Lookback = 30d; を使用する。IPアドレスは、過去7日間のアダプターごとの最新レポートから取得する。インストール済みソフトウェアは現在のTVMスナップショットである。オンボード済みのエンドポイントのみをチェックする(OnboardingStatus == "Onboarded"、DeviceCategory == "Endpoint")。
以下のいずれかに一致した場合、そのデバイスはPHP検出済みとみなす。
名前のマッチング方法:
^…$)であるため、ファイル名全体に一致する必要があり、部分一致はしない。startswith "php"、さらに両OSで frankenphp、Linuxでは lsphp。その後、残ったものに対してのみ正規表現を実行する。php9.dll)は自動的に一致する。patterns.test.js は各正規表現をこれらの例に対して実行する。正規表現を変更した場合は、そこの例も更新すること。Windows
| チェック | テーブル | 一致対象 | 重要な理由 |
|---|---|---|---|
| PHP実行ファイルの起動 | DeviceProcessEvents | 正規表現 ^(php(-cgi|-win)?|frankenphp)\.exe$。これは正確に4つの名前のみを許可する: php.exe(CLI)、php-cgi.exe(FastCGIワーカー)、php-win.exe(CLI、コンソールなし)、frankenphp.exe(Webサーバーに埋め込まれたPHP)。phpstorm64.exe やその他の php* ツールは一致しない | PHPが実行されたことを証明する。ハント2ではバイナリのバージョン情報(ProcessVersionInfoProductVersion)からバージョンを表示する |
| PHPエンジンDLLの読み込み | DeviceImageLoadEvents | 正規表現 ^php\d*(ts|isapi|apache[\w_]*)?\.dll$: php、任意のバージョン番号、オプションの ts / isapi / apache… サフィックス、そして .dll。例は以下のDLL表にある。拡張DLL(php_curl.dll やその他の php_*.dll)は一致しない。php の直後にアンダースコアが許可されていないため | IISやApacheなど別のプロセス内でPHPが動作しているのを確認できる唯一の方法。そこでは php*.exe は起動しない。ハント2ではDLLとそれを読み込んだプロセスを表示する |
| インストール済みソフトウェア | DeviceTvmSoftwareInventory | 正規表現ではない: SoftwareName has "php"(名前に php が単語として含まれる)または SoftwareVendor =~ "php"(完全一致、大文字小文字を区別しない) | 登録済みのインストールを検出する。Zipインストール(Windowsで一般的な方法)は通常登録されない |
Windowsパターンが一致するPHPエンジンDLLの例
| DLL | 内容 | 典型的なホストプロセス(hunt 2 に表示) |
|---|---|---|
php8.dll、php7.dll | エンジン、非スレッドセーフビルド | php-cgi.exe(IIS、nginx、Apache mod_fcgid 配下の FastCGI)、php.exe |
php8ts.dll、php7ts.dll、php5ts.dll | エンジン、スレッドセーフビルド | httpd.exe(Apache モジュール)、php.exe |
php8apache2_4.dll、php7apache2_4.dll、php5apache2_2.dll | Apache モジュールローダー | httpd.exe |
php5isapi.dll | レガシーな IIS ISAPI モジュール(PHP 5.2 以前、サポート対象外) | w3wp.exe |
この表は一般的な名前を列挙したものであり、許可リストではありません。パターンに一致する名前であればどれでもマッチします。DLL 名からわかるのはメジャーバージョンのみです。正確なバージョンは、同じデバイス上の php-cgi.exe または php.exe のプロセス行、あるいはインストール済みソフトウェアから取得します。
Linux
| チェック | テーブル | マッチ条件 | 重要な理由 |
|---|---|---|---|
| PHP バイナリの起動 | DeviceProcessEvents | 正規表現 ^(php[\d.]*|php-(fpm|cgi)[\d.]*|lsphp[\d.]*|frankenphp)$: php、php-fpm、php-cgi、lsphp(LiteSpeed)のそれぞれに、数字とドットからなる任意のバージョンが付いたもの、および frankenphp。例: php、php8.3、php83、php-fpm8.3、php-cgi8.2、lsphp83。phpunit、phpize、php-config などのツールはマッチしません。ベース名の後に続けられるのは数字とドットのみだからです | PHP が実行されたことを証明します。hunt 2 では、2 つ目の正規表現 php-?(?:fpm|cgi)?/?(\d+\.\d+) によって名前、パス、またはコマンドラインから抽出された major.minor のヒントが表示されます(例えば php-fpm8.3 や /etc/php/8.3/ からは 8.3.x) |
| インストール済みパッケージ | DeviceTvmSoftwareInventory | 正規表現ではありません: SoftwareName contains "php"(部分文字列なので、php8.3-fpm や libapache2-mod-php8.3 のようなバージョン付きの名前もマッチします)または SoftwareVendor =~ "php" | Apache mod_php と、ウィンドウより前から稼働している php-fpm を確認する唯一のチェックです。Linux にはここでライブラリロードのチェックがなく、フォークされた php-fpm ワーカーはプロセス起動を生成しないためです |
| チェック | テーブル | マッチ条件 | 重要な理由 |
|---|---|---|---|
| アクティブな Web 配信 | DeviceNetworkEvents | 固定ポートリスト(80、443、8080、8443)での InboundConnectionAccepted。ループバックは除外されます(RemoteIPType != "Loopback")。個別の送信元とパブリックな送信元、および受け入れプロセス(via <process>)を表示します | Web サーバーがルックバックウィンドウより前に起動していても、デバイスが配信していることを証明します。ntoskrnl.exe = HTTP.sys(IIS、ADFS、RD Gateway、Windows Admin Center、SSTP) |
| Web サーバーの起動 | DeviceProcessEvents | 正規表現ではありません: 大文字小文字を区別しない(in~)完全一致名の固定リスト。Windows: w3wp.exe、httpd.exe、nginx.exe、caddy.exe、lighttpd.exe、frankenphp.exe。Linux: httpd、apache2、nginx、lshttpd、litespeed、openlitespeed、caddy、lighttpd、frankenphp | ウィンドウ内で起動し、PHP をロードしなかった Web サーバーは、高い信頼度で「Not seen」となります。Windows では、正規表現 -ap\s+"?([^"\s]+) によって w3wp.exe のコマンドラインから抽出された IIS アプリプール名も表示されます |
hunt 3 における PHP 列の値
| 値 | 意味 |
|---|---|
PHP seen (see hunt 2) | 少なくとも 1 つの PHP 検出チェックがマッチしました。 |
Not seen | PHP の証拠がなく、かつウィンドウ内で Web サーバーが起動した(高い信頼度)か、デバイスが Web を配信していません。 |
Not seen - no web server start in <N>d(N = Lookback) | Web ポートで配信していますが、検査すべき Web サーバープロセスの起動がありません。これが残存する盲点です: ホスト上の via プロセスを確認してください。 |
uncomment for evidence-only rows と記された行のコメントを解除してください。let Lookback = 30d; の1行を変更し、すべてのハントで同じ値を使用してください。
npm test は 30d を超える値や、ハント間で値が混在している場合を拒否します。両 OS:
Windows:
Linux、Linux ポジティブコントロールテストが合格するまで未検証:
Linux のカバレッジギャップ:
PHP 検出ブロックは、各 OS の3つのハント内で同一です。3つすべてをまとめて変更してください。これらがずれると npm test が失敗します。検出用正規表現を変更した場合は、tests/lint/patterns.test.js の期待されるマッチと非マッチを更新してください。テストはクエリから正規表現を読み取るため、コピーではなくあなたの編集内容をチェックしています。