
「php2shell」CVE-2026-109618 および CVE-2026-109619 向けの Zeek と Suricata の検知
「php2shell」CVE-2026-109618 / CVE-2026-109619 に関連する HTTP インジケーター向けの Zeek 検出および Suricata ルール。これは PHP の longjmp() メモリ処理における潜在的な RCE です。検出結果は、疑わしいトラフィックまたは影響を受ける可能性のあるソフトウェアを特定します。
Zeek 7.0.0 以降が必要です。Zeek Package Manager でインストールします:
zkg install https://github.com/corelight/php2shell-detect
インストール済みパッケージは、Zeek 設定で @load packages を指定して読み込みます。
直接チェックアウトした場合は、/path/to/php2shell-detect/scripts を読み込みます。
このパッケージは、標準の Zeek HTTP、ファイル解析、notice、およびクラスタ API を使用します。
このパッケージは、POST フィールド名の長さ、選択されたシリアライズデータ 構造、および繰り返される MIME ヘッダーを検査します。また、設定されたセッション Cookie を使用して、 関連する不審なトランザクションを相関付けます。レスポンスヘッダーからは、 別途 PHP バージョンのインベントリを提供します。トラフィックは Zeek の HTTP アナライザーから可視である必要があり、暗号化された HTTP は検査前に復号が必要です。
すべての通知タイプは PHP2SHELL:: の下にあります:
| 通知 | 観測内容 |
|---|---|
PHP_Crash_Sequence | 1 つのセッション内で関連する不審な HTTP トランザクション |
Multipart_Header_Flood | 認識された MIME ヘッダーの過度な繰り返し |
Large_Form_Key | 異常に長い URL エンコードされたフォームフィールド名 |
Serialized_Integer_Overflow | 異常に大きなシリアライズ整数トークン |
Serialized_Integer_Collisions | 認識されたシリアライズ整数キーパターン |
Serialized_String_Collisions | 認識されたシリアライズ文字列キーパターン |
Serialized_Duplicate_Array | 繰り返されるシリアライズ配列エントリパターン |
Serialized_Object_Repetition | 繰り返されるシリアライズオブジェクトパターン |
Potentially_Affected_PHP_Version | レスポンスヘッダーが、設定された影響範囲内のバージョンを通知する |
結果は標準の notice.log に出力されます。このパッケージは追加のログ
ストリームを作成しません。通知の sub フィールドには、ヘッダー名、
トランザクションの深さ、カウント、および短いエスケープ済み POST ボディサンプルなどの
限定的なコンテキストが提供されます。サンプルはデフォルトでエスケープ前に
64 生バイトに制限されます。省略するには
notice_body_sample_bytes=0 を設定します。Cookie とそのダイジェストは
メモリ内でのみ使用され、このパッケージによってログに記録されることはありません。
デフォルトでは、個別のリクエストインジケーターごとにトランザクションごとに通知を出力できます。
同じインジケーターの繰り返し一致は抑制されます。各トランザクションで最初のリクエスト検出後に
ボディ検査を停止するには、detect_only_once=T を設定します。
バージョンインベントリ通知は、リクエスト検出とは独立しています。
すべての設定は PHP2SHELL:: 名前空間を使用します:
| 設定 | デフォルト | 効果 |
|---|---|---|
detect_only_once | F | 個別のリクエストインジケーターごとに報告します。T は最初の検出後に停止します |
max_trans_depth | 3 | 接続ごとに最初の 3 つの HTTP トランザクションを検査します。0 はすべてを意味します |
inspect_cookies | T | 相関付けにセッション Cookie を使用します |
session_cookie_names | {"PHPSESSID"} | セッションの関連付けに使用する Cookie 名 |
correlation_window | 1 min | 保留中のセッション相関状態の最大経過時間 |
max_pending_sessions | 1000 | 各相関テーブルに保持するエントリ数の上限 |
inspect_post_bodies | T | POST エンティティデータと MIME ヘッダーを検査します |
http_body_analysis_byte_depth | 8388608 | POST ボディ検査作業に対する保守的な 8 MiB の上限。MIME パート間で共有されます。0 はボディ配信を無効にします |
body_overlap_bytes | 256 | ボディチャンクをまたぐパターンのために保持するオーバーラップ |
large_form_key_bytes | 65536 | フォーム名のワイヤバイトしきい値 |
multipart_header_threshold | 128 | 検出に必要な、繰り返される認識済み MIME ヘッダー数 |
notice_body_sample_bytes | 64 | 通知サンプルごとの最大生ボディバイト数。0 はサンプルを省略します |
detect_affected_php_versions | T | レスポンスヘッダーのバージョンインベントリを有効にします |
php_version_notice_interval | 1 hr | サーバー IP ごとに、ポートとバージョンをまたいでバージョン通知を抑制します |
php_fixed_patch_versions | 8.2: 35, 8.3: 36, 8.4: 27, 8.5: 12 | ブランチごとの暫定的な最初の修正パッチ |
例:
redef PHP2SHELL::detect_only_once = T;
redef PHP2SHELL::notice_body_sample_bytes = 0;
redef PHP2SHELL::session_cookie_names += { "custom_session" };
POST検査では、本文全体を組み立てるのではなく、重複を制限したファイル解析チャンクを使用します。フォーム名のカウントは値の区切り文字と生のNUL終端を考慮します。対象となるPOSTエンティティのみがアナライザーを受け取り、本文の予算とトランザクション制限は意図的に検出範囲を制約します。これらの指標は不完全であり、良性のトラフィックにも一致する可能性があります。
Zeekクラスタでは、ワーカーは小さなセッション観測をCluster::publish_hrw()でプロキシにルーティングします。同じセッションの両方のステージが同じプロキシに到達し、プロキシがそれらを照合してPHP_Crash_Sequenceを直接発行します。通知はフォローアップの接続ID、UID、トランザクション深度を保持し、peer_descrがプロキシを識別します。ワーカーはコールバック用の完全な接続レコードやHTTPレコードを保持しません。
トリガーと一致しないフォローアップはcorrelation_windowの後に期限切れとなり、max_pending_sessionsが各テーブルを制限します。プロキシは各セッションキーについて最初の一致しないフォローアップを保持します。トリガーはそのキーを直接検索して逆順に到着するメッセージを処理し、同じキーの後続の一致しないフォローアップは、そのエントリが一致するか期限切れになるまで統合されます。照合には観測タイムスタンプと、クライアント、サーバー、サーバーポート、HTTPホスト、セッションダイジェストを使用します。スタンドアロンのZeekはローカルで相関処理を行います。通常のプロキシ接続が必要です。
プロキシ通知は、correlation_windowに対してUIDとトランザクション深度をキーとする標準の通知抑制を使用します。これはワーカーの検出結果とは独立しており、ワーカーの本文検査を停止しません。異なるプロキシ上の異なるセッションCookieに対する同時一致は、クラスタの抑制が伝播する前に重複した通知を生成する可能性があります。
外側のServerおよびX-Powered-Byレスポンスヘッダーが、GETへのレスポンスを含めてチェックされます。ヘッダー名は大文字と小文字を区別しません。影響を受ける下限はPHP 5.4.0です。以下のカットオフは、2026年10月18日に確認された最新の安定版リリースの次のパッチが両方のバグに対処することを前提としています:
| ブランチ | 確認時の最新安定版 | 暫定的な最初の修正リリース |
|---|---|---|
| 8.2 | 8.2.34 | 8.2.35 |
| 8.3 | 8.3.35 | 8.3.36 |
| 8.4 | 8.4.26 | 8.4.27 |
| 8.5 | 8.5.11 | 8.5.12 |
設定された修正リリースおよび同じブランチのそれ以降のパッチはアラートを発しません。カットオフが設定されていないブランチは候補のままです。一般的なベンダーのサフィックスは基になる数値バージョンを使用します。バナーは存在しない場合、書き換えられている場合、または修正がバックポートされたベンダービルドを反映している場合があるため、これらはインベントリのヒントです。
suricata/php2shell.rules内の3つのルールはZeekとは独立して動作します。zkgでZeekパッケージをインストールしても、これらのルールはSuricataに読み込まれません。3つのルールはすべてファイル内で有効になっており、Zeekの設定オプションはそれらに影響しません。
| SID | 検査バッファ | 観測内容 |
|---|---|---|
| 3000650 | メソッドPOSTのhttp.request_body | リクエスト本文内の検出マーカー |
| 3000651 | http.response_header | ServerまたはX-Powered-Byによって通知される、影響を受ける可能性のあるPHPバージョン |
| 3000652 | http.response_body | phpinfo形式のHTML見出し内の、影響を受ける可能性のあるPHPバージョン |
suricata.yaml内の既存のrule-filesリストにルールファイルを追加し、他のルールエントリはそのまま保持してください。チェックアウトの絶対パスを使用します:
rule-files:
- /path/to/php2shell-detect/suricata/php2shell.rules
設定を検証してから、通常のデプロイ手順で Suricata をリロードまたは再起動してください:
suricata -T -c /path/to/suricata.yaml
ルールは Suricata 8.0.6 で検証済みです。HTTP ボディ検査を有効にする必要があり、リクエスト/レスポンスのボディ上限とストリーム再構成の上限が、どのコンテンツがこれらのルールに到達するかを決定します。暗号化された HTTP は、検査前に復号が必要です。
これらのルールは、EVE アラートレコード内で alert.signature_id を使用するか、設定されたアラート出力内の SID によって特定します。POST マーカーは正当なトラフィックでも発生する可能性があり、エクスプロイト試行を立証するものではありません。Suricata は Zeek パッケージのセッション相関を実行しません。
バージョンルールはソフトウェアインベントリのシグナルです。ヘッダー名は大文字小文字を区別せずに照合され、ボディルールは phpinfo 形式の HTML 見出しを認識します。どちらも上記の暫定的なバージョンカットオフを使用しており、バナーが欠落または書き換えられている場合やベンダーのバックポートによって、その精度は制限されます。 リリース前の TODO: 両方のルールの影響範囲と最初の修正リリースを、公式の PHP アドバイザリおよびリリースノートと照合して検証すること。
各バージョンルールは、クライアント、ポート、および通知されるバージョン全体にわたり、サーバー IP ごとに 1 時間あたり 1 件のアラートに制限します。これらはレスポンスを検査するため、track by_src はサーバーを追跡します。各 SID は独自の制限を維持するため、1 台のサーバーが同じ 1 時間内にヘッダーアラートと phpinfo アラートの両方を生成する可能性があります。POST マーカールールには 1 時間あたりの制限はありません。
btest をインストールし、zeek、zeek-cut、unzip、bash、および make が PATH 上にあることを確認してから、次を実行します:
make check
make test
make zam
make zam は Zeek の ZAM オプティマイザを有効にしてテストを繰り返します。含まれる単一の btest は、圧縮・マージされた上流の Zeek HTTP コーパスを使用し、完全な TSV HTTP ログと notice ログを比較します。PHP 8.0.26 のインベントリ notice が 1 件発生することが想定されており、reporter.log は存在してはなりません。入力の詳細とライセンスについては corpus provenance を参照してください。