Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
ngxray — ngxray — nginx config security scanner | Kitploit
Tools/GitHubGitHub/califio/ngxray
Static AnalysisVulnerability ScannersCode AnalysisConfiguration AuditingWeb SecurityMisconfiguration
GitHubcalifio/ngxray

ngxray

ngxray — nginx config security scanner

View Repository
2443 months agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

ngxray

Static vulnerability scanner for nginx configurations. Parses configs with nginx's own tokenizer and matches against declarative JSON rules to detect known CVEs in rewrite/script engine directive patterns.

Quick start

root@kitploit:~
cd ngxray
git submodule update --init
make
python3 scan.py /etc/nginx/nginx.conf

What it detects

Rules are defined as JSON files in rules/. The scanner ships with:

Download Tool
RuleCVEPattern
rewrite-is-argsCVE-2026-42945rewrite … /path?args + set $var $1
rewrite-is-args-ifCVE-2026-42945rewrite … /path?args + if (… $1 …)
nested-capture-redirectCVE-2026-9256rewrite ^/((…))$ … redirect with overlapping $N

CVE-2026-42945 — is_args stale flag overflow

Affected: NGINX 0.6.27 – 1.30.0, NGINX Plus R32 – R36. Fixed: 1.31.0 / 1.30.1.

A rewrite replacement containing ? sets e->is_args=1 in the script engine. This flag persists into subsequent set or if directives, causing the copy pass to URI-escape capture data (3x expansion) into a buffer sized for raw bytes.

root@kitploit:~
location ~ ^/api/(.*)$ {
    rewrite ^/api/(.*)$ /internal?migrated=true;
    set $original_endpoint $1;    # $1 evaluated with stale is_args=1
}

CVE-2026-9256 — nested capture fast-path overflow

Affected: through NGINX 1.31.0. Fixed: 1.31.1 / 1.30.2.

Nested capture groups cause the fast-path escape budget to undercount — the same URI bytes are escaped once per overlapping $N reference.

root@kitploit:~
rewrite ^/((.*))$ http://backend/$1$2 redirect;

Usage

root@kitploit:~
# 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 Collection

The corpus_tools/ package can collect public nginx configuration material from GitHub Code Search and extract nginx snippets from wrapper formats.

root@kitploit:~
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

See corpus_tools/README.md and corpus_tools/docs/ for the full workflow and the documented Rift-focused collection method.

Adding new rules

Drop a JSON file in rules/. The engine loads all *.json files from the rules directory at startup.

Rule schema

root@kitploit:~
{
  "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 { ... }"]
  }
}

Each rule can include tests with vulnerable (must trigger) and safe (must not trigger) config snippets. Run python3 scan.py --test to validate all rules.

Match patterns

match is a list of directive conditions. If there's one condition, the engine searches the entire config tree for any matching directive. If there are multiple, all conditions must match sibling directives in the same block.

Each condition:

root@kitploit:~
{
  "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 — keyed by position (as string). Each value is a condition object.
  • max_args — reject if the directive has more than N args.
  • overlapping_refs — verify that $N references in args[1] correspond to capture groups in args[0] that physically contain each other.
  • any_arg — condition must match at least one arg at any position.
  • or — at least one branch must match. Branches can check args or any_arg_from + any_of.

Condition objects

KeyTypeMatches when
containsstringArg contains the substring
regexstringArg matches the regex
not_regexstringArg does NOT match the regex
any_of[string]Arg equals one of the values
extractstringRegex with a capture group — extracts all matches for counting
min_uniqueint(used with extract) At least N distinct matches found

extract + min_unique is the general mechanism for counting patterns in an arg. For example, to require at least 2 distinct capture references ($1–$9):

root@kitploit:~
{ "extract": "\\$([1-9])", "min_unique": 2 }

Example: adding a new CVE

root@kitploit:~
{
  "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; } }"
    ]
  }
}

How it works

Parser

The parser/ directory contains a standalone C program that uses nginx's own tokenizer (ngx_conf_read_token and ngx_conf_parse from src/core/ngx_conf_file.c) to parse config files into a JSON AST. This is the same lexer nginx uses at startup — no reimplementation.

The key modification: ngx_conf_handler() (which dispatches directives to compiled-in modules) is replaced with conf_handler(), which records every directive into a tree regardless of name. This lets the parser handle any valid config without the module system. The modification is applied as a patch at build time — no nginx source files are copied into this repo.

The remaining nginx source files (pool allocator, string functions, logging, etc.) are compiled directly from the submodule.

Scanner

scan.py loads JSON rules from rules/, parses configs via nginx_conf_parse, and evaluates each rule against the AST. All detection specifics live in the JSON rule files.

Building

Requires a C compiler and Python 3. nginx source is included as a git submodule.

root@kitploit:~
git submodule update --init
make
make test

Mirroring

This repo is mirrored read-only to github.com/califio/ngxray via Copybara. See COPYBARA.md.