
ngxray — scanner di sicurezza per configurazioni nginx
Scanner statico di vulnerabilità per configurazioni nginx. Analizza le configurazioni con il tokenizer di nginx stesso e le confronta con regole JSON dichiarative per rilevare CVE note nei pattern delle direttive del motore di rewrite/script.
cd ngxray
git submodule update --init
make
python3 scan.py /etc/nginx/nginx.conf
Le regole sono definite come file JSON in rules/. Lo scanner include:
| Regola | CVE | Pattern |
|---|---|---|
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 con riferimenti $N sovrapposti |
is_args persistenteVersioni interessate: NGINX 0.6.27 – 1.30.0, NGINX Plus R32 – R36. Corretto in: 1.31.0 / 1.30.1.
Una sostituzione rewrite contenente ? imposta e->is_args=1 nel motore di script. Questo flag persiste nelle direttive set o if successive, facendo sì che il passaggio di copia applichi l'escape URI ai dati di cattura (espansione 3x) in un buffer dimensionato per i byte grezzi.
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1; # $1 evaluated with stale is_args=1
}
Versioni interessate: fino a NGINX 1.31.0. Corretto in: 1.31.1 / 1.30.2.
I gruppi di cattura annidati fanno sì che il budget di escape del fast-path venga sottostimato — gli stessi byte URI vengono sottoposti a escape una volta per ogni riferimento $N sovrapposto.
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
Il pacchetto corpus_tools/ può raccogliere configurazioni nginx pubbliche da GitHub Code Search ed estrarre snippet nginx da formati wrapper.
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
Consulta corpus_tools/README.md e corpus_tools/docs/ per il flusso di lavoro completo e per il metodo di raccolta documentato incentrato su Rift.
Inserisci un file JSON in rules/. All'avvio il motore carica tutti i file *.json dalla directory delle regole.
{
"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 { ... }"]
}
}
Ogni regola può includere tests con snippet di configurazione vulnerable (devono attivare il rilevamento) e safe (non devono attivarlo). Esegui python3 scan.py --test per validare tutte le regole.
match è un elenco di condizioni sulle direttive. Con una sola condizione, il motore cerca nell'intero albero di configurazione qualsiasi direttiva corrispondente. Con più condizioni, tutte devono corrispondere a direttive dello stesso livello all'interno dello stesso blocco.
Ogni condizione:
{
"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 — indicizzate per posizione (come stringa). Ogni valore è un oggetto condizione.max_args — rifiuta se la direttiva ha più di N argomenti.overlapping_refs — verifica che i riferimenti $N in args[1] corrispondano a gruppi di cattura in args[0] che si contengono fisicamente a vicenda.any_arg — la condizione deve corrispondere ad almeno un argomento in qualsiasi posizione.or — almeno un ramo deve corrispondere. I rami possono verificare args oppure any_arg_from + any_of.extract + min_unique è il meccanismo generale per contare i pattern in un argomento. Ad esempio, per richiedere almeno 2 riferimenti di cattura distinti ($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; } }"
]
}
}
La directory parser/ contiene un programma C standalone che usa il tokenizer di nginx (ngx_conf_read_token e ngx_conf_parse da src/core/ngx_conf_file.c) per analizzare i file di configurazione in un AST JSON. È lo stesso lexer che nginx usa all'avvio — nessuna reimplementazione.
La modifica chiave: ngx_conf_handler() (che smista le direttive ai moduli compilati) viene sostituito da conf_handler(), che registra ogni direttiva in un albero indipendentemente dal nome. Ciò consente al parser di gestire qualsiasi configurazione valida senza il sistema dei moduli. La modifica viene applicata come patch in fase di build — nessun file sorgente di nginx viene copiato in questo repository.
I restanti file sorgente di nginx (allocatore di pool, funzioni per stringhe, logging, ecc.) vengono compilati direttamente dal sottomodulo.
scan.py carica le regole JSON da rules/, analizza le configurazioni tramite nginx_conf_parse e valuta ogni regola sull'AST. Tutti i dettagli di rilevamento sono contenuti nei file JSON delle regole.
Richiede un compilatore C e Python 3. Il sorgente di nginx è incluso come sottomodulo git.
git submodule update --init
make
make test
Questo repository è replicato in sola lettura su github.com/califio/ngxray tramite Copybara. Consulta COPYBARA.md.
| Chiave | Tipo | Corrisponde quando |
|---|
contains | string | L'argomento contiene la sottostringa |
regex | string | L'argomento corrisponde alla regex |
not_regex | string | L'argomento NON corrisponde alla regex |
any_of | [string] | L'argomento è uguale a uno dei valori |
extract | string | Regex con gruppo di cattura — estrae tutte le corrispondenze per il conteggio |
min_unique | int | (usato con extract) Almeno N corrispondenze distinte trovate |