Nuclei vulnerabilities: Information Disclosure, Denial of Service

Nuclei vulnerabilities: Information Disclosure, Denial of Service

Introduction

Nuclei is a powerful, template-based vulnerability scanner primarily used to identify CVEs and misconfigurations across HTTP services. With over 30k stars on GitHub and a massive community-driven template library, it has become a staple tool for security researchers and DevSecOps teams alike.

At Stoïk, we utilize Nuclei as part of our scanning infrastructure to proactively detect exposures for our insured partners. One of Nuclei’s core strengths is its extensibility: detection logic is defined in YAML templates, which specify the requests, matching conditions, and dynamic variables required to identify a vulnerability.

Finding the bug

The Discovery

While monitoring the stability of our scanning infrastructure, we noticed a Nuclei instance that consistently failed to terminate. Investigation revealed a specific target was triggering a total resource exhaustion: every time Nuclei scanned this HTTP server, CPU usage spiked to 100% and the process hung indefinitely.
To isolate the behavior, we developed a minimal Go-based mock server to replicate the target's HTTP responses. This allowed us to systematically strip down the response body until we identified the trigger.

Root Cause: Template Injection in the Scanner

The "poison" response contained a high density of template tags (e.g., {{example}} or {:variable}). We discovered that Nuclei was inadvertently attempting to process and evaluate template expressions found within the body of the HTTP response it was supposed to be scanning.
While Nuclei is designed to evaluate these tags within its own local YAML templates to craft dynamic requests, it should never treat data from an untrusted remote server as executable template logic. This flaw creates a "Scanner-side Template Injection" (SSTI), allowing a malicious server to force the scanner into executing internal Nuclei functions, leading to:

  • Denial of Service (DoS): Forcing the scanner into infinite loops or heavy computations.
  • Information Disclosure: Potentially leaking internal scanner variables or environment data if the functions are used to echo back internal state.

POC

To demonstrate the flaw, we created a minimal Go-based HTTP server. This server returns a response body containing a Nuclei-specific template function: {{md5("Hello")}}.

If the flaw exists, Nuclei will not treat this as a literal string. Instead, it will evaluate the md5 helper function and use the resulting hash in its subsequent logic.

1. The malicious mock server
The following server listens on port :8000. When scanned, it serves the payload and logs any incoming data to show what the scanner "thinks" it found.

// file main.go (run with `go run main.go`)
// Starts an HTTP server on port :8000 that replies `md5("Hello")`

package main

import (
	"log"
	"io"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPost {
			body, _ := io.ReadAll(r.Body)
			log.Printf("Received POST body: %s", string(body))
			w.Write([]byte("OK"))
			return
		}
		log.Printf("Received request: %s %s", r.Method, r.URL.Path)
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write([]byte(`{{md5("Hello")}}`))
	})

	log.Println("Serving on <http://localhost:8000>")
	log.Fatal(http.ListenAndServe(":8000", nil))
}

2. Execution
We run Nuclei against this local server using a standard CVE template. By using the -debug flag, we can observe the internal behavior of the engine.

nuclei -debug -t http/cves/2023/CVE-2023-20073.yaml -u <http://localhost:8000>

3. The results (verification)
Upon execution, our mock server logs the following:

Received POST body: [...]
8b1a9953c4611296a827abf8c47804d7
[...]

Analysis:
The value 8b1a9... is the MD5 hash of the string "Hello". Because this value appears in our server logs, it proves that Nuclei extracted the tag from our HTTP response, executed the md5() function internally, and then passed that processed data back to us in a subsequent request.

Impacted Nuclei templates

To exploit this vulnerability, a template must utilize an extractor to pull data from a server response and subsequently use that data in a following request or condition. This pattern is foundational to Nuclei’s logic, particularly when testing for multi-step vulnerabilities like XSS or CSRF.
Commonly impacted templates include:

Turning Features Into Attack Primitives

While computing an MD5 hash is a harmless proof-of-concept, the actual function surface available to an attacker is vast. Nuclei’s Domain Specific Language (DSL) provides numerous helper functions that can be weaponized by a malicious target:

  • Information Disclosure: Accessing internal environment variables via env_var (if --env-vars is enabled).
  • Denial of Service (Resource Exhaustion): Forcing intensive CPU cycles using regex(), zip(), or cookie_unsign().
  • Process Hanging: Indefinite execution delays using wait_for().
  • SSRF & Internal network probing: Triggering internal network fingerprinting via jarm("internal_ip:port").
  • API Abuse: If run with AI features (-ai), a target can force calls to the OpenAI API using llm_prompt(), potentially exhausting the user's API credits.

