CVE-2026-42945に関する完全な調査リポジトリ。ヒープバッファオーバーフロー解析、RCEエクスプロイト(ヒープスプレー+Feng Shui)、検出スクリプト、NGINX rewriteモジュールの脆弱性に対するパッチ適用ガイダンスを含みます。
NGINX ngx_http_rewrite_module におけるヒープバッファオーバーフロー
| 項目 | 値 |
|---|---|
| CVSS v4.0 | 9.2(緊急) |
| CVSS v3.1 | 8.1(高) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | 122 — ヒープベースのバッファオーバーフロー |
| 導入時期 | 2008年6月 — v0.6.27 |
| 発見 | 2026年4月 — DepthFirst Research |
| 修正 | 2026年5月13日 — v1.30.1、v1.31.0 |
| CVE公開 | 2026年5月21日 |
| 潜伏期間 | 約18年(未検出) |
| 修正コミット | 524977e7c534e87e5b55739fa74601c9f1102686 |
認証されていないリモート攻撃者は、特定の rewrite + set/if/rewrite 構成パターンを持つサーバーに対して細工したHTTPリクエストを送信することで、NGINXのワーカープロセスに決定論的なヒープバッファオーバーフローを引き起こすことができます。このオーバーフローはヒープメタデータ(ngx_pool_cleanup_t ポインタ)を破壊し、ヒープスプレーとFeng Shuiの技術により**リモートコード実行(RCE)**を可能にします。
server { listen 19321;
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
}
**主な要件:**
- 置換文字列に `?`(クエリ文字列区切り文字)を含む `rewrite` ディレクティブ
- その後に続く `set`、`if`、または `rewrite` ディレクティブが **名前なしPCREキャプチャ**(`$1`、`$2` など)を参照している
- rewrite の置換文字列内の `?` が `ngx_http_script_start_args_code` をトリガーし、`e->is_args = 1` を設定する
### 攻撃者が達成できること
| 能力 | 説明 |
|-----------|-------------|
| **サービス拒否 (Denial of Service)** | ワーカープロセスを決定的にクラッシュさせ、再スポーンループを引き起こす(ASLR の有無に関係なく機能) |
| **リモートコード実行 (Remote Code Execution)** | ASLR が無効(または部分的な上書きでバイパス)の場合、nginx ユーザーとして完全な RCE を達成 |
| **データ漏えい (Data Exfiltration)** | メモリ読み取りプリミティブを通じて、ワーカーヒープから機密データを抽出 |
| **永続化 (Persistence)** | ワーカープロセスメモリ内でのコード実行によりバックドアを仕込む |
---
## 2. 根本原因分析
### 2パススクリプトエンジン
NGINX の `ngx_http_rewrite_module` は、`src/http/ngx_http_script.c` 内で **2パススクリプトエンジン** を使用しています:
1. **長さパス** (`ngx_http_script_run`): すべてのスクリプトコードを反復処理し、必要なバッファサイズの合計を計算します。長さを `le.ip` と `le.pos` に書き込みます。
2. **コピーパス** (`ngx_http_script_copy_len`/`_code`): 再度反復処理し、事前に割り当てられたバッファ内の `e->ip` と `e->pos` に実際のバイトを書き込みます。
各スクリプトコードには、各パスに対応する2つのハンドラがあります。例:
- `ngx_http_script_copy_len` → `ngx_http_script_copy_code`
- `ngx_http_script_start_args_len` → `ngx_http_script_start_args_code`
### `is_args` フラグ
**エンジン構造体** (`ngx_http_script_engine_t`) 上のフラグ `e->is_args` は、コピーパスが特定の文字をどのように処理するかを制御します。```c
typedef struct {
u_char *ip;
u_char *pos;
ngx_http_variable_value_t *sp;
ngx_str_t buf;
int flushed;
unsigned is_args:1; // <-- THE BUG
unsigned ncaptures:1;
ngx_uint_t captures_size;
// ...
} ngx_http_script_engine_t;
When e->is_args = 1, the copy-code for $N capture references calls ngx_escape_uri() with NGX_ESCAPE_ARGS, which expands:
+ → %2B (1 byte → 3 bytes, +200%)% → %25 (1 byte → 3 bytes, +200%)& → %26 (1 byte → 3 bytes, +200%)脆弱なパターンに対する実行フロー:``` rewrite ^/api/(.*)$ /internal?migrated=true;
1. **リライト評価**中に、エンジンは置換文字列内の`?`に遭遇し、`ngx_http_script_start_args_code`をトリガーして、`e->is_args = 1`を設定します。
2. リライトはリクエストURIを変更し、続いて次のディレクティブに進みます。
3. **`e->is_args`は決してクリアされません**。
次に:```
set $original_endpoint $1;
le)が作成されます: ```c
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
これにより le.is_args = 0 が正しくゼロになるため、長さパスは生の、エスケープされていないキャプチャ長を返します。
e を再利用します。この e はステップ1から e->is_args = 1 のままです。コピーパスはURIエスケープを適用し、エスケープ可能な各文字を、raw長に合わせてサイズ設定されたバッファ内で1バイトから3バイトに展開します — ヒープオーバーフロー。Pass 1 (Length — sub-engine le): le.is_args = 0 capture $1 = "A+++++B" → length = 7
Buffer allocated: 7 bytes
Pass 2 (Copy — main engine e): e.is_args = 1 ← LEAKED from rewrite capture $1 = "A+++++B" ngx_escape_uri("A+++++B", NGX_ESCAPE_ARGS): A → A (1 byte) + → %2B (3 bytes) ← EXPANSION + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) B → B (1 byte) total written: 17 bytes buffer size: 7 bytes OVERFLOW: 10 bytes
展開倍率は `7 + (n_escapable * 2)` です。ここで `n_escapable` はキャプチャ内の `+`、`%`、`&` の数です。
---
## 3. 悪用のメカニズム
### 概要
| ステップ | 技術 | 説明 |
|------|-----------|-------------|
| 1 | オーバーフロー | `+` パディングを含む細工した URI を送信し、ヒープバッファをオーバーフローさせる |
| 2 | ヒープスプレー | `/spray` に大きなボディを POST し、制御したデータでヒープを埋める |
| 3 | Feng Shui | オーバーフロー対象(`ngx_pool_cleanup_t`)が隣接するように割り当てを調整する |
| 4 | ハンドラの破壊 | オーバーフローで `ngx_pool_cleanup_t.handler` を `system()` のアドレスで上書きする |
| 5 | クリーンアップのトリガー | プールの破棄を待機 → `system(cmd)` が攻撃者のコマンドを実行する |
| 6 | リバースシェル | リバースシェルペイロードに連鎖させ、インタラクティブなアクセスを得る |
### リクエスト横断型 Feng Shui
**単一リクエストの Feng Shui は失敗します**。オーバーフローが `cleanup` ポインタに到達する前に、プールのメタデータ(`->d.next`、`->d.failed`)を破壊してしまうためです。リクエスト終了時にプールが破棄されると、破壊されたメタデータが原因で **`system()` が呼び出される前にクラッシュ** します。
代わりに、このエクスプロイトは **リクエスト横断型 Feng Shui** を使用します。
1. **リクエスト 1(スプレー)**: `/spray` に大きなボディを POST します。バックエンド(`server.py`)は `X-Delay` ヘッダーでレスポンスを保持し、接続を開いたままにしてヒープの割り当てを維持します。スプレーによって、偽の `ngx_pool_cleanup_t` ブロックでヒープが埋められます。
2. **リクエスト 2(オーバーフロー)**: オーバーフロー URI を送信します。オーバーフローは `cleanup` ポインタのみを破壊し(プールのメタデータは破壊せず)、スプレーした偽ブロックを指すようにします。
3. **プールの破棄**: スプレーのレスポンスが完了すると(遅延が期限切れになると)、プールのクリーンアップチェーンは偽ブロックに移動し、`system(cmd)` を呼び出します。
### アドレス要件
| シンボル | 値(Docker、ASLR 無効) | 説明 |
|--------|--------------------------|-------------|
| `HEAP_BASE` | `0x555555659000` | nginx ヒープのベース |
| `system@libc` | `0x7ffff6f6e420` | glibc 内の `system()` |
| `NGX_CYCLES_POOL` | `0x5555556a4040` | cycles プールへのポインタ |
| 偽クリーンアップアドレス | `0x5555556a4030` | スプレーのターゲットアドレス |
### ASLR バイパス
ASLR を無効にしなくても、**DoS**(クラッシュ)は決定的に動作します。ASLR が有効な状態での RCE には、2 つのアプローチがあります。
1. **部分的な上書き**: 1 バイトまたは 2 バイトの上書きを使用して、同じページ内でポインタをずらし、残りのニブルを総当たりします(16〜256 回の試行)。
2. **情報漏洩**: `/proc/self/maps` を読み取るか、`log_parser.py` のメモリ分析を使用してレイアウトを特定します。
---
## 4. 修正の分析
### 公式修正
**コミット**: `524977e7c534e87e5b55739fa74601c9f1102686`
**ファイル**: `src/http/ngx_http_script.c`
**行**: 約 1205(`ngx_http_script_regex_end_code` 内)```diff
void
ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
{
ngx_http_script_regex_code_t *code;
code = (ngx_http_script_regex_code_t *) e->ip;
+ e->is_args = 0; /* ← THE FIX */
e->ip += sizeof(ngx_http_script_regex_code_t);
// ...
}
ngx_http_script_regex_end_code は、長さパスとコピーパスの両方で すべての正規表現評価の後 に実行されます。ここで e->is_args = 0 をリセットすることで、以下が保証されます:
set, if, rewrite) はクリーンな is_args = 0 で開始されますngx_http_script_start_args_code は、置換文字列内で ? を検出したときに is_args = 1 を設定できます — この修正はその機能を壊しませんpatches/0002-hardening-bounds-check.patch は ngx_http_script_copy_capture_code に境界チェックを追加します:```c
if (e->pos + len > e->buf.data + e->buf.len) {
return; /* gracefully truncate instead of overflowing */
}
### バックポートパッチ