新規のお客様新規アカウントで DNS と CDN の無料プランをご利用いただけます。アカウント作成

PHP2Shell(CVE-2026-109618/109619):エッジでの仮想パッチ対応

2 件の重大な PHP アドバイザリにより、OOM bailout と古いグローバル状態を通じて認証なしの RCE が可能になります。脆弱性の仕組み、Yeap WAF が初日からエッジでブロックしているもの、そしてオリジン側で引き続き修正が必要なことを解説します。

2026 年 10 月 21 日、PHP プロジェクトは 2 件の Critical なセキュリティアドバイザリを同時に公開しました。GHSA-w8hw-w9fx-j79w(CVE-2026-109618)と GHSA-6wmf-4rv5-p3rp(CVE-2026-109619)で、CVSS v4 のスコアはいずれも 9.2 です。どちらも認証なしでのリモートコード実行を可能にします。この 2 件は PHP2Shell と呼ばれるようになりました。

サポート中の 4 つのブランチ(PHP 8.2、8.3、8.4、8.5)すべてが影響を受け、PHP チームは mod_php がデフォルト設定のままで悪用可能であることを確認しています。WordPress や Laravel をはじめとする PHP アプリケーションを動かしているサイトでは、匿名の POST リクエスト 1 回でサーバーを乗っ取られるおそれがあります。

アドバイザリが公開された当日、私たちは公開済みの攻撃経路に対する仮想パッチをすべての Yeap CDN エッジノードに配信しました。本記事では、脆弱性の仕組み、私たちのルールが防げるもの・防げないもの、そしてオリジン側で引き続き対応が必要なことを解説します。

PHP2Shell の攻撃チェーン:初期化途中のメモリが信頼されたまま残る OOM bailout と、リクエストをまたいで use-after-free として再利用される古いグローバル状態
どちらのバグも単一の拡張機能ではなく、Zend エンジンのリクエストライフサイクルに存在します。

脆弱性の概要

CVE-2026-109618:OOM bailout によるメモリ破壊

PHP はメモリ不足になると longjmp() を呼び出して現在のリクエストを中断します。一部の箇所では、リクエスト用のメモリが完全に初期化される前にすでに参照可能な状態になっています。ちょうどそのタイミングでメモリ確保に失敗すると、早期終了によって不正なメモリが残り、後のリクエスト終了処理がそれを読み取って分岐に使ってしまいます。

攻撃者は、不正な形式の multipart/form-data リクエストを使えば、PHP の通常の制限内で OOM を確実に引き起こせます。具体的には、有効なコンテンツ長を持たないリクエスト、膨大な数のヘッダーを持つパート、max_file_uploads をすり抜ける空のファイルフィールド、深くネストしたフィールド名などです。公式の修正では、OOM 時の longjmp() が完全に廃止されました。PHP は次の安全なポイントで正常に終了するようになり、メモリ使用量がより高いしきい値を超えた場合は即座に _exit() を呼び出します。

CVE-2026-109619:リクエストをまたいで残る古いグローバル状態

リクエストに属するグローバル状態(エラー、セッション、オブジェクト、拡張機能の状態)が、そのリクエストのメモリが解放された後もワーカープロセス内に残ることがあります。一部のコードは次のリクエストが正しく初期化される前にこの状態を読み取るため、解放済みメモリを指すポインタをたどってしまいます。これが use-after-free です。

  • プロデューサー(古い状態を残すリクエスト):致命的エラー、実行タイムアウト、require の失敗、メモリ不足、PHP 8.5 の fatal_error_backtraces。
  • コンシューマー(その状態を読み取るリクエスト):リクエスト開始前に適用される INI 診断、および session.upload_progress の処理(デフォルトで有効)。

プロデューサーとコンシューマーが同じワーカーに割り当てられると、攻撃者はヒープを整形して use-after-free をコード実行につなげることができます。PHP チームは、アップロード進捗の経路が mod_php と PHP-FPM の両方で悪用可能であることを検証しています。

