
ngxray — nginx 설정 보안 스캐너
nginx 구성 파일용 정적 취약점 스캐너입니다. nginx 자체 토크나이저로 구성을 파싱하고 선언적 JSON 규칙과 대조하여 rewrite/script 엔진 지시문 패턴에서 알려진 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 with overlapping $N |
is_args 스테일 플래그 오버플로영향받는 버전: 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.
중첩 캡처 그룹은 패스트패스 이스케이프 예산을 과소 계산하게 만듭니다. 즉, 동일한 URI 바이트가 겹치는 각 $N 참조마다 한 번씩 이스케이프됩니다.
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/를 참조하세요.
rules/ 디렉터리에 JSON 파일을 넣으세요. 엔진은 시작 시 규칙 디렉터리의 모든 *.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 { ... }"]
}
}
각 규칙에는 vulnerable(트리거되어야 함) 및 safe(트리거되지 않아야 함) 구성 스니펫이 포함된 tests를 포함할 수 있습니다. python3 scan.py --test를 실행하여 모든 규칙을 검증하세요.
match는 지시문 조건 목록입니다. 조건이 하나라면 엔진은 전체 구성 트리에서 일치하는 지시문을 검색합니다. 여러 개라면 모든 조건이 같은 블록 안의 형제 지시문과 일치해야 합니다.
각 조건:
{
"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 — 조건이 임의의 위치에서 하나 이상의 인자와 일치해야 합니다.or — 하나 이상의 분기가 일치해야 합니다. 분기는 args 또는 any_arg_from + any_of를 검사할 수 있습니다.extract + min_unique는 인자 내 패턴 개수를 세는 일반적인 메커니즘입니다. 예를 들어 서로 다른 캡처 참조($1–$9)가 최소 2개 이상 필요하다면:
{ "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()로 대체되며, 이 함수는 이름과 관계없이 모든 지시문을 트리에 기록합니다. 이를 통해 파서는 모듈 시스템 없이도 모든 유효한 구성을 처리할 수 있습니다. 이 수정은 빌드 시 패치로 적용되며, 이 저장소에는 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개 이상의 서로 다른 일치 항목이 발견됨 |