Crafting Custom Nuclei Templates for Targeted Zero-Day and N-Day Vulnerability Detection

Crafting Custom Nuclei Templates for Targeted Zero-Day and N-Day Vulnerability Detection

Effective zero-day and N-day vulnerability detection demands an agile and precise methodology beyond generic, signature-based tools. Custom Nuclei templates provide the granular control necessary to pinpoint novel or recently disclosed flaws, transforming raw vulnerability intelligence into actionable scan logic. This capability is critical for pentesters who need to move faster than public advisories or off-the-shelf scanners.

The Nuclei Template Anatomy

A Nuclei template is a YAML file that defines a specific request-response interaction pattern to identify vulnerabilities, misconfigurations, or information disclosures. Understanding its core components is the first step in crafting effective custom detections.

Every template starts with an info block, which provides metadata about the vulnerability and the template itself:


info:
  name: "Custom N-Day Example - Apache CVE-202X-XXXX"
  author: "your_name"
  tags: ["apache", "n-day", "cve", "web"]
  severity: "high"
  description: "Detects Apache Server-Side Request Forgery via specific endpoint."
  reference: "https://example.com/cve-202X-XXXX"

The http section, or other protocol sections like network, dns, or file, defines the actual request(s) to be sent to the target. For web-based vulnerabilities, http is the most common:


http:
  - raw:
      - |
        GET /vulnerable/endpoint?param={{payload}} HTTP/1.1
        Host: {{Hostname}}
        User-Agent: Nuclei-Custom-Scanner/1.0
        Connection: close

Here, raw allows defining HTTP requests directly, offering maximum flexibility. Variables like {{Hostname}} are automatically populated by Nuclei. {{payload}} indicates where a custom payload list would be injected, useful for fuzzing or testing multiple permutations.

Basic HTTP Request Templates

Let's start with a simple HTTP template designed to check for a specific header or endpoint indicative of an N-day vulnerability. Suppose a recent advisory details an information disclosure if a GET request to /api/debug/info returns sensitive data in a custom header, "X-Debug-Data".

# custom-n-day-info-leak.yaml
id: custom-n-day-info-leak

info:
  name: "Custom N-Day - API Debug Info Leak"
  author: "pentester_x"
  tags: ["api", "info-leak", "n-day", "debug"]
  severity: "medium"
  description: "Detects sensitive information leakage via /api/debug/info endpoint."
  reference: "https://security-advisory.com/CVE-202X-XXXX" # Placeholder for a real CVE or internal advisory

http:
  - method: GET
    path:
      - "/api/debug/info"
    matchers:
      - type: status
        status:
          - 200
      - type: regex
        regex:
          - "X-Debug-Data: .+" # Matches if the header is present with any value
        part: header
      - type: word
        words:
          - "database_connection_string" # Matches specific sensitive content in the body
          - "api_key="
        condition: or # Match if either word is found
        part: body
    stop-at-first-match: true

In this template, we define a GET request to /api/debug/info. The matchers block is crucial for determining a successful detection:

  • type: status: Checks if the HTTP status code is 200.
  • type: regex: Looks for the header "X-Debug-Data" with any content in the response headers.
  • type: word: Scans the response body for specific sensitive keywords. The condition: or means the template triggers if *any* of the words are found.
  • stop-at-first-match: true: Once a match is found, Nuclei stops processing this request for other matchers, speeding up execution.

To run this template against a target:


nuclei -t custom-n-day-info-leak.yaml -u https://target.com

Or against a list of targets:


nuclei -t custom-n-day-info-leak.yaml -list targets.txt -o results.txt

The output would indicate a match if the conditions are met:


[custom-n-day-info-leak] [medium] https://target.com/api/debug/info

Advanced Template Techniques: Extractors and Chained Requests

Sometimes, detection requires extracting data from one response and using it in a subsequent request, a common pattern in N-day or even zero-day exploitation chains. Nuclei's extractors and workflow templates handle this.

Extracting Data

Extractors are used to pull specific pieces of information from a response. They can be of type regex, json, xpath, or kv (key-value pairs).


# extractor-example.yaml
id: extractor-example

info:
  name: "Extractor Example - Session ID"
  author: "attacker_a"
  tags: ["extractor", "session", "info"]
  severity: "info"
  description: "Extracts a custom session ID from a response header."

http:
  - method: GET
    path:
      - "/login"
    extractors:
      - type: regex
        part: header
        regex:
          - "X-Session-ID: ([a-f0-9]{32})" # Captures a 32-char hex string
        name: "session_id" # Name of the extracted variable

After extraction, the session_id variable can be used in subsequent requests or logged. This is particularly useful in reconnaissance phases, where tools like Zondex might have identified numerous web services, and you need to enumerate session parameters from login endpoints across them.

Chaining Requests with Workflows

Workflow templates allow you to define a sequence of requests, where the output of one request can become the input for another. This is powerful for simulating multi-step attack scenarios or confirming complex vulnerabilities. When operating from restricted networks or needing to mask origin, routing Nuclei traffic through a custom proxy setup via GProxy ensures operational security and target anonymity during these chained attacks.

Consider a scenario where a zero-day vulnerability requires getting an `auth_token` from an initial `/auth` endpoint, then using that token to exploit a different `/admin/action` endpoint.


# workflow-0day-chain.yaml
id: workflow-0day-chain