In short, in the worst case, a single HTTP response from a server you are scanning can exhaust your CPU, hang your process indefinitely, exfiltrate your environment variables, drain your API credits, and quietly map your internal network — all without you ever noticing. The scanner doesn't just fail; it becomes an unwitting agent of the target it was sent to inspect.

Vulnerability reported to ProjectDiscovery

We believe in responsible disclosure and reached out to ProjectDiscovery to resolve this flaw before public release:

  • January 31, 2026: Initial vulnerability report, PoC, and suggested remediation sent to security@projectdiscovery.io.
  • February 1, 2026: ProjectDiscovery pushed an optimization related to template processing; however, our testing confirmed the core injection vulnerability remained unpatched.
  • February 6, 2026: Follow-up email sent after a week of silence.
  • February 7, 2026: ProjectDiscovery responded, stating they do not consider this a security vulnerability.
  • March 14, 2026: After sharing a draft of this article, ProjectDiscovery reconsidered the status of the vulnerability and opened an issue on Github with a Pull Request.
  • March 31, 2026: We were able to reproduce the vulnerability with the upstream fix and shared our reproduction details with ProjectDiscovery.
  • April 2nd, 2026: ProjectDiscovery opened another issue with its Pull Request which fixed our reproduction case.
  • April 18th, 2026: Nuclei v3.8.0 was released with the upstream fix.

Root cause

The vulnerability stems from a "Double Pass" evaluation logic within Nuclei’s expression engine. Specifically, in pkg/protocols/common/expressions/expressions.go, the evaluate function was replacing placeholders (variables) before processing helper functions. This allowed an attacker to inject a function string into a variable that would then be executed in the second pass.

Conclusion

While Nuclei is an invaluable tool for security research, this vulnerability demonstrates that even the tools we use to find bugs can introduce risks. Treating the scanner as an attack surface is essential for maintaining a secure scanning infrastructure.

Recommendations for Nuclei Users
To mitigate the risk of Scanner-side Template Injection (SSTI) and resource exhaustion, we recommend the following security best practices:

  • Isolate Scanning Infrastructure: Run Nuclei in a sandboxed environment (e.g., a restricted Docker container) with limited network egress and strict resource quotas (CPU/Memory limits).
  • Disable Sensitive Features: Avoid using the --env-vars and -ai flags when scanning untrusted or third-party targets, as these significantly expand the impact of function injection.
  • Audit Custom Templates: Carefully review third-party or custom templates that utilize extractors. Be aware that any data captured by these extractors could potentially be re-evaluated by the engine in subsequent requests ;
  • Monitor for Anomalies: Implement monitoring for Nuclei processes that exhibit unusual CPU spikes or fail to terminate within expected timeouts, as these may indicate a targeted DoS attack.
    By treating target responses as untrusted data (even within the context of a vulnerability scanner) security teams can ensure their automation remains a defensive asset rather than a liability.

Indicators of Compromise
Because the victim here is the scanner itself rather than a traditional target, IOCs differ from the usual network or file-based artifacts. The following signals should be treated as suspicious when running Nuclei against untrusted targets.

  • Process & host-based: A Nuclei process stuck at 100% CPU with no termination, a scan duration significantly exceeding the expected baseline, or unexpected outbound connections to api.openai.com from the scanning host.
  • Network-based: POST requests sent back to a scanned target containing computed values (hashes, encoded strings) absent from the original template; outbound JARM fingerprinting connections toward internal IPs outside the scan scope; unexpected internal network probing originating from the scanner host.
  • Log-based: In -debug output, extracted values containing {{ and }} being passed into follow-up requests, or response bodies matching the pattern \{\{[a-zA-Z_]+\(.*\)\}\}. Pay particular attention to the presence of wait_for(, jarm(, env_var(, llm_prompt(, or cookie_unsign( in any server response.

Vulnerability identifiers

This vulnerability was initially published as CVE-2026-41282 then CVE-2026-41645.

By Martin Desrumaux, Software Engineer @ Stoïk