影響を受けるバージョン

ブランチ影響範囲修正バージョン
PHP 8.5< 8.5.128.5.12
PHP 8.4< 8.4.278.4.27
PHP 8.3< 8.3.368.3.36
PHP 8.2< 8.2.358.2.35

mod_php は悪用可能であることが確認されています。ソースコードの分析から、FPM、FrankenPHP、LSAPI も影響を受けると考えられます。サポート終了(EOL)バージョンには公式パッチが提供されないため、その前段での防御がいっそう重要になります。

WAF で防げること

公開されているエクスプロイトは、本質的には認証なしの multipart POST リクエスト 1 回です。アップロード進捗ハンドラに到達するための PHP_SESSION_UPLOAD_PROGRESS フィールドとファイルアップロードに加え、ヒープをスプレーして解放済みメモリを再取得するための大量のフォームフィールドを含んでいます。この形は HTTP レイヤーで見えるため、リクエストが PHP に届く前に止めることができます。

私たちは Yeap WAF の OWASP CRS エンジンに、独立した仮想パッチルール群を追加しました。このエクスプロイトが依存する「アップロード進捗のトリガー + ヒープスプレー」の組み合わせを検知し、PHP がリクエストを解析する前に 403 を返します。

  • 単独でブロックします。異常スコアリングに依存しないため、サイトのパラノイアレベルやスコアしきい値の影響を受けません。
  • 固定のシグネチャではなく挙動を見ます。フィールド名の変更やペイロードの形の変更では回避できません。
  • 正規のアップロードには影響しません。進捗バー付きのものを含め、正規のアップロードフォームはそのまま動作します。
  • エッジで動作します。オリジンに何かをインストールする必要も、設定を 1 行変更する必要もありません。
Yeap CDN エッジの防御レイヤー:TLS 終端とオリジン秘匿、PHP2Shell 仮想パッチ、OWASP CRS マネージドルール、レート制限とブロックリスト
攻撃の試みはエッジで止まり、通常のトラフィックはそのままオリジンへ届きます。

アドバイザリ公開から全ノードの防御まで

タイムライン:10 月 21 日にアドバイザリ公開、同日 23:34 にルールを検証して全エッジノードで適用、10 月 22 日に OWASP CRS v4.30.0 バンドルとともに配信

リリース前に、攻撃トラフィックの各パターンと複数種類の正規アップロードに対してルールを検証しました。攻撃パターンはすべてブロックされ、正規のアップロードはすべて通過しました。その後、ルールはプラグインとして CRS バンドルにコンパイルされ、10 月 21 日 23:34(UTC+8)に全エッジノードで適用されました。10 月 22 日未明にはバンドル全体を OWASP CRS v4.30.0 にアップグレードし、仮想パッチもそれとともに配信されています。

このスピードを支えているのが Yeap WAF の更新パイプラインです。新しいバンドルはバックグラウンドでコンパイル・検証された後、カナリアノードに配信され、そこで負荷状況とブロック率を監視します。正常と判断されたバンドルだけが全体に展開されます。エッジノードはルールをアトミックに切り替えます。新しいルールの準備が整うまで古いルールが動き続けるため、防御に空白が生じることはなく、ワンクリックで直前の正常なバージョンにロールバックすることもできます。

WAF はパッチではない:引き続き必要な対応

はっきりさせておくと、WAF が止められるのは攻撃のうちHTTP リクエストボディに含まれる部分だけです。CVE-2026-109619 には、PHP 8.5 の fatal_error_backtraces や起動時の INI 警告のように multipart とまったく関係のないプロデューサーやコンシューマーも存在し、解放と使用をそれぞれまったく正常に見える 2 つのリクエストに分けることもできます。これらは PHP エンジン内部でしか修正できません。また、slow POST のようなタイミングベースの攻撃は、コンテンツを検査する WAF からは見えません。