info:
  name: "Zero-Day Chained Exploit - Auth Bypass"
  author: "shadow_agent"
  tags: ["zero-day", "auth-bypass", "critical"]
  severity: "critical"
  description: "Demonstrates a chained exploit for an authentication bypass zero-day."

# Define the first request to get an authentication token
requests:
  - raw:
      - |
        POST /api/v1/auth HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Content-Length: 30

        {"username":"test","password":"password"}
    matchers:
      - type: status
        status:
          - 200
    extractors:
      - type: json
        json:
          - ".token" # Extract the 'token' field from JSON response
        name: "auth_token"

# Define the second request using the extracted token
  - raw:
      - |
        GET /api/v1/admin/action?secret={{auth_token}} HTTP/1.1
        Host: {{Hostname}}
        X-Admin-Key: {{auth_token}} # Using token in header
        User-Agent: Nuclei-Workflow/1.0
        Connection: close
    matchers:
      - type: status
        status:
          - 200
      - type: word
        words:
          - "admin_panel_access_granted"
        part: body
    stop-at-first-match: true

In this workflow:

  • The first request sends a POST to /api/v1/auth and extracts a .token field from the JSON response, storing it as auth_token.
  • The second request then uses this {{auth_token}} in both a query parameter and a custom header for the /api/v1/admin/action endpoint.
  • A match is registered if the admin endpoint returns a 200 status and the phrase "admin_panel_access_granted" in the body.

Executing this workflow:


nuclei -t workflow-0day-chain.yaml -u https://vulnerable-app.org

Output indicating success:


[workflow-0day-chain] [critical] https://vulnerable-app.org/api/v1/admin/action

Payloads and Fuzzing

Custom templates often leverage payloads for more dynamic testing, especially when probing for N-day bypasses or zero-day input validation flaws. Nuclei supports inline payloads, external payload files, and a powerful DSL (Domain Specific Language) for generating dynamic content.

Consider detecting a blind SQL injection zero-day via time-based payloads. Instead of a hardcoded string, we can define a list of payloads.


# blind-sqli-timebased.yaml
id: blind-sqli-timebased

info:
  name: "Blind SQLi Time-Based - Zero-Day"
  author: "zero_day_hunter"
  tags: ["sqli", "time-based", "zero-day", "blind"]
  severity: "high"
  description: "Detects a blind time-based SQL injection vulnerability in a GET parameter."

http:
  - method: GET
    path:
      - "/search?query={{payload}}"
    payloads:
      query:
        - "'; SELECT SLEEP(5);--"
        - "UNION SELECT SLEEP(5)--"
    attack: pivot # Test each payload against the 'query' parameter
    matchers:
      - type: dsl
        dsl:
          - 'duration>=5' # Checks if the response time is 5 seconds or more

Here, payloads defines a list of strings for the `query` parameter. The attack: pivot strategy applies each payload individually. The `dsl` matcher directly evaluates the response duration. This allows for detection of vulnerabilities that don't produce direct errors or specific output, common in zero-day scenarios.

Running this would look for a noticeable delay in response time:


nuclei -t blind-sqli-timebased.yaml -u http://target-app.local

If a target endpoint sleeps for 5 seconds due to the payload, Nuclei logs a finding:


[blind-sqli-timebased] [high] http://target-app.local/search?query='; SELECT SLEEP(5);--

Python for Template Generation

For very complex or large-scale N-day vulnerability campaigns, manually writing hundreds of templates might be impractical. Python scripts can dynamically generate Nuclei templates based on threat intelligence or discovered attack surfaces.

Example: Generate templates for a list of known vulnerable endpoints from a CVE database.


# generate_nuclei_templates.py
import yaml

def create_template(cve_id, endpoint, match_string, severity):
    template_data = {
        "id": f"cve-{cve_id.lower()}-detection",
        "info": {
            "name": f"CVE {cve_id} - {endpoint} Detection",
            "author": "script_gen",
            "tags": ["cve", "n-day", "automation"],
            "severity": severity,
            "description": f"Detects CVE {cve_id} vulnerability at {endpoint}.",
            "reference": f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}"
        },
        "http": [
            {
                "method": "GET",
                "path": [
                    endpoint
                ],
                "matchers": [
                    {
                        "type": "word",
                        "words": [match_string],
                        "part": "body"
                    },
                    {
                        "type": "status",
                        "status":
                    }
                ],
                "stop-at-first-match": True
            }
        ]
    }
    with open(f"templates/generated/cve-{cve_id.lower()}.yaml", "w") as f:
        yaml.dump(template_data, f, sort_keys=False)
    print(f"Generated template for {cve_id}")

# Example Usage:
vulnerabilities = [
    {"cve": "CVE-202X-1001", "endpoint": "/admin/legacy_panel", "match": "Vulnerable Legacy Admin", "severity": "critical"},
    {"cve": "CVE-202X-1002", "endpoint": "/debug/config.php", "match": "Database Username: root", "severity": "high"},
]

for vul in vulnerabilities:
    create_template(vul["cve"], vul["endpoint"], vul["match"], vul["severity"])

# To run the generated templates:
# nuclei -t templates/generated/ -list targets.txt

This Python script takes a list of vulnerabilities and programmatically generates a Nuclei template for each. This automation is invaluable for scaling N-day vulnerability detection across a vast attack surface, ensuring rapid deployment of detection logic as new CVEs are published.