不正なアップロードが1件あるだけで秘密情報が漏えいする可能性のあるサーバーは何台ありますか?
PHP2Shellは、Webリクエストを処理するPHP環境で、認証されていない攻撃者がリモートからコードを実行できる2つの重大な脆弱性です。PHPは世界中のWebサイトの約70%で稼働しており、これらのバグはアプリケーションコードが実行される前に発生するため、完全にパッチが適用されたWordPress、Drupal、Joomlaのサイトであっても、その基盤となるPHPが更新されるまでは危険にさらされており、攻撃者はすでにこの脆弱性を探っています。
OpenAIの「Patch the Planet」イニシアチブのもとで活動するセキュリティ研究企業Califは、8月1日に最初にこのバグを発見し、8月11日にPHPに報告しました。Wiz Researchも別途発見しました。
これまでに判明していること
- PHP自体に2つの重大な脆弱性があります。CVE-2026-109618とCVE-2026-109619はコアエンジンに存在するため、安全なアプリケーションコードであっても、脆弱なサーバーを保護することはできません。認証されていないリモートの攻撃者は、PHPワーカーのメモリ(パスワード、トークン、APIキー)を読み取ることができ、場合によってはサーバー上でコードを実行できます。
- 広範囲に及びます。PHP 5.4までさかのぼるすべてのバージョンが、PHP-FPM、Apache modphp、php-cgi、FrankenPHP、LiteSpeedにおいて影響を受けており、その中でもmodphpのリスクが最も高くなっています。
- 実際のプラットフォームで実証済みです。 最新のWordPress、Drupal、Joomlaのコンテナイメージに対するリモートコード実行が実証されました。 "Hello World"のPHPアプリでさえ、脆弱性を持つ可能性があります。
- 修正パッチは公開されましたが、脆弱性報告が先行しました。10月21日に修正済みリリースが公開されました(PHP 8.5.12、8.4.27、8.3.36、8.2.35)。PHP 8.1以前には公式リリースがありません。
これが重要な理由
ほとんどのチームは、これを現在は修正済みの2件のPHPバグとして記録するでしょう。その背景にあるエクスポージャーはそれよりも大規模なものです。
- 脆弱性はアプリケーションの下層に存在します。CVE-2026-109618およびCVE-2026-109619はPHPランタイム内に存在するため、その上にあるフレームワーク、CMS、カスタムコードは関係ありません。アプリケーションコードが実行される前に悪用される可能性があるため、アプリケーションレベルのログにはほとんど、またはまったく記録されない場合があります。
- アプリケーション側のミスは必要ありません。この攻撃は、脆弱なプラグインや弱いログインフロー、ずさんなセッション管理に依存しません。コードが適切に記述されたサイトでも、基盤となるPHPが更新されていなければ、脆弱な状態にさらされる可能性があります。
- 認証なしで悪用される可能性があります。 攻撃者に必要なのは、サーバーにリクエストを送信できることだけで、アカウントもパスワードも足掛かりも必要ありません。
- エクスプロイトを構築するコストは低下しています。研究者らは、AIモデルによって、かつて専門家が数カ月かけて行っていた作業が数日で済むようになったと報告しています。10年前のバグの種類のうち、かつて武器化は現実的ではないと考えられていたものが、今では手の届く範囲にあり、攻撃者も同じツールを利用できます。
PHPはウェブの広範な部分で稼働しており、WordPressやDrupalから、何年も誰も手をつけていない社内ツールに至るまで使われています。問題は、PHPを使っているかどうかではなく、どこで使われているのか、そしてすべてのインスタンスを見つけられるかどうかです。
今週取るべき対応
検出
- セットアップフェーズに注目してください。 不正または異常なマルチパートアップロードリクエストと、それに続くメモリ制限エラーやHTTP 500レスポンスの急増が、探すべきパターンです。
- 送信Cookieを検査します。 応答に含まれるPHPSESSIDが大きすぎる、または形式が不正な場合、プロセスメモリがクライアントに漏れ出している可能性があります。
- アップロード処理中のプロセス生成にフラグを設定してください。 PHPワーカーは、アプリケーションコードが実行される前のアップロード解析中に、子プロセスを決して生成するべきではありません。 信頼度の高いシグナルとして扱ってください。
これらのシグナルは調査の指針となります。 パッチ適用の代わりにはなりません。
修正
- すべてのPHPインストールをインベントリ化します。一般的なWebアプリだけでなく、さらに範囲を広げて確認してください。バンドルソフトウェア、アプライアンス、忘れられた社内ツールもすべて対象です。
- インターネットに公開されているシステムに、まずパッチを適用してください。 ブランチに応じて、PHP 8.5.12、8.4.27、8.3.36、または8.2.35に移行してください。
- 外部に公開されたサーバー上のシークレットをローテーションしてください。 パッチを適用する前に攻撃者がワーカーのメモリを読み取れた可能性がある場合、そのメモリ内に存在する認証情報、トークン、APIキーは侵害されたものとみなしてください。
メモリ不足によるクラッシュがどのようにコード実行につながるのか知りたいですか?以下をお読みください。
PHP2Shellについて判明している事実の詳説
PHPの情報公開では、4つのバグが説明されており、2つのCVEとして分類されています。これらを合わせてPHP2Shellと呼びます。最初の3つには共通する「前段階」があり、攻撃を成功させる準備を整え、最終的な標的であるRCE(リモートコード実行)を実質的に可能にします。4つ目はより内部的なプロセスであり、どのように悪用されたかは公表されていません。
それらがサーバーにどのような影響を与えるかを確認するには、まずいくつかの概念を押さえておく必要があります。
PHPの概要を知る短い用語集
- PHP Webサーバーワーカー。 HTTPリクエストを処理し、リクエストごとに再起動するのではなく、後続のリクエストにも再利用されるプロセスです。
- マルチパートリクエストの解析。PHPが受信したmultipart/form-dataリクエスト(ファイルアップロード)を読み取り、ラベル付きのパートに分割すること。これはリクエストのごく早い段階で開始されます。
- セッションのアップロード進捗処理。ファイルのアップロード進捗を追跡し、ページにプログレスバーを表示できるようにするPHPの機能。サーバーがマルチパートリクエストを解析している間に実行されます。
- セッション。PHPがリクエスト間でユーザーを記憶する仕組み(PHPSESSIDクッキー)。
- メモリ制限。 単一のリクエストで使用できる最大メモリ量。
- エンジングローバルポインター。PHPエンジンがグローバルな実行状態を追跡するために使用する内部管理変数。ここでは次の3つが重要になります。
- PS(id):セッションID。
- BG(unserialize):アンシリアライザーの状態(シリアル化された文字列から元の配列またはオブジェクトを再構築する状態)。
- EG(last_fatal_error_backtrace): 最後のエラーに関する情報を格納します。
- ワーカーのクリーンアップ。PHPでは、通常のティアダウン処理が再利用されるワーカー上でリクエスト間に実行され、エンジン全体のポインターをリセットして、次のリクエストがクリーンな状態で開始されることになっています。このバグの連鎖が発生するのは、メモリ不足による致命的エラーが、このクリーンアップが実行される前にリクエストを中断し、それらのポインターが古い状態のまま残るためです。
- OPcache. すべてのワーカーが読み取れる、プリコンパイル済みスクリプトのバイトコードを保持する共有メモリで、同じPHPファイルを再読み込みして再コンパイルする必要がなくなります。
用語について理解できたところで、これらすべてを稼働する部分に進みましょう。
事前フェーズ
悪用されている中心的な概念は、解放後使用(UAF)です。メモリが解放された後もそのメモリへのポインタが保持され、後に使用されるという脆弱性です。攻撃者によって解放済みメモリに新しいデータが配置されると、PHPは攻撃者のバイト列を自身の信頼できる構造として扱います。
攻撃者が細工したマルチパートアップロードを送信すると、リクエストの早い段階でセッションのアップロード進捗処理中にPHPがメモリ不足に陥ります。リクエストが予期せず終了し、ワーカーのクリーンアップがスキップされ、古いポインターが残ります。その結果、メモリの漏えいや破損につながる可能性があります。
攻撃者のフロー:
- 武装リクエストを作成します。攻撃者は、解析が続いている間にメモリを使い果たすほどのサイズのセッションアップロード進捗データを含むmultipart/form-dataアップロードを送信します。
- アプリケーション解析前の早期処理。 アップロード進行状況の処理は、通常のPHP起動が完了する前に実行されるため、エンジンのグローバルポインターは早期に設定されます。
- メモリ不足による致命的なエラー。リクエストがmemory_limitを超えたため、PHPはリクエストを中止し、リクエストヒープを解放します。
- クリーンアップはスキップされます。 ポインターは決してリセットされません。
- ワーカーは存続し、再利用されます。 同じワーカーが、古いポインターを保持したまま次のリクエストを処理します。
- メモリ使用量。攻撃者のデータが、解放された割り当ての1つを再利用します(各ポインターには個別の割り当てがあります)。
これは、最初の3つのバグの前提となる設定です。
バグ1:リモートプロセスメモリの漏えい
前段階の後、同じワーカー上の別のマルチパートリクエストも、解放されたPS(id)の割り当てを引き続き信頼します。PHPは再利用されたバイト列を有効なセッションIDとして読み取り、URLエンコードしてPHPSESSIDレスポンスCookieに格納します。攻撃者はCookieを読み取るだけで、漏えいしたワーカーのメモリを取得できます。これによりアドレスの不確実性を解消でき、次のステップであるRCEを現実的なものにできます。
バグ2:アンシリアライザーの解放後使用
ここでは、BGの解放済み割り当て(アンシリアライズ)がメモリ破壊を引き起こすものとして信頼されています。PHPは攻撃者によって再利用されたメモリを有効なメモリであるかのように扱い続けて「アンシリアライズ」を行い、メモリ破壊プリミティブとなります。バグ1の漏えいと組み合わせることで、このバグはアドレスの不確実性を悪用して未認証のリモートコード実行を実現し、WordPress、Drupal、Joomlaで実証されました。
バグ3:致命的エラーのバックトレースにおける解放後使用
EG(last_fatal_error_backtrace)における、もう1つのポインターの不正利用です。クリーンアップの失敗後に同じワーカーが診断イベント(その後のエラーや警告など)を送信した場合、状態が再初期化される前に解放されたポインターにアクセスする可能性があります。PHPはバックトレースのポインターを更新または破棄し、攻撃者によって再要求(再確保)されたメモリーをたどります。この解放後使用は、RCE(リモートコード実行)につながるメモリ破損へと発展する可能性があります。
バグ 4:OPcacheの型の混同(ワーカー間にまたがる独立したフロー)
Wizの開示では、このバグに関する具体的な要求や手順は説明されておらず、内部メカニズムの欠陥のみが述べられています。つまり、要約すると以下の通りです。
- 共有コンパイル済みコードキャッシュ。OPcacheはコンパイル済みのバイトコードを共有メモリに格納するため、各ワーカーは再コンパイルの代わりにそれを再利用できます(php.net: 「PHPがリクエストごとにスクリプトをロードして解析する必要がなくなります」)。
- 2つのトラッカー。プロセスローカルのトラッカー(CG(map_ptr_last))と共有トラッカー(ZCSG(map_ptr_last))は、共有メモリがどこまで埋まったかを追跡します。
- 更新途中での割り当て失敗。2つのトラッカーの更新の間に割り当てに失敗すると、共有トラッカーが書き込まれた内容と同期しなくなる可能性があります。
- ワーカー間での再利用。Wizによると、これにより別のワーカーがそのメモリスロットを再利用できる可能性があり、その結果、誤った型のポインターに解決されます。これにより型の混同が発生して共有OPcacheの状態が破損し、後にRCEに利用されました。これがどのように、またいつ発生するのかは明記されていないため、正確な手順ではなく、このバグの大まかな概要として捉えてください。
Varonis製品の影響
Varonisは、製品ポートフォリオ全体におけるPHP2Shellの潜在的な影響を事前に評価し、必要な修正措置を完了しました。 これらの脆弱性に関連する侵害の証拠は確認されていません。お客様がVaronis製品に関して追加の対応を行う必要はありません。
リファレンス
Varonisがお手伝いできること
Varonisは、不審な侵入後の横展開や異常なデータアクセスを検知し、侵害されたアカウントを自動的に封じ込めることで、PHP2Shellをはじめとする脅威への対処を支援し、機密データを保護します。
マネージドデータ検知&対応(MDDR)は、パッチ適用に加えて保護レイヤーを強化するソリューションです。Varonisのエキスパートが24時間365日体制でデータアクティビティを監視し、機密データにアクセスする盗難された資格情報などの脅威を調査して、被害を食い止めるための対策を講じます。お客様は、自社で24x7体制のチームを配置することなく、専門家による24時間体制のサポートを受けられます。
サマリー
PHP2Shellは、メモリ不足に陥ったリクエストがクリーンアップ処理を行わないという、地味な出来事から生まれた一連の攻撃チェーンです。その1つの古いポインターを起点に、研究者たちはメモリ漏えい、2種類の解放後使用、さらに別個のOPcache型混同を発見しました。これらはいずれもアプリケーション層より下位にあり、PHP 5.4から存在しています。
注:このブログはAI翻訳され、当社日本チームによってレビューされました