エッジのルールは時間を稼ぐための第一の防御線と位置づけ、できるだけ早く次の対応を進めてください。

  1. PHP をアップグレードする:8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 以降へ。アドバイザリによれば、これが唯一の完全な修正です。
  2. アップグレード前に php.ini を強化する:変更後、すべての PHP ワーカーを再起動してください。
    ; アップロード進捗バーを使っていない場合は無効にする
    ; (アドバイザリはパッチ適用後も無効のままにすることを推奨)
    session.upload_progress.enabled = 0
    
    ; PHP 8.5:確認済みの RCE プロデューサーを取り除く
    fatal_error_backtraces = 0
    
    ; ボディと入力の制限を控えめに設定する
    post_max_size = 8M
    max_input_vars = 1000
    max_input_nesting_level = 16
    
  3. PHP-FPM では pm.max_requests = 1 を検討する:各ワーカーが 1 リクエストごとに再生成され、リクエストをまたいだ再利用を断ち切れます。パフォーマンスが低下するため、短期的な緊急措置として使ってください。
  4. アップロードが不要な箇所では multipart を拒否する:Yeap CDN コンソールのファイアウォール → カスタムルールで、「メソッドが POST」「リクエストヘッダー Content-Type に multipart/form-data を含む」と対象パスを組み合わせたブロックルールを作成し、実際にアップロードを受け付けるエンドポイントだけを許可します。
  5. POST リクエストにレート制限をかける:スキャンや繰り返しの探索を遅らせることができます。

環境を確認するには:

php -v
php -i | grep -E "session.upload_progress.enabled|fatal_error_backtraces"

CLI と Web サーバーでは読み込む php.ini が異なる場合があります。最も確実なのは、サイト上に一時的な phpinfo() ページを置いて確認する方法です。確認したらすぐに削除してください。

Yeap CDN と WAF で PHP サイトを守る

PHP2Shell が PHP の最後の重大なバグになることはないでしょう。アドバイザリの公開からすべてのサーバーにパッチが適用されるまでには必ず空白期間があり、攻撃者はたいてい数時間以内にスキャンを始めます。サイトを Yeap CDN の背後に置けば、その空白期間を私たちがエッジで守ります。

  • 常に最新のマネージドルール:SQL インジェクション、XSS、リモートコマンド実行、PHP インジェクション、Web シェルなど 14 カテゴリにわたる OWASP CRS v4 の完全なルールセット。個々のルールを調整するのではなくカテゴリ単位でオン・オフでき、上流の新リリースは自動でビルド、カナリア検証、展開されます。
  • 重大なバグへの仮想パッチ:今回のように、アドバイザリ公開当日に対象を絞ったルールがエッジで適用され、オリジン側の変更は一切不要です。
  • オリジンの秘匿:攻撃者が到達できるのはエッジだけです。オリジンへのアクセスを CDN からのトラフィックだけに限定すれば、直接攻撃される範囲はほぼゼロになります。
  • カスタムルールとレート制限:IP、国、パス、ヘッダー、メソッドを組み合わせて、トラフィックをブロック、チャレンジ、またはレート制限できます。
  • 無料の HTTPS とグローバル高速化:証明書は自動で発行・更新され、複数リージョンのエッジノードが訪問者の近くで応答します。

導入は数分で完了します。ドメインを追加し、その DNS レコードを Yeap CDN が発行する CNAME に向け、ドメインの WAF 設定で保護を有効にするだけです。手順の詳細は 10 Minutes で Web サイトに Yeap CDN と無料の HTTPS を導入する を、製品の詳細は Yeap CDN と セキュリティ をご覧ください。

OWASP CRS マネージドルールエンジンは、ドメインごとに順次有効化を進めています。この PHP2Shell 仮想パッチをすぐにサイトへ適用したい場合は、コンソールからサポートチケットを作成してください。お客様のドメインで有効化いたします。

参考資料

Yeap Cloud で開発を始める

新規アカウントで DNS と CDN の無料プランをご利用いただけます。

無料登録