nginx 設定向けの静的脆弱性スキャナです。nginx 自身のトークナイザで設定を解析し、宣言型の JSON ルールと照合して、rewrite/スクリプトエンジンのディレクティブパターンにおける既知の CVE を検出します。
cd ngxray
git submodule update --init
make
python3 scan.py /etc/nginx/nginx.conf
ルールは rules/ 内の JSON ファイルとして定義されています。スキャナには以下のルールが同梱されています:
| ルール | CVE | パターン |
|---|---|---|
rewrite-is-args | CVE-2026-42945 | rewrite … /path?args + set $var $1 |
rewrite-is-args-if | CVE-2026-42945 | rewrite … /path?args + if (… $1 …) |
nested-capture-redirect | CVE-2026-9256 | rewrite ^/((…))$ … redirect(オーバーラップする $N 参照を含む) |
is_args の stale フラグによるオーバーフロー影響を受けるバージョン: NGINX 0.6.27 – 1.30.0、NGINX Plus R32 – R36。修正済みバージョン: 1.31.0 / 1.30.1。
rewrite の置換文字列に ? が含まれると、スクリプトエンジン内で e->is_args=1 が設定されます。このフラグは後続の set または if ディレクティブまで保持されるため、コピーパスは生バイト用に確保されたバッファに対してキャプチャデータを URI エスケープ(3 倍に展開)して書き込みます。
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1; # $1 evaluated with stale is_args=1
}
影響を受けるバージョン: NGINX 1.31.0 まで。修正済みバージョン: 1.31.1 / 1.30.2。
ネストしたキャプチャグループにより、fast-path のエスケープ予算が過小カウントになります — 同じ URI バイトが、オーバーラップする $N 参照ごとに 1 回ずつエスケープされるためです。
rewrite ^/((.*))$ http://backend/$1$2 redirect;
# Single file
python3 scan.py /etc/nginx/nginx.conf
# Directory (recursive, all *.conf files)
python3 scan.py /etc/nginx/
# Filenames from stdin
find /etc/nginx -name '*.conf' | python3 scan.py -
# Pre-parsed JSONL (faster for large batches)
find /etc/nginx -name '*.conf' | ./build/nginx_conf_parse - | python3 scan.py --jsonl -
# JSON output
python3 scan.py --json /etc/nginx/nginx.conf
# Custom rules directory
python3 scan.py --rules ./my-rules/ /etc/nginx/nginx.conf
corpus_tools/ パッケージは、GitHub Code Search から公開されている nginx 設定資料を収集し、ラッパー形式から nginx スニペットを抽出できます。
python3 corpus_tools/collect_github_nginx_corpus.py \
--output-dir corpus_out/nginx-rift \
--query-file corpus_tools/queries/nginx-rift.txt
python3 corpus_tools/extract_nginx_configs.py \
--input-dir corpus_out/nginx-rift/raw \
--output-dir corpus_out/nginx-rift/extracted
python3 scan.py corpus_out/nginx-rift/extracted
完全なワークフローと、文書化された Rift に焦点を当てた収集方法については、corpus_tools/README.md と corpus_tools/docs/ を参照してください。
JSON ファイルを rules/ に置いてください。エンジンは起動時にルールディレクトリ内のすべての *.json ファイルを読み込みます。
{
"id": "rule-name",
"cve": "CVE-YYYY-NNNNN",
"severity": "CRITICAL",
"message": "short description shown in output",
"affected": "nginx version range",
"fixed": "fixed versions",
"ref": "https://link-to-advisory",
"match": [ ... ],
"tests": {
"vulnerable": ["server { ... }"],
"safe": ["server { ... }"]
}
}
各ルールには tests を含めることができ、vulnerable(検出される必要あり)と safe(検出されてはならない)の設定スニペットを指定します。すべてのルールを検証するには python3 scan.py --test を実行してください。
match はディレクティブ条件のリストです。条件が 1 つの場合、エンジンは設定ツリー全体から一致するディレクティブを探します。複数の場合、すべての条件が同じブロック内の兄弟ディレクティブに一致する必要があります。
各条件:
{
"directive": "rewrite",
"args": {
"0": { "contains": "?" },
"1": { "regex": "\\$[1-9]" }
},
"any_arg": { "regex": "\\$[1-9]" },
"or": [
{ "args": { "1": { "contains": "?" } } },
{ "any_arg_from": 2, "any_of": ["redirect", "permanent"] }
]
}
args — 位置(文字列)をキーとします。各値は条件オブジェクトです。max_args — ディレクティブの引数が N 個を超える場合に拒否します。overlapping_refs — args[1] 内の $N 参照が、args[0] 内で物理的に互いを含むキャプチャグループに対応していることを検証します。any_arg — 条件が任意の位置の少なくとも 1 つの引数に一致する必要があります。or — 少なくとも 1 つのブランチが一致する必要があります。ブランチは args または any_arg_from + any_of をチェックできます。extract + min_unique は、引数内のパターンをカウントするための一般的な仕組みです。たとえば、少なくとも 2 つの異なるキャプチャ参照($1–$9)を要求する場合:
{ "extract": "\\$([1-9])", "min_unique": 2 }
{
"id": "proxy-buffer-overflow",
"cve": "CVE-2099-99999",
"severity": "CRITICAL",
"message": "proxy_pass with oversized buffer — heap overflow",
"ref": "https://example.com/advisory",
"match": [
{
"directive": "proxy_pass",
"args": { "0": { "regex": "https?://" } }
},
{
"directive": "proxy_buffer_size",
"args": { "0": { "regex": "^(6[5-9]|[7-9]\\d|\\d{3,})k$" } }
}
],
"tests": {
"vulnerable": [
"server { location / { proxy_pass http://backend; proxy_buffer_size 128k; } }"
],
"safe": [
"server { location / { proxy_pass http://backend; proxy_buffer_size 8k; } }"
]
}
}
parser/ ディレクトリには、nginx 自身のトークナイザ(src/core/ngx_conf_file.c の ngx_conf_read_token と ngx_conf_parse)を使用して設定ファイルを JSON AST に解析する、スタンドアロンの C プログラムが含まれています。これは nginx が起動時に使用するものと同じレクサーであり、再実装ではありません。
主な変更点は、ngx_conf_handler()(ディレクティブを組み込みモジュールにディスパッチする関数)を conf_handler() に置き換えたことです。conf_handler() は名前に関係なくすべてのディレクティブをツリーに記録するため、パーサーはモジュールシステムなしで任意の有効な設定を処理できます。この変更はビルド時にパッチとして適用され、nginx のソースファイルはこのリポジトリにコピーされません。
残りの nginx ソースファイル(プールアロケータ、文字列関数、ロギングなど)は、サブモジュールから直接コンパイルされます。
scan.py は rules/ から JSON ルールを読み込み、nginx_conf_parse 経由で設定を解析し、AST に対して各ルールを評価します。検出の詳細はすべて JSON ルールファイルに記述されています。
C コンパイラと Python 3 が必要です。nginx のソースは git サブモジュールとして含まれています。
git submodule update --init
make
make test
このリポジトリは Copybara 経由で github.com/califio/ngxray に読み取り専用でミラーリングされています。COPYBARA.md を参照してください。
| キー | 型 | 一致条件 |
|---|
contains | string | 引数が部分文字列を含む |
regex | string | 引数が正規表現に一致する |
not_regex | string | 引数が正規表現に一致しない |
any_of | [string] | 引数がいずれかの値と等しい |
extract | string | キャプチャグループを持つ正規表現 — カウント用にすべてのマッチを抽出する |
min_unique | int | (extract と併用)少なくとも N 個の異なるマッチが見つかる |