本文へスキップ
タンヴィル・アハメド

フルスタック開発

php2shellへの対応: CVE-2026-109618と-109619に対するPHPの緊急修正

php2shellの経緯と影響範囲を整理し、環境ごとにPHPを8.5.12、8.4.27、8.3.36、8.2.35へ更新して修正を確認する手順を解説します。

著者:タンヴィル・アハメド・公開 ・約17分で読めます

Read this article in English

2026年10月21日、PHPプロジェクトは2件の脆弱性に対する緊急リリースを公開しました。どちらも、HTTPでPHPを配信しているだけのサーバー上で、認証されていない攻撃者がコードを実行できてしまうものです。CVE-2026-109618とCVE-2026-109619の2件で、php2shellという通称で呼ばれています。問題はフレームワークや拡張機能ではなくPHPエンジン自体にあるため、アプリケーションのコードに脆弱性がなくてもサーバーは危険にさらされます。この記事では、何が起きたのか、なぜ重要なのかを説明したうえで、Debian・Ubuntu、RHEL系、Alpine、Docker、Homebrew、マネージドプラットフォーム向けのパッチ適用手順と、すぐにはパッチを当てられないホスト向けの暫定対策を紹介します。

php2shellとは

PHPのセキュリティアドバイザリには2件のバグが掲載されており、それぞれに個別のGitHubアドバイザリがあります。

  • CVE-2026-109618、メモリ不足時の破損によるRCE(GHSA-w8hw-w9fx-j79w)。リクエストの内部データが一部しか初期化されていない状態でPHPがメモリ不足に陥ると、その後のクリーンアップ処理が初期化途中のデータを有効なものとして扱う可能性があります。
  • CVE-2026-109619、古いグローバル状態によるRCE(GHSA-6wmf-4rv5-p3rp)。あるリクエストが所有する状態が、致命的エラーの後などにメモリが解放された後も残り、同じワーカー上の後続リクエストがそれを使ってしまう可能性があります。いわゆるuse-after-freeで、PHPはメモリの情報漏えいも確認済みの影響として挙げています(Tenable)。

PHPは両方をCVSS v4.0で9.2と評価しています。リリース前に流出した識別子に付けられていた10.0というスコアはPHPによる評価ではなく、PHP Foundationはこれを誤りだとしています。

2件目のバグを見ると、長時間稼働するワーカーモデルがなぜ問題になるのかがわかります。PHP-FPM、mod_php、FrankenPHPは1つのプロセスを多数のリクエストにまたがって生かし続けるため、攻撃者の最初のリクエストが残した状態が、同じワーカーに2回目のリクエストが届いた時点でまだ残っている可能性があります。

公開までの経緯

リリースは当初10月22日の予定でした。公開されているタイムラインをまとめたTenableのFAQによると、CVE識別子が出回り始めたことで1日前倒しになりました。

日付(2026年)出来事
10月17日公開の4日前、Amazon Threat IntelligenceがMadPotハニーポットに対する攻撃の試みを観測。攻撃者がどこから詳細を入手したのかはAmazonも把握していない。
10月20日CVE識別子が公に流出。PHPが翌日のリリースを告知。CERT-EUがアドバイザリ2026-018を公開。
10月21日PHPがアドバイザリとともに8.5.12、8.4.27、8.3.36、8.2.35をリリース。CloudflareがWAFルールを展開。CISAが即時のパッチ適用を促すアラートを公開。
10月22日パッチ適用済みの公式DockerイメージがDocker Hubに登場。

この研究はCalif.ioのLCFRによるもので、そのエクスプロイトをKirill Firsovがphp2shellという名前で動画で公開しました。PHP FoundationはほかにもOpenAIやWizなどの名前を挙げて謝意を示しており、CalifとWizはどちらも研究にAIモデルを使ったと述べています(Tenable)。

実際に悪用されているのか

