
ASLR非依存のnginx RCEチェーンPoC。PoolSlipヒープ過読リーク(CVE-2026-9256)とriftオーバーフロー(CVE-2026-42945)を組み合わせ、標準のnginxでsystem()に到達する。
最近公開された2つのnginx rewriteエンジンのバグを、公式のnginx:1.30.0 Dockerイメージ(Debian 13、glibc 2.41)上で**ASLR非依存のリモートsystem()**へと連鎖させた初の公開チェーン — ハードコードされたアドレスなし、nginx再起動なし、新規ワーカーあたり約90%の成功率。
$argsのヒープオーバーリード → 稼働中のlibc + ヒープをリーク。is_argsのヒープオーバーフロー(URLセーフバイトのみ) → limit_connクリーンアップポインタの2バイト部分上書き → ngx_destroy_poolがそれを辿る → system(cmd)。どちらも同じis_argsの2パス不一致を、2つのシンクに向けたもの。完全な技術解説: Writeup
開示と安全性
- 両CVEはすでに修正済みであり、野外で活発に悪用されています。nginx 1.30.2(stable)または1.31.1(mainline)へアップグレードしてください —
1.30.1はriftを修正しますが、PoolSlipには依然として脆弱であるため、両方を塞ぐのは1.30.2のみです。NGINX Plus: R36 P5 / R32 P7 / R37.0.1.1。- これは教育および許可されたテストのみを目的として公開されています。同梱のローカルDockerラボに対して実行されます — 所有していない、または書面によるテスト許可を持たないインフラに対して向けないでください。
| CVE | ニックネーム | 脆弱 | 修正済み (nginx OSS) |
|---|---|---|---|
| CVE-2026-42945 | rift | 0.6.27 – 1.30.0 | 1.30.1 / 1.31.0 |
| CVE-2026-9256 | PoolSlip | 0.1.17 – 1.30.1 + 1.31.0 | 1.30.2 / 1.31.1 |
| ファイル | 目的 |
|---|---|
nginx.conf | 現実的なAPIゲートウェイ設定(ターゲット — limit_conn、v1→v2移行rewrite、アップロードプロキシ、劣化した検索ページ)。アロケータのチューニングなし。 |
run.sh | ローカルラボを起動: 公式nginx:1.30.0がnginx.confを配信、さらに遅いアップストリームをホスト:19322で提供。 |
exp_official.py | 完全なチェーン: PoolSlipリーク → libc/ヒープの導出 → スプレー → グルーミング → rift部分上書き → system()。 |
exp_official.pyの実行用)、curl、Linux x86-64。run.shは遅いアップストリームにperl(公式nginxイメージにすでに含まれている)を使用するため、追加パッケージもslow_backendファイルも不要で、インターネット接続も必要ありません。nginxバイナリ自体は公式のままです。WRITEUP.mdのgdbセッションを追うには、docker execで入り、apt-get install gdb file binutilsを実行してからgefをロードしてください。git clone https://github.com/y198nt/Nginx-chain-Rift-Poolslip
cd Nginx-chain-Rift-Poolslip
bash run.sh # stock nginx:1.30.0 on http://127.0.0.1:19322
python3 exp_official.py --cmd 'id > /tmp/rce_proof' # leak → overwrite → system()
docker exec nginx-rift-official cat /tmp/rce_proof # → uid=101(nginx) ...
exp_official.pyはワンショットです(新規ワーカーあたり約90%): 一度リークし、一度発火します。プルーフファイルが空の場合、グルーミングがスロットを1つ外しただけです — 再実行してください。
docker rm -f nginx-rift-official # tear the lab down
rewrite ^/search/((.*))$ /lookup?$1$2により、コピーパスがr->args.lenをバッファを超えて設定します。劣化した/searchページが$argsを隣接ヒープに反映します。短いウォームアップ後、libpcre-clusterポインタ(libcの0xb0ad08下に固定)とヒープポインタが安定したオフセットに落ち着く → libc_baseとheap_base、ハードコードされたものは何もありません。limit_connクリーンアップポインタの下位2バイトだけを上書きします — ASLRでランダム化された上位バイトはそのまま維持される → ASLR非依存。ngx_pool_cleanup_t{handler=&system, data=cmd, next=0}へリダイレクトされます。被害者を閉じるとngx_destroy_poolのクリーンアップウォークがトリガーされる → system(cmd)。完全な導出、gdbセッション、行き止まり/ピボットについてはWriteupを参照してください。
?を含みかつPCREキャプチャ($1/$2)を参照するrewrite、または^/x/((.*))$ → /y?$1$2のようなネストされたキャプチャパターンを介してのみ発火します。設定を監査してこれらを探し、パッチが適用されるまで問題のあるrewrite/setを作り直してください。$args(またはリクエスト由来の変数をそのままエコーバックすること)が、オーバーリードを情報リークに変える原因です — リクエスト引数をそのまま反映しないでください。%2B/+の洪水(GET /search/+++…)、長い$argsをエコーする劣化ページからの503、ワーカーのSIGSEGV/再生成ストーム。