「パッチ公開4日前に悪用発生」PHP2Shellが打ち砕いたアップデートの猶予期間

「パッチ公開4日前に悪用発生」PHP2Shellが打ち砕いたアップデートの猶予期間

PHPセキュリティ脆弱性RCECVE-2026-109618CVE-2026-109619運用セキュリティ

データソース:Datadog Security Labs + PHP 官方公告 + AWS 威胁情报

脆弱性の事前流出:パッチより4日早く始まった野生の悪用

2026年10月21日、PHP公式メンテナは深刻なリモートコード実行(RCE)の脆弱性2件に対するセキュリティ修正をリリースした。CVE-2026-109618およびCVE-2026-109619と採番されたこれらは、いずれもCVSS v4.0スコアで9.2に達する。修正版はPHP 8.2.35、8.3.36、8.4.27、8.5.12向けに提供され、すでに公式サポートが終了した7.4、8.0、8.1系に対しても、セキュリティチームは例外的にソースコードレベルのバックポートパッチを配布した。

従来の協調的脆弱性開示(CVD)は、パッチの公開が外部での悪用に先行することを前提に成立していた。しかし「PHP2Shell」と名付けられたこの事案において、その前提は崩壊した。公式パッチの公開に先立ち、セキュリティ企業のHuntressは「Leaked Critical PHP Vulnerabilities」と題した警告を発令し、脆弱性の詳細が地下組織を通じて事前に流出したと指摘。PHP開発チームも異例の対応としてphp.announceで第509号アナウンスを配信し、世界中のインフラ管理者に事前警戒を呼びかけた。Ars Technicaはこの異例の事態を取り上げ、メンテナ自らが事前に警告を発したことは事態が制御不能に陥った証拠であると報じた。

脅威アクターは公式パッチを待たなかった。Datadog Security Labsは公表前に独自で実証コード(PoC Exploit)の構築に成功。さらにAWSの脅威インテリジェンスチームは、ハニーポット「MadPot」において10月17日の時点で実環境での攻撃試行を捉えていたことを確認した。野生での悪用は公式発表より実に4日も早く始まっていた。防御側が正式な通知を受け取る前に、インターネットに公開されたノードはすでに実弾による探索に晒されていた。

セキュリティ企業Sansecは即座に警告を発し、パッチ公開から15分以内に全世界規模の自動化スキャンが始まると予測した。10月21日時点で、DatadogはPatchstackの観測データを引用し、すでに複数の悪用試行と無差別スキャンがインターネット上で拡散していることを確認した。攻撃者と防御者が同時にパッチコードを手にした瞬間、管理者が事態を把握して対応するための猶予期間(パッチ適用ウィンドウ)は完全に消滅した。

Datadog Security Labs による PHP2Shell 脆弱性の研究概要 図:Datadog Security Labsが公表したPHP2Shellの影響範囲と攻撃チェーン概要。出典:Datadog Security Labs

アプリコードに到達せず:パーサーのクリーンアップ処理で直接シェルを奪取

多くのWebアプリケーションセキュリティは、APIゲートウェイでの認証やルーティング認可、ビジネスロジックの監査に依存している。しかしDatadog Security Labsの実証において、PHP2Shellはこれらすべてのアプリケーション層ロジックをすり抜けた。検証環境であるPHP 8.5.11、Apache prefork(mod_php)、memory_limit=16M、post_max_size=64Mという構成において、攻撃者は未認証の細工されたHTTP POSTリクエストを1回送信するだけでホストの完全な制御権を奪取できた。

悪用の引き金は、PHPランタイムのC言語レイヤーで引かれる。悪意あるリクエストがサーバーに到着した瞬間、構文解析のエラーが引き起こされる。この時点で制御フローはPHPスクリプト側に一切渡っていない。問題は、PHPが失敗したリクエストの後処理(クリーンアップルーチン)を実行するフェーズで、ペイロードに仕込まれた任意のシステムコマンドがそのまま実行されてしまう点にある。固定文字列を返すだけの最小限のスクリプトしか置いていないサーバーであっても、パーサーがエラーを処理した瞬間に完全に陥落する。

この突破口は、現在の主流な運用形態のほぼすべてに波及する。Huntressの報告や技術トラッカーによると、PHP 5系からPHP 8系に至る広範なバージョンが影響を受ける。影響を受けるランタイムには、Apache mod_php、php-cgi、高負荷運用の要であるPHP-FPM、新世代のFrankenPHP、そしてLiteSpeedが含まれる。解析フェーズで発動するため、アプリケーション層の認証やミドルウェアの防壁は何一つ機能しない。

リクエスト解析段階でコマンド実行を引き起こす PHP2Shell の呼び出しフロー 図:解析段階のエラー後処理ルーチンからシステムコマンド実行に至る呼び出しフロー。出典:Datadog Security Labs

カウンタ加算がメモリ確保に先行:二重CVEの内部メカニズムを読み解く

2件の脆弱性は、それぞれHTTPリクエスト処理ライフサイクルの異なる構造的欠陥を突いている。CVE-2026-109618の本質は、メモリ確保が失敗した際のエラー処理ルーチンが無効なデータ構造を誤って信頼してしまう点にある。トリガーとなるのはZendエンジンのコア関数 _zend_hash_str_add_or_update_i() だ。この関数がハッシュテーブルにキーと値を書き込む際、キー文字列のためのメモリを割り当てる前に、内部カウンタを先行してインクリメントしていた。

もしキー名のためのメモリ確保が memory_limit を超過した場合、Zendエンジンは即座に致命的エラー(Fatal Error)を発生させる。しかしこの時点で、内部カウンタにはメモリ未確保のバケットノードがすでに計上されている。このノード内部には過去のヒープ割り当てで残されたダーティバイト(残存データ)が含まれている。その後のクリーンアップ処理は、このゴミデータを有効な変数構造体と見なして破棄ルーチンを実行するため、攻撃者は残存データを制御することで system() 関数の呼び出しへと制御を誘導できる。最新のPHP 8.5.12においてもカウンタの先行更新処理自体は残存しており、公式の修正は割り当て失敗時に不完全なエントリを残さないようエラー処理フローを改修することで対処した。

もう一方の脆弱性 CVE-2026-109619 は、ワーカープロセスがリクエスト間で状態を使い回す仕組みを悪用する。PHP 8.5環境下のmod_phpでは、致命的エラー時のバックトレース機能が直接的な任意コマンド実行に結びつく。さらに一般的なファイルアップロード処理においても、致命的エラーによって破棄されなかったセッションやデコーダーの状態が後続のリクエストへと引き継がれる。多くの環境でデフォルト有効化されている session.upload_progress.enabled は、コード側で session_start() を明示的に呼び出さなくてもバックグラウンドで常時稼働している。

CVE-2026-109619の成立には、後続のリクエストが同一のワーカープロセスに連続して到達する必要がある。マルチプロセス構成や高トラフィック環境では単一リクエストでの成功率は分散するものの、これは統計的な確率の問題に過ぎず、脆弱性の深刻度を下げるものではない。

ワーカープロセスがリクエスト間でダーティメモリを再利用する遷移メカニズム 図:ワーカープロセスがリクエスト間で汚染されたメモリを再利用する状態遷移の仕組み。出典:Datadog Security Labs

アップロード進捗の無効化では防げない:設定変更で覆い隠せないメモリ欠陥

脆弱性の公表直後、コミュニティ内では一時的な回避策として session.upload_progress.enabled = 0 に設定する手法が広く共有された。しかしこの設定変更には重大な盲点が存在する。これはCVE-2026-109619における一部のアップロード経路を塞ぐに過ぎず、CVE-2026-109618の根本的なメモリ割り当て欠陥には一切効果がない。メモリ欠陥を放置したままアップロード進捗機能のみを無効化しても、システム全体の危機を回避することは不可能だ。

PHP公式チームはその後、厳格な暫定緩和策を提示した。CVE-2026-109618に対しては、memory_limit = -1 の設定を推奨している。同時にリバースプロキシ側でリクエストボディのサイズを厳密に制限し、コンテナ層でcgroupsによるメモリリミットを適用する必要がある。インフラ担当者は RLIMIT_AS や RLIMIT_DATA などのプロセスレベル制限を使用してはならない。これらは攻撃者が悪用に必要とするメモリ割り当て失敗を意図せず誘発してしまうからだ。

CVE-2026-109619への対策としては、アップロード進捗の無効化に加えてPHP 8.5運用環境で fatal_error_backtraces = 0 の設定が求められる。これらすべての設定値はPHPがリクエストをパースする前にロードされる必要があり、各ワーカープロセスの再起動が必須となる。CERT-EUは勧告2026-018において、即座に修正パッチを適用できないシステムは外部ネットワークから隔離すべきであると忠告した。Datadogもまた、リバースプロキシやエッジ境界で異常リクエストを遮断することを強く推奨している。

40年の技術的負債の清算:ランタイムの即時刷新こそ唯一の防御

この一連の事態を振り返ると、PHP2Shellが破綻させたのは単なるパーサーの実装にとどまらず、「パッチが先に出てユーザーが落ち着いて適用する」という長年の防衛モデルそのものだった。公式開示前の情報漏洩と4日間にわたる野生の先行攻撃により、防御側に残されていた安全マージンは完全に消滅した。攻撃者と管理者が同時にパッチコードを手にしたことで、世界規模の攻撃はパッチ公開から数分で動き出した。

米IEEE Spectrumはこの事態を象徴的に表現した。「AIの進化は、40年間にわたって積み重なった技術的負債の即時清算を迫っている」。PHP2Shellはその典型例と言える。メモリ確保に先立ってカウンタをインクリメントするという設計は、何十年もの間見過ごされてきた。PHP 8.5.12のパッチでさえ、カウンタの順序そのものを再設計したのではなく、失敗時の例外処理に手を加えたに過ぎない。

潜在的な侵害の兆候を調査するインフラ担当者は、3つの典型的な兆候に注意を払う必要がある。第一に、リクエスト初期化フェーズで発生する突発的なメモリ不足(Out of Memory)エラー。第二に、異常なフィールド名を含む巨大なフォーム送信。そして第三に、Webサーバーの実行権限下で予期せずフォークされたシェルの子プロセスだ。これらが検知された場合、パーサーを突破しようとする攻撃が試行されている可能性が高い。

対応時間がゼロに短縮された今、設定の微調整は一時しのぎに過ぎない。リバースプロキシによる遮断は一部のスキャンを防ぐことはできても、パーサー内部に潜む脆弱性を根本から解決するものではない。すべての本番環境において、影響を受けるランタイムを直ちにパッチ適用済みバージョンへ刷新することだけが、防御側に残された唯一の選択肢である。

参考リンク:

  • Datadog Security Labs 脆弱性解析レポート
  • PHP 公式セキュリティ更新通知および php.announce 第509号アナウンス
  • Huntress 脅威インテリジェンスレポート
  • AWS Security Blog 観測記録
  • Ars Technica 特集記事
  • CERT-EU セキュリティ勧告 2026-018
  • IEEE Spectrum 特集記事