報告は食い違っており、率直にまとめるなら「悪用されている前提で動く」のが妥当です。CISAは公開時点では悪用を観測していないとしていました。一方、10月21日に複数とみられる攻撃者からのさらなる試みを観測したAmazonは、php2shellを「現在、実環境で広く悪用されている」と説明し、10月17日までさかのぼって活動を調査するよう推奨しています。CloudflareのCTOは初期の攻撃を報告し、watchTowrは両方のバグを再現でき、それらに対する攻撃の試みも観測していると述べています。実際のシステムに対する試みが成功したかどうかは確認されていません。

これらのバグを悪用できるとうたうコードが出回っていますが、いずれも検証されておらず、PHP Foundationはリリース時点の調査で機能するエクスプロイトは見つからなかったとしています。大切なマシンで未検証のPoCを実行してはいけません。マルウェアが含まれている可能性があります。

影響を受ける環境

CERT-EUによると、Webリクエストを処理するすべてのPHPインストールが対象です。具体的にはPHP-FPM、Apacheとmod_php、php-cgi、FrankenPHP、LiteSpeedです。PHPはmod_phpを最もリスクが高いと評価しています。アプリケーションのコードが実行される前にエクスプロイトが発動しうるためです。Web入力を受け取らない定期実行スクリプトを動かしているだけのCLIは主な懸念対象ではありませんが、同じパッケージ更新で修正されます。

ブランチ状況
8.58.5.12で修正
8.48.4.27で修正
8.38.3.36で修正
8.28.2.35で修正(2026年12月31日にサポート終了)
7.4〜8.1公式リリースなし。ソースへの最小限のバックポートのみ
5.4〜7.3サポート終了。PHPプロジェクトからの修正なし

影響を受ける最も古いコードはPHP 5.4.0までさかのぼります。8.1以前で動いていて、すぐにはサポート中のブランチへ移行できないサーバーについて、CISAの助言は明快です。パッチ適用またはアップグレードが済むまでオフラインにしてください。

直接インストールしていないPHPも忘れないようにしてください。CISAは他のソフトウェアに組み込まれたPHPを棚卸しするよう求めており、CERT-EUは影響を受けるサードパーティ製品を特定する手段としてNCSC-NLのphp2shellリポジトリを挙げています。フレームワークのアップグレードも対策になりません。新規インストールしたLaravel 13.36も、その下で動くPHPバイナリとまったく同じだけ危険にさらされています。

ステップ1: 実際にトラフィックを処理しているPHPを特定する

php -vが表示するのはCLIのバイナリで、Qualysの警告によるとWebリクエストを処理しているものとは別のインストールである可能性があります。まずはホスト上のすべてのPHPランタイムを洗い出します。

# CLI(HTTPを処理しているものとは限らない)
php -v
 
# 常駐しているPHPサーバーとその背後のバイナリ
ps -eo pid,lstart,args | grep -E 'php-fpm: master|php-cgi|frankenphp|lsphp' | grep -v grep
 
# Apacheとmod_php
apachectl -M 2>/dev/null | grep -i php
 
# インストール済みのPHPパッケージ(Debian/Ubuntu、次にRHEL系)
dpkg-query -W -f='${db:Status-Abbrev}${Package} ${Version}\n' 'php*' 2>/dev/null | awk '/^ii/{print $2, $3}'
rpm -qa 'php*' 2>/dev/null

lstart列は後で重要になります。アップグレード前に起動したワーカーは、まだ脆弱なコードを実行しているからです。

ディストリビューションのバージョン番号は古く見えることがある

Linuxディストリビューションや長期サポートを提供するベンダーは事前にパッチを受け取っており、多くは新しいアップストリームのバージョンに上げずに修正をバックポートしています。DebianやRHELのパッケージは、修正が完全に適用されていても、たとえば8.2.35未満の8.2.xと表示されることがあります。QualysはベンダーのアドバイザリとしてUbuntu USN-8948-1とUSN-8948-2、RHSA-2026:87070シリーズ、Amazon Linux ALAS2023-2026-3301〜3304、SUSE、Debian、Alpine 3.24、Arch、Gentoo、Fedoraを挙げています。バージョン文字列を信用せず、パッケージのchangelogでCVEを確認してください。

# Debian / Ubuntu
apt-get changelog php8.3-fpm 2>/dev/null | grep -E 'CVE-2026-10961[89]'
 
