
CVE-2026-17544 用エクスプロイト: PHP bcmath の OOB 書き込みをメモリのみの RCE に変換し、ランタイムのオフセットフリーレゾルバーで disable_functions と open_basedir をバイパスします。
CVE-2026-17544 の実証コード(weaponized proof-of-concept)です。PHP の書き直された bcmath 拡張機能(bc_str2num)における領域外書き込みを利用します。これにより、公知の DoS のみ のクラッシュを、disable_functions と open_basedir の両方を同時に迂回する 完全かつ普遍的な、メモリのみによるリモートコード実行プリミティブ に変換します。
認可された研究・教育目的のみ。 自分が所有するシステム、または明示的にテストを許可されたシステムに対してのみ実行してください。
bc_str2num は手動の scale で小数部を切り詰め、末尾のゼロを再トリミングしますが、fractional_end を更新しません。バッファは再トリミング後の長さでサイズ設定される一方、コピーはトリミング前の長さを使用するため、Zend MM ヒープへの制御可能な が発生します。zend_string の参照カウントがゼロにされます(長さは維持)。→ 参照カウント不整合(refcount-desync)による use-after-free → 巨大文字列による相対 R/W → フォールトセーフな絶対任意読み取り → 実行時シンボル解決 → 完全に データのみ(data-only) のネイティブ関数呼び出し。zend_class_entry + インラインの function_table + 偽の zend_function{ handler = zif_shell_exec } を構築し、それをメソッドとして呼び出します。コマンド出力は文字列として返され、レスポンス/stdout に配信されます。.data、対象シンボルはすべて、メモリをスキャンして実行時に解決されるため、単一のファイルがビルドやディストリビューションをまたいで動作します。これは サンドボックスエスケープ / ポストエクスプロイテーション のプリミティブであり、リモート入力のみの RCE ではありません。
| シナリオ | 結果 |
|---|---|
攻撃者がすでに PHP を実行できる(webshell、デシリアライゼーション、eval シンク)が、disable_functions + open_basedir によって制限されている | ✅ ネイティブ RCE(この PoC) |
アプリケーションが生のユーザー入力に対して bcmath 関数を単に呼び出すだけ、例: bccomp($_POST['v']) | ❌ DoS / クラッシュのみ |
攻撃者の入力に対する単一の bcmath 呼び出しは、フィードバックのない ブラインド(blind) な OOB ゼロ書き込みを 1 回発生させます。完全な RCE には、ヒープをグルーミングし、R/W プリミティブを構築し、リゾルバを実行するための任意の PHP 実行が必要であり、そのいずれも関数の引数として表現できません。したがって、入力のみの露出における現実的な上限は リモートサービス妨害(remote denial of service) です。コード実行には既存の PHP 実行コンテキストが必要であり、この PoC はそのコンテキストをサンドボックスから解放します。
このバグは 書き直された bcmath(PHP 8.4 / 8.5 系)にのみ存在します:
| PHP ブランチ | 脆弱性 |
|---|---|
| 7.x、8.0 – 8.3 | なし(旧 libbcmath、別の bc_str2num) |
| 8.4.0 – 8.4.23 | あり |
| 8.5.0 – 8.5.8 | あり |
| ≥ 8.4.24 / ≥ 8.5.9 | パッチ済み |
PHP 8.4.23 および PHP 8.5.8、amd64 において、同じ未修正ファイルでエンドツーエンドの検証済み(uid=0、出力キャプチャあり)— Zend 構造体のレイアウトは 8.4 / 8.5 で同一です。
bcmath が有効な、脆弱性の影響範囲内の PHP 8.4/8.5 バイナリ。.text から ELF ヘッダーまでページ単位で降下します。これは、連続マッピングされた任意のバイナリで安全です。マップされていない 2 MB のギャップを持つ人為的な -z separate-code ビルドのみ、ブラインドスキャンでは自動解決できません(プロダクションでは見られません)。exit() がサーバーを停止させないように、リクエストの分離が必要です: php-fpm、Apache mod_php、または PHP_CLI_SERVER_WORKERS>0 のビルトインサーバー。php exploit.php "id; uname -a"
例として shell.php として配置し、次に:
GET /shell.php?cmd=id
POST cmd=id
コマンド出力は zif_shell_exec を介してキャプチャされ、レスポンスボディに書き込まれます。
bccomp() を実行し、OOB ゼロ書き込みが隣接する zend_string の参照カウントのみに着弾するようにします(長さは維持)。zend_reference(間接参照経由)により、ターゲットの参照カウント付きヘッダーに触れることのない、フォールトセーフな絶対任意読み取りが得られます。.text ポインターから: ページ単位で ELF マジック(ベース)まで降下し、プログラムヘッダーを解析して .data を見つけ、次に .data 内でハンドラーが .text を指す shell_exec の zend_function_entry をスキャンします。zend_class_entry、単一バケットを持つインラインの function_table(正しい zend_string ハッシュ、終端された衝突チェーン)、およびハンドラーが zif_shell_exec である偽の zend_internal_function を構築します。プローブオブジェクトのクラスポインターを破壊し、メソッドを呼び出します。出力は返されてエコーされます。exploit.php — 普遍的なメモリのみのエクスプロイト(CLI + Web)。セキュリティ研究および教育のために提供されます。著者は誤用について責任を負いません。テストを許可されていないシステムに対して展開しないでください。