Article

🔐 PHP2Shell徹底解説:CVE-2026-109618・109619はなぜ認証不要RCEになるのか【図解・対策】

php, security, cve, mermaid, backend

調査基準日:2026年10月22日(日本時間)
本記事はPHP公式のセキュリティ告知、php-srcの2件のGitHub Security Advisory、PHP Foundation、Wiz Researchの公表資料を照合して作成しています。図は理解を助けるための概念図であり、実際のエクスプロイトを再現する手順ではありません。

2026年10月21日(UTC)、PHPプロジェクトがCVSS v4.0で9.2(Critical)の脆弱性を2件修正しました。名称はCVE-2026-109618とCVE-2026-109619。セキュリティ研究者らは一連の問題を「PHP2Shell」とも呼んでいます。

どちらもPHPアプリケーションの認証・認可実装の問題ではありません。PHP本体がHTTPリクエストを処理する途中のメモリ管理と、異常終了時の内部状態の後始末に原因があります。

一定の条件下では、ログインしていない第三者によるリモートコード実行(RCE)が可能で、さらにPHPワーカーのプロセスメモリを読み出す攻撃も研究者によって実証されました。

最も重要な結論を先に書きます。

  • 該当するPHPを利用しているなら、修正済みバージョンへの更新が最優先。
  • Apacheのmod_phpでは、アプリケーションコードの実行前に成立する攻撃経路が確認されている。
  • PHP-FPMや他の実行方式も一律に安全ではない。ただし、攻撃が成立する条件・確認された経路は異なる。
  • WAF、入力制限、PHP設定変更などは一部経路のリスク軽減にすぎず、公式は完全な代替策はないとしている。
  • PHP 8.1以前は公式の通常サポート対象外。一部のベンダーはバックポートを提供するが、可能ならサポート中の系統へ移行する。

1. 何が発表されたのか

項目 CVE-2026-109618 CVE-2026-109619
通称 RCE via OOM corruption RCE via stale global state
主な原因 メモリ不足時に処理を中断し、不完全な状態のまま後片付けする 前回リクエストのメモリを指す内部状態がワーカーに残る
キーワード OOM、longjmp()、部分初期化 グローバル状態、Use After Free、リクエスト間の状態持ち越し
起こり得る被害 メモリ破壊、RCE メモリ破壊、情報漏えい、RCE
CVSS v4.0 9.2 / Critical 9.2 / Critical

用語を整理します。

  • CVE:公表された脆弱性を識別するための番号。問題の深刻度そのものを示す数字ではありません。
  • CVSS:脆弱性の深刻度を0〜10の数値などで表す共通の評価方式です。9.2は「Critical(緊急)」です。
  • RCE(Remote Code Execution):ネットワーク越しに、サーバー側で意図しないコードを実行されること。
  • OOM(Out Of Memory):処理に必要なメモリを確保できなくなる状態。
  • ワーカー(worker):Webリクエストを処理するPHPのプロセスなど。同じワーカーが複数のリクエストを順に処理することがあります。

修正版の一覧

PHPの系統 脆弱な範囲 今回の修正版
PHP 8.5 8.5.11以前 8.5.12以降
PHP 8.4 8.4.26以前 8.4.27以降
PHP 8.3 8.3.35以前 8.3.36以降
PHP 8.2 8.2.34以前 8.2.35以降
PHP 8.1以前 公式サポート終了済み 原則、サポート中の系統へ移行

※対象となるのは各系統のリリース版です。Linuxディストリビューションなどが独自に修正をバックポートしている場合、バージョン番号が表と一致しなくても修正済みの可能性があります。配布元のセキュリティアドバイザリで確認してください。PHPプロジェクトは7.4・8.0・8.1向けの最小限のソースバックポートも提供していますが、これらの系統が通常サポートに復帰したわけではありません。

参考:PHP公式の2026年10月21日告知

2. まずPHPがHTTPリクエストを処理する流れを理解する

多くのWebアプリケーションでは、PHPに到達したHTTPリクエストを次のような順序で扱います。

flowchart TD
    A["HTTPリクエストを受信"] --> B["PHPのリクエスト開始処理"]
    B --> C["POST本文・multipartの解析など"]
    C --> D["内部状態を初期化"]
    D --> E["アプリケーションのPHPコードを実行"]
    E --> F["終了処理・後片付け"]
    F --> G["次のリクエストへ"]
    C -. "異常終了時の問題" .-> F
    D -. "異常終了時の問題" .-> F

multipartとは、ファイルアップロードのように、1つのHTTPリクエスト内に複数の入力項目やファイルを格納する形式です。

通常、PHPは必要なメモリを確保し、データの初期化を終えてから処理を進め、最後に後片付けをします。問題は、初期化途中で致命的エラーが起きた場合です。

「作りかけのデータは必ず後片付け可能な状態になっている」「前のリクエストのデータは次のリクエストまでに消えている」という前提が破られると、PHP本体の実行に問題が生じます。

3. CVE-2026-109618:メモリ不足で「作りかけのデータ」を後片付けする

3-1. longjmp()とOOM

PHPは従来、メモリ不足(OOM)の際にlongjmp()を用いて現在の処理から一気に脱出する仕組みを利用していました。

longjmp()はC言語で使われる仕組みで、通常の関数呼び出しの戻り順をたどらず、事前に保存した実行位置へ処理を移します。

問題は、途中で抜けることを想定していない内部処理です。データを準備している最中にlongjmp()で脱出すると、そのデータが初期化済みなのかどうかを区別できない場合があります。

flowchart TD
    A["リクエストの初期化開始"] --> B["内部データのメモリを確保"]
    B --> C["一部のフィールドを初期化"]
    C --> D{"OOMが起きる?"}
    D -- "いいえ" --> E["初期化完了・通常処理"]
    D -- "はい" --> F["従来の処理:longjmpで中断"]
    F --> G["不完全な内部データが残り得る"]
    G --> H["終了処理が有効なデータとして扱う"]
    H --> I["メモリ破壊・RCEにつながる可能性"]

重要なのは、不正な状態になった領域自体の確保に失敗する必要はないことです。関連しないメモリ確保の失敗でも、タイミングが合えば問題のある状態で処理が中断され得ます。

3-2. なぜHTTPリクエストだけで起こり得るのか

PHP公式のAdvisoryは、multipart解析時に通常のpost_max_sizeなどの上限設定をすり抜ける、あるいは想定以上のメモリを消費させる入力条件があることを説明しています。

例えば、HTTPで送られる本文が小さく見えても、PHPが内部でヘッダーや配列を組み立てるときに、より多くのメモリを使う場合があります。

PHPのmemory_limit(PHP処理ごとのメモリ使用上限)に達するタイミングが、内部状態の初期化途中と重なると危険です。

特にApache + mod_phpでは、リクエスト開始に失敗しても終了処理が呼ばれるため、アプリケーションのPHPコードが実行される前の経路で、認証不要RCEが実証されています。

一方、PHP-FPMは開始処理自体に失敗したワーカーを終了させる動作があり、この特定の経路は同一ではありません。ただし、起動後の処理でOOMが発生する別経路は残り、PHP-FPMも影響対象です。公式Advisoryでは、このCVE単独の完全なFPMエクスプロイトは未実証と記載しています。

3-3. 修正の考え方

修正版では、OOM発生時に無条件でlongjmp()する設計を改めています。

  • まずsoft OOMとして、PHP仮想マシンが安全に中断できる地点まで処理を遅延させる。
  • さらに高いメモリ使用量のしきい値に到達した場合は、hard OOMとしてプロセスを直ちに終了させる。

これにより、初期化途中で不完全なデータを残したまま後片付けへ飛ぶ問題を根本から回避します。単にメモリ使用上限を引き上げるパッチではありません。

一次情報:CVE-2026-109618のPHP公式GitHub Advisory

4. CVE-2026-109619:前のリクエストのメモリを次のリクエストが参照する

こちらの中心は**Use After Free(UAF:解放済みメモリの利用)**です。

「メモリを解放する」というのは、使っていた領域を今後ほかのデータに使える状態にすることです。解放済みなのに古い参照(ポインタ)を使うと、別の用途に再利用されたメモリを、昔のデータだと誤認することがあります。

PHPワーカーには、リクエストごとに確保されるメモリとは別に、ワーカー自体に残る内部状態があります。そこで、前のリクエストのメモリを指す情報が適切に消されなければ、次のリクエストで問題になる可能性があります。

4-1. 2つのリクエストをまたぐ不具合

sequenceDiagram
    participant C as クライアント
    participant W as 同じPHPワーカー
    participant M as リクエスト用メモリ
    C->>W: リクエストA
    W->>M: 内部データを作成
    M-->>W: データへの参照
    Note over W: ワーカー内の状態が参照を保持
    Note over W: 致命的エラーなどでリセット失敗
    W->>M: A用メモリを解放
    C->>W: リクエストB
    Note over W: 古い参照が残っている
    W->>M: 解放済み領域を誤って参照
    Note over W,M: UAF・メモリ破壊・情報漏えい

図では同じワーカーが2つのHTTPリクエストを順番に処理しています。これが重要な条件です。

公式Advisoryは攻撃成立に必要な役割を2つに分けています。

  1. Producer(作る側):リクエストAに属するデータへの参照を、ワーカーに残る内部状態へ保存する処理。
  2. Consumer(使う側):リクエストBで、その古い参照を正常なデータとして読み込む・解放するなどの処理。

前者が残した状態を後者が利用すると、UAFに至ります。

4-2. なぜ異常終了が関係するのか

致命的エラー、メモリ不足、実行時間超過などで、内部状態をリセットする処理が飛ばされる可能性があります。

また、正常な終了処理でいったん状態をクリアしても、後続の終了処理で警告などが発生し、古い内部状態が再度保存される経路も問題になります。

研究者は次のような具体例を確認しています。

  • Session upload progress:アップロードの進捗をセッションへ保存する機構の内部状態が残る。アプリ側でsession_start()を呼んでいない場合でも成立し得る経路がある。
  • fatal error backtrace:致命的エラーの呼び出し履歴が解放済みメモリを指す。特にPHP 8.5の関連設定が影響する経路が確認された。
  • OPcacheの型の取り違え(Type Confusion):コンパイル済みのPHP情報を共有する仕組みで、異なる用途の内部ポインタを誤って対応付けてしまう経路も研究者が実証した。

OPcacheはPHPのコードを事前に解析・コンパイルした結果を保持し、Webアプリケーションの処理を高速化する仕組みです。Type Confusionは、実際とは異なるデータ型だとプログラムが誤認して処理する不具合を指します。

研究者Wizは、WordPress、Drupal、Joomlaを使った検証環境で認証不要RCEを実証し、別途PHPワーカーのメモリが漏えいする経路も示しました。ただし、これはすべてのサイトで即座に攻撃が成功することを意味しません。バージョン、PHPの実行方式、拡張機能、メモリ配置、フロントエンド設定などで成立条件が変わります。

一次情報:CVE-2026-109619のPHP公式GitHub Advisory / Wiz Researchの技術解説

5. なぜ「ログイン機能が安全」でも防げないのか

問題の一部は、アプリケーションが動き始めるより前のPHP本体にあります。

flowchart LR
    A["外部のHTTPリクエスト"] --> B["Webサーバー"]
    B --> C["PHP本体の入力解析・初期化"]
    C --> D["フレームワークの処理"]
    D --> E["ログイン認証・認可"]
    E --> F["業務ロジック"]
    C -. "一部の攻撃経路" .-> G["メモリ破壊"]

例えばWordPressのプラグインを全部更新したとしても、PHP本体の脆弱性はPHP本体を修正しなければ解消しません。

WordPressやLaravelなどのフレームワーク自体が必ずしも悪いわけではありません。これらが稼働しているサーバーで、影響を受けるPHPが使われているかが重要です。

攻撃に成功すると、通常はPHPワーカーを実行しているOSユーザーの権限でコードが動くため、その権限からアクセス可能な設定、ファイル、DB接続情報などが危険にさらされ得ます。必ずroot権限を取得できる、という話ではありません。

6. Apache / PHP-FPM / コンテナ環境ごとの違い

構成 注意点
Apache + mod_php 公式が最も高リスクと評価。アプリ処理前の認証不要RCEを確認
Nginx + PHP-FPM 安全とはいえない。CVE-2026-109619のセッション関連経路などでRCEが確認されている
Apache + PHP-FPM FPMを使う場合はmod_phpと異なる。設定や入力経路の確認が必要
FrankenPHP / LiteSpeed PHP公式は影響対象として警告。個別経路の確実性は構成によって異なる
Docker / Kubernetes コンテナ内で実行されるPHPが対象。ホストOSや依存ライブラリの更新だけでは不足する場合がある
WordPressなどのCMS CMS本体だけでなく、その実行環境のPHPバージョンを確認する

公式Advisoryの記載は確認済みの攻撃と、ソース解析で懸念される経路を区別しています。mod_phpの実証結果をFPMのすべての構成へ無条件に当てはめたり、逆にFPMなら安全だと判断したりしないことが重要です。

7. 実際に何を確認すればいいか:運用者向けチェック

7-1. PHPのバージョンを調べる

php -v
  • php:PHPのCLI(ターミナルから実行する環境)を起動するコマンド。
  • -v:バージョンを表示するオプション。

注意:これはCLI側のPHPであり、Webサーバーが実際に使用するPHPとは異なる場合があります。

php --ini
  • --ini:CLIが読み込んでいるphp.ini(PHPの設定ファイル)や追加設定の場所を表示します。Webサーバーの設定場所と一致する保証はありません。

FPMを使う場合は、利用している環境でPHP-FPMのバイナリ、稼働サービス、プール設定を確認します。パッケージによりコマンド名はphp-fpm、php-fpm8.4などと異なります。

7-2. Docker Composeの場合

以下はComposeのサービス名がphpの例です。名前は自分の設定に置き換えてください。

docker compose ps
docker compose exec php php -v
  • docker compose ps:Composeで管理しているコンテナの状態を一覧表示します。
  • docker compose exec php php -v:起動中のphpサービスのコンテナ内でphp -vを実行します。execは既存コンテナ内でコマンドを実行する操作です。

更新時は、まずDockerfileのベースイメージやパッケージが修正版のPHPを含むことを確認し、更新済みイメージをビルドして再デプロイします。単にdocker restartするだけでは、古いイメージやバイナリは修正されません。

代表的なコマンド例:

docker compose build --pull php
docker compose up -d --no-deps php
docker compose exec php php -v
  • build:Composeサービスのイメージを作り直します。
  • --pull:ビルド時に参照するベースイメージの更新を取得しようとします。ただし、固定タグが古いままなら安全になるとは限りません。
  • up -d:指定サービスをバックグラウンドで起動・更新します。
  • --no-deps:関連する他サービスを自動的に起動しません。
  • 最後のexecで、新しいコンテナ内のPHPバージョンを確認します。

**上記は一般例であり、ステートレスでないサービス、デプロイ方式、イメージ固定方法によって手順を調整する必要があります。**可能なら本番環境へ適用する前に検証環境で互換性確認を行います。

7-3. Linuxディストリビューションのパッケージの場合

Ubuntu/Debian系のapt、RHEL系のdnfなどで、OSの提供元がCVE-2026-109618とCVE-2026-109619の修正を含むと明記したパッケージへ更新します。

ディストリビューションがセキュリティ修正をバックポート(古いバージョン番号を維持しながら修正部分だけ移植)している場合、php -vだけで未修正と断定しないでください。

7-4. 更新完了の確認

  1. Webアプリが実際に使うPHPバイナリ・パッケージ・コンテナイメージを特定する。
  2. 修正版、または配布元が両CVEの修正を確認したパッケージを適用する。
  3. 全ワーカーを再起動し、古いPHPプロセスが残っていないことを確認する。
  4. アプリケーションの主要機能、アップロード、セッション、ログイン、バッチ連携などをテストする。
  5. 監視・エラーログを確認し、更新後の異常がないか見る。

7-5. 不審なアクセスを調べるポイント

Wiz Researchは、暫定的な調査シグナルとして次を挙げています。

  • 通常と異なるmultipartリクエストの直後にOOMエラーやHTTP 500が増える。
  • PHPSESSIDのCookie値が不自然に大きい、または形式が不自然になる。
  • PHPワーカープロセスが異常に終了・再起動を繰り返す。
  • 予期せぬ致命的エラーやメモリ不足が発生する。

これらが存在すれば侵害確定というわけではなく、存在しなければ安全というわけでもありません。ログは調査材料として保全し、構成確認・パッチ適用を優先します。

8. 今すぐ更新できない場合の「暫定対策」と限界

ここは誤解しやすい部分です。公式は「アップデート以外に完全な対策はない」と明言しています。設定変更をパッチの代わりにしてはいけません。

暫定対策 減らせるリスク 注意点
memory_limit=-1を管理者設定で強制 PHPの予測可能なメモリ上限到達によるOOM経路を減らす メモリ無制限化は可用性リスク。OSのメモリ確保失敗は防げない。監視とコンテナ/cgroup制限を慎重に検討
post_max_sizeを抑え、Webサーバー側でもリクエストを制限 大きな入力やmultipart悪用の一部を抑える 入力制限だけでは根絶できない
session.upload_progress.enabled=0 確認されたセッション進捗関連の経路を減らす 全ワーカー再起動が必要。他の経路には無効
PHP 8.5でfatal_error_backtraces=0 確認されたbacktrace関連の経路を減らす PHP 8.5の特定経路に対する限定策
リクエスト開始前に生じる警告・古いINI設定を除去 初期化前の古い内部状態を読む経路を減らす display_errors=Offなどで警告を隠しても防げない

**memory_limit=-1を安易に本番投入することは推奨しません。**PHPが使用するメモリの上限が外れ、メモリ枯渇やサービス停止を招くおそれがあるため、公式の条件と自環境の可用性・隔離設計を検討したうえで、あくまで緊急時の限定措置と考えてください。

外部公開サービスで更新ができない場合、リスク評価のうえ一時的なサービス停止や公開範囲の制限も検討対象です。パッチが可能なら、暫定設定を先に詰めるよりパッチの適用を優先します。

参考:PHP公式の緩和策

9. 2024年から存在した問題が、なぜ2026年に重大化したのか

PHP Foundationによると、最初のOOM問題の兆候は2024年の外部セキュリティ監査に関連する調査で発見されていました。しかし、当時はRCEにつながる具体的な経路が分からず、広い互換性影響や実装上のトレードオフを伴う修正方針が決められない状態が続きました。

2026年8月にはCalif、OpenAI、Wizなどが実際の攻撃につながる経路を報告。これによって深刻さが明確になり、修正が優先されました。

timeline
    title 問題の発見と修正の経緯
    2024年 : 外部監査を契機にOOM問題の兆候を把握
    2026年8月 : 複数研究チームが具体的なRCE経路を報告
    2026年10月20日 : 公開前にCVE情報が流通・PHPが事前告知
    2026年10月21日 : 4系統の修正版を公開

PHP Foundationは、修正までの長期化を意思決定プロセスと開発リソースの不足による構造的な失敗と認め、セキュリティ対応体制の改善を説明しています。

研究過程ではAI支援も使われました。ただし「AIだけで自動的に全部発見・修正された」という話ではありません。人間の研究者、PHP開発者、複数の組織による検証・修正・調整が行われています。

なお、2026年10月21日の公表時点でPHPセキュリティチームは実際に動作する公開PoCを確認していなかったとしています。一方で研究者が管理下の環境でRCEを実証した事実とは矛盾しません。「研究環境で再現済み」と「実世界で攻撃が観測済み」は別の情報です。

参考:PHP Foundationの説明

10. 開発者・運用担当者が持ち帰るべき教訓

今回の2件から学べるのは、PHP固有の話だけではありません。

第一に、例外的な終了処理も正常系と同じくらい重要です。メモリ不足・タイムアウト・致命的エラーといった処理が、初期化や後片付けを中断しても、破綻しない設計が必要です。

第二に、状態の寿命を越えた参照は危険です。リクエスト単位で寿命が終わるデータを、長生きするワーカーやキャッシュの状態から参照し続けてはいけません。

第三に、フレームワークの安全性とランタイムの安全性は別です。認証コード、依存ライブラリ、プラグインが最新でも、PHP本体が未修正であれば危険が残ります。

第四に、パッチ適用はバージョン番号だけでなく実際の実行環境まで追跡する必要があるということです。CLI、PHP-FPM、コンテナ、ホストOSで別々のPHPが使われている場合があります。

まとめ:PHP2Shellの本質は、異常終了時に「不完全な状態」や「寿命切れの参照」が残ることです。利用者の最優先作業は、攻撃の再現ではなく、実行中のPHPの特定・修正版への更新・全ワーカー再起動・監視の確認です。


一次情報・参考資料

  1. PHP公式:Security advisory(2026年10月21日) — 修正版・影響対象・暫定対策
  2. PHP Security Advisory:CVE-2026-109618 / GHSA-w8hw-w9fx-j79w — OOM、longjmp()、mod_php/FPMの条件、パッチ設計
  3. PHP Security Advisory:CVE-2026-109619 / GHSA-6wmf-4rv5-p3rp — Producer/Consumer、UAF、緩和策
  4. PHP Foundation:Today's PHP Security Release, and How We're Strengthening PHP Security — 発見経緯・セキュリティ対応体制
  5. Wiz Research:PHP2Shell — WordPress/Drupal/Joomlaでの研究上の実証、メモリ漏えい、内部経路
  6. Google Cloud:Responding to core PHP vulnerabilities — クラウド環境ごとの更新案内

追記方針:実際の悪用状況・追加修正版・ベンダーによるバックポートの提供状況は変化するため、運用時には必ず最新の公式告知を再確認してください。