# RHEL、Rocky、Alma、Amazon Linux、Fedora
rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]'

両方のCVE IDが表示されれば、そのパッケージには修正が含まれています。

ステップ2: プラットフォームごとのパッチ適用

DebianとUbuntu

Ubuntuのアップデートは再起動を必要としませんが、PHPを読み込むすべてのサービスを再起動する必要があります(Qualys)。次のコマンドは、すでにインストールされているPHPパッケージだけをアップグレードします。

sudo apt-get update
apt list --upgradable 2>/dev/null | grep -i php
sudo apt-get install --only-upgrade $(dpkg-query -W -f='${db:Status-Abbrev}${Package}\n' 'php*' 'libapache2-mod-php*' 2>/dev/null | awk '/^ii/{print $2}')

PHPをディストリビューションではなくppa:ondrej/phpリポジトリから入れている場合は、apt-cache policy php8.4-fpm(使用中のブランチに合わせて)を実行し、候補のバージョンが修正済みのアップストリームリリースであることを確認してから次に進んでください。

RHEL、Rocky、AlmaLinux、Amazon Linux、Fedora

sudo dnf upgrade --refresh 'php*'
rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]'

CERT-EUが指摘している注意点が1つあります。Remiリポジトリ(rpms.remirepo.net)には、リリース当日の時点でアップデートがありませんでした。PHPをRemiから入れていてdnfが何も提示しない場合は、待つのではなく、ディストリビューションのパッケージに切り替えるかソースからビルドしてください。

Alpine

Alpine 3.24では修正済みのphp83、php84、php85パッケージが提供されています。

apk update
apk upgrade
apk info -v | grep -E '^php8'

Docker公式イメージ

公式のphpイメージは10月22日に再ビルドされました。古いキャッシュから以前のイメージが使われないよう、パッチ適用済みの正確なバージョンを固定します。

FROM php:8.4.27-fpm-alpine

修正済みのすべてのブランチとバリアントに同等のタグがあり、たとえば8.5.12-fpm、8.3.36-apache、8.2.35-fpm-bookwormなどです。同じバージョン番号に一致するRC1タグはリリース候補なので使わないでください。そのうえでレイヤーキャッシュを使わずに再ビルドし、結果を確認します。

docker build --pull --no-cache -t myapp:php-8.4.27 .
docker run --rm myapp:php-8.4.27 php -v

再ビルドだけでは不十分です。デプロイし直し、古いイメージを使っているコンテナが稼働していないことを確認してください。

docker ps --format '{{.Names}}  {{.Image}}  {{.RunningFor}}'

Homebrew

Homebrewではすでにphp 8.5.12、php@8.4 8.4.27、php@8.3 8.3.36、php@8.2 8.2.35が提供されています。インストール済みのformulaを指定してください。

brew update
brew upgrade php@8.4
brew services restart php@8.4

マネージドプラットフォーム

Google Cloudについては、Googleのガイダンスが対応範囲を次のように分けています。

  • PHP 8.2以降のApp Engineスタンダード環境は自動的にパッチが適用されます。
  • ベースイメージの自動更新を有効にしたCloud Runのサービスと関数は自動的にパッチが適用されます。それ以外はすべて再ビルドと再デプロイが必要です。
  • App Engineフレキシブル環境、GKE、自分でビルドしたイメージは利用者側の責任です。
  • PHP 8.1以前のランタイムにはパッチが提供されないため、アップグレードが必要です。

共有ホスティングやその他のマネージドPHPでは、プロバイダーのステータスページで修正済みバージョンを確認し、何も書かれていなければ問い合わせてください。ホスティング側で適用されたはずだと思い込んでいるだけのパッチは、パッチとは言えません。

ステップ3: すべてのワーカーを再起動して確認する

パッケージのアップグレードで置き換わるのはディスク上のファイルです。すでに動いているプロセスは再起動するまで古いコードをメモリに保持し続け、アップグレード後も生き残ったワーカーは以前のリクエストの古い状態を抱えている可能性があります。reloadではなくrestartしてください。

sudo systemctl restart php8.3-fpm
sudo systemctl restart apache2

RHEL系のシステムでは、ユニット名は通常php-fpmとhttpdです。その後、ステップ1のpsコマンドをもう一度実行します。すべてのPHPマスタープロセスの起動時刻がアップグレード後になっていること、そしてphp-fpm8.3 -v(または使用しているバイナリの同等のコマンド)が修正済みのバージョン、もしくはchangelogに両方のCVEが記載されたビルドを示すことを確認してください。

すぐにパッチを当てられない場合

完全な修正はアップデートだけです。PHPのアドバイザリには影響を軽減する暫定対策が挙げられていますが、それでインストールが安全になるわけではないと明記されています。設定はアプリケーションから上書きできない場所に置き、すべてのワーカーを再起動してください。ini_set()やFPMの.user.iniファイルで変更した設定は対策になりません。

PHP-FPMでは、プールの設定に追加します。

; /etc/php/8.3/fpm/pool.d/www.conf
; CVE-2026-109618: PHP内でのメモリ枯渇をなくし、リクエストボディを小さくする
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 2M
; CVE-2026-109619
php_admin_flag[session.upload_progress.enabled] = off

アプリケーションでアップロードが必要な場合はpost_max_size = 32Mとし、upload_max_filesizeはそれより小さく保ちます。PHP 8.5では、php.iniでfatal_error_backtraces = 0も設定してください。Qualysはさらに任意の対策としてFPMのpm.max_requests = 1を挙げています。これはリクエストごとにワーカーを入れ替えるもので、スループットに実際のコストがかかります。

memory_limitを無効にした場合は、暴走したリクエストがそのリクエストのワーカーだけを停止させるよう、PHPの外側にコンテナやcgroupのメモリ制限を設けてください。この目的にRLIMIT_ASやRLIMIT_DATAを使ってはいけません。CVE-2026-109618の悪用に必要なメモリ確保の失敗そのものを引き起こす可能性があります。

エッジ側では次の対策があります。

  • 既知のエクスプロイト手法に対するCloudflareのWAFルールは無料プランを含むすべてのプランで利用できますが、対象になるのはCloudflareを経由するトラフィックだけです。
  • Google Cloudでは、ファイルアップロードとRFIのカテゴリを含むCloud Armorのマネージドルールを有効にし、検査できないほど大きなリクエストボディをブロックします。
  • Webサーバーやプロキシで、リクエストのサイズ、フィールド、ヘッダーの上限を設定します。

パッチを当てられないインターネット公開システムについては、修正が適用され確認されるまで公開ネットワークから隔離するようCERT-EUが推奨しています。

侵害の痕跡を調べる

攻撃の試みは公開より前から始まっていたため、パッチ適用で入口はふさげても、誰かが侵入したかどうかはわかりません。CISAは、シェルを起動したりツールをダウンロードしたりするPHPワーカープロセスを監視するよう提案しています。10月17日以降のWebサーバーとFPMのログを確認し、過大または不正な形式のPOSTリクエスト、ワーカーのクラッシュ、致命的エラーがないか調べてください。php-fpmやhttpdの説明のつかない子プロセスは、ただの珍しい現象ではなくインシデントとして扱ってください。

対応の順序は単純です。まずインターネットに公開されたmod_phpのホストにパッチを当て、次にFPM、FrankenPHP、LiteSpeedに進みます。すべてを再起動し、起動時刻を確認します。そのうえで、公式リリースが今後も提供されない8.1以前のサーバーをどうするか決めます。PHP 8.2は12月31日にサポート終了を迎えるため、どのみち古いブランチから移行するのであれば、再び短期間で終わる移行先ではなく8.4か8.5を計画してください。

参考資料

  • PHP
  • Security
  • php2shell
  • CVE-2026-109618
  • CVE-2026-109619

Next.jsの静的サイトをFirebase Hostingで公開する

このサイトの構成を解説します。Next.jsの静的エクスポートをFirebase Hostingで配信し、URL・キャッシュ・SEOメタデータの整合性をとる方法です。

フルスタック開発, 約5分で読めます