PHP重大脆弱性は何が危険? php2shellについて図でわかりやすく解説
執筆・制作:何でも自作する人(運営者について)
PHP / PHP2Shell / 脆弱性 / セキュリティ
PHPの脆弱性「PHP2Shell」がニュースになっています。2026年10月21日にPHPが修正版を公開した、PHP本体の脆弱性2件の通称です。
- CVE-2026-109618:メモリが足りなくなったときの後始末に不備がある
- CVE-2026-109619:前のリクエストの記録が、次のリクエストまで残ってしまう
どちらも深刻度はCVSS v4で9.2(Critical)です。条件がそろうと、ログインなしで外からサーバー上のコマンドを実行される、つまりサーバーを乗っ取られるおそれがあります。
発見者には、Wiz Researchの研究者らに加えて、OpenAIの脆弱性発見AI「Codex Security」のチームも名を連ねています。
騒ぎが大きいのは、問題がPHPそのものにあるからです。WordPressやDrupalのようなアプリのコードに関係なく、古いPHPで動いていれば影響を受ける可能性があります。発見者によると、PHP 5.4以降のほぼすべてのバージョンが、2件のどちらかに当てはまります。セキュリティ企業Datadogの解説では、AWSが公開前の10月17日から悪用の試みを確認していたとされています。
以下、2件を「618」「619」と呼びます。
ざっくり言うと:台帳と棚がズレる
サーバーの中を、棚(メモリ)と台帳(どの棚に何があるかの記録)がある作業場だと考えてください。
通常の流れ(3ステップ)
- 準備:台帳に「通常の命令は棚1」と書き、棚1に通常の命令を入れる
- 使う:担当者が台帳を見て棚1を開け、命令を実行する
- 片付け:使い終わったら棚1を空け、台帳の記録も消す
どこでズレるか
- 618は①で壊れる:台帳だけ先に書いたところでメモリの上限に達して止まり、棚1には攻撃命令が残ったまま。②は飛ばして、③後片付けで読まれる
- 619は③で壊れる:棚1は空けたのに台帳の記録が残り、次の回のアクセスで攻撃命令がその棚に入る。次の回の②で読まれる
どちらの場合も、後で棚1を開ける担当者(618では後片付け担当、619では次に使う担当者)は、いつもどおり台帳を信じるだけです。中身がすり替わっていても気づけず、攻撃命令を実行してしまいます。
図で見る:通常の流れ
担当者は棚の中身を確かめません。台帳を信じて開けるだけです。台帳と中身が一致している限り、それで問題は起きません。
図で見る:618は①準備の途中で止まる
攻撃者は、フォームの送信に見せかけて「攻撃命令」と「大量データ」を一緒に送ります。大量データは、後の処理でメモリを大量に使わせるためのデータです。サーバーはこれをデータとして受け取るだけです。攻撃命令を置いた棚1は、使い終わると空き扱いになりますが、紙は入ったままです。
PHPには、1回のアクセスで使えるメモリの上限があります(設定名は memory_limit。128MBなどが一般的です)。サーバーは受け取ったデータを1件ずつ台帳に書き足していき、そのたびにメモリを少しずつ使います。
1件を書き足す手順は、台帳に「1件増えた(中身は棚1)」と先に書き、それから棚1の中身を書くという順番になっていました。中身を書くには、項目名を書くための用紙(メモリ)がもう少し必要です。
攻撃者は大量データの量を調整して、ちょうどこの「台帳に書いた直後、用紙を取る瞬間」にメモリの上限を超えるようにします。上限を超えたところで準備は中断され、棚1の中身は書き直されないまま残ります。
台帳は「通常の命令」、中身は攻撃命令のまま。準備の途中で止まったので、②使うには進みません。それでも、止まった後の後片付けだけは必ず動きます。後片付け担当が台帳を見て棚1を開け、攻撃命令を通常の命令として処理してしまいます。
つまり、大量データの役割は、メモリを上限ぎりぎりまで使わせて、中身を書く直前で止めさせることです。
図で見る:619は③片付けで台帳が残る
619は、2回分のアクセスにまたがる話です。まず前の回の③片付けで、棚1は空けたのに台帳の記録を消し忘れます。
次の回の①準備で、攻撃者のデータが届きます。受付は空いている棚1にそれを保管するだけで、この時点ではまだ何も起きません。
ところが次の回の②使うで担当者が台帳を見ると、「通常の命令は棚1」と書いてあります。棚1を開けると確かに紙が入っているので、それを通常の命令だと思って実行してしまいます。
実際のPHPでは
棚はメモリ、台帳は「どこに何があるか」を指すPHP内部の記録です。攻撃者が送るのは文章の命令ではなく、メモリに残る細工したデータです。それが内部の記録をたどって読まれると、次に呼ぶ処理の行き先などとして使われ、最終的にコマンドの実行までつながります。
- 618:PHPはフォームの入力を受け取って内部の一覧を作るとき、件数を先に増やしてから項目名の領域を確保していました。その確保がメモリ上限で失敗すると、中身が書かれていない項目が「1件」として数えられたまま残ります。リクエストの最後の後片付けでその項目が読まれ、前に残っていたデータが使われます。
- 619:同じPHPのプロセス(ワーカー)が続けて次のリクエストを処理するとき、前のリクエストのメモリを解放しても、そこを指す記録が残ることがあります。次のリクエストの準備が整う前にその記録が使われると、解放後のメモリを読んでしまいます。
どちらも、メモリ上のデータの配置やサーバーの構成といった成立条件があり、大量データを送れば必ず成功するわけではありません。それでも公開前から悪用の試みが報告されているので、条件を見積もるより先に更新するのが安全です。
対策:修正版に更新する
2026年10月22日時点で、修正版は次のとおりです。
| 系列 | 修正版 |
|---|---|
| PHP 8.2 | 8.2.35 以降 |
| PHP 8.3 | 8.3.36 以降 |
| PHP 8.4 | 8.4.27 以降 |
| PHP 8.5 | 8.5.12 以降 |
PHP公式は、修正版への更新だけが確実な対策だとしています。設定による緩和策(619の session.upload_progress.enabled=0 など)もありますが、影響を減らすだけです。
Apacheのモジュール、PHP-FPM、FrankenPHPなど、HTTPを受けるPHPはすべて対象です。コンテナや、PHPを同梱しているアプリの中のPHPも忘れずに確認してください。