Feb 25, 2026·6 min read·8 visits
Caddy < 2.11.1 failed to escape backslashes in its `file` matcher sanitization. Attackers can use paths like `/admi\n` to bypass explicit block rules for `/admin`, as the filesystem globber interprets the escaped character literally, serving the protected file.
A semantic gap between Caddy's HTTP request matching and the underlying Go filesystem globbing logic allows attackers to bypass path-based access controls. By injecting backslashes into the request URI, an attacker can evade security matchers while still successfully resolving the target file on disk.
Caddy is the darling of the modern web server world—automatic HTTPS, clean config syntax, and usually, secure defaults. But like any complex piece of software sitting at the boundary between the wild internet and your dusty filesystem, it faces a classic problem: translation errors.
In web security, we often talk about "semantic gaps" or "parser differentials." This happens when two different parts of a system interpret the same piece of data in two different ways. The firewall thinks the packet is safe, but the kernel thinks it's a command. In Caddy's case, the differential lies between the HTTP request matcher (which decides if you can see a file) and the file matcher (which decides which file to show you).
CVE-2026-27585 is a beautiful, textbook example of this. It's not a memory corruption bug. It's not a buffer overflow. It's a logic error where a single character—the humble backslash—lets us walk right past the bouncer.
To understand this bug, you have to look at how Caddy handles the try_files directive and the file matcher. When you tell Caddy to check if a file exists, it often takes the URL path and runs it against the filesystem. But filesystems are weird. They support "globbing"—wildcards like * (match anything) or ? (match one character).
If a user requests /images/*, and Caddy passes that directly to the filesystem, the OS might dutifully list every file in the directory. That's bad. So, Caddy implements a sanitizer called globSafeRepl. Its job is to neuter these special characters before they hit the disk logic.
Here is where the developers tripped. They remembered to escape the obvious wildcards: asterisks, question marks, and brackets. But they forgot the master key of globbing: the escape character itself, the backslash (\).
In Go's path/filepath.Glob implementation (and many others), a backslash is used to escape the next character. So, \* means "literal asterisk", not wildcard. But crucially, \a just means "literal a". This creates our bypass mechanism. To the HTTP matcher, /admi\n is a completely different string than /admin. But to the globber, they are identical.
The vulnerability lived in modules/caddyhttp/fileserver/matcher.go. The globSafeRepl function was responsible for making paths safe for globbing. Let's look at the diff found in commit 68d50020eef0d4c3398b878f17c8092ca5b58ca0.
The Vulnerable Code:
// It escapes *, ?, and [ but leaves \ alone
var globSafeRepl = strings.NewReplacer(
"*", "\\*",
"?", "\\?",
"[", "\\[",
)Because the backslash wasn't in this list, a user input of foo\bar would remain foo\bar. When passed to filepath.Glob, that backslash would escape the b.
The Fix (v2.11.1):
// Now escaping the escape character itself
var globSafeRepl = strings.NewReplacer(
"*", "\\*",
"?", "\\?",
"[", "\\[",
"\\", "\\\\", // <--- The fix
)It's a one-line fix, as most critical logic bugs are. By turning \ into \\, Caddy ensures that the globber sees a literal backslash, rather than interpreting it as an escape sequence. Now, /admi\n becomes /admi\\n, which the filesystem looks for literally—and fails to find (unless you have a file named admi\n on disk, in which case, you have other problems).
Let's construct a realistic attack scenario. Imagine a system administrator has set up a Caddy server with a blocklist for a sensitive directory, but uses try_files to serve content.
Vulnerable Configuration:
example.com {
# 1. Security Rule: Block access to /secret
@blocked {
path /secret/*
}
respond @blocked 403
# 2. File Server: Try to find the file
try_files {path} /index.php
file_server
}The Attack Chain:
GET /secret/password.txt. The @blocked matcher sees the path starts with /secret/. The request is denied (403).GET /secre\t/password.txt.@blocked matcher compares /secre\t/ against /secret/. They do not match. The request is allowed to proceed.try_files. The placeholder {path} expands to /secre\t/password.txt.filepath.Glob. The globber reads \t as an escaped literal t./secret/password.txt on the disk. Caddy serves the file.We have successfully accessed a protected resource by exploiting the discrepancy between string comparison and filesystem globbing logic.
This is a High Integrity impact vulnerability. While it doesn't give you Remote Code Execution (RCE) directly, it completely undermines the access control mechanisms that administrators rely on.
If you use Caddy as a reverse proxy or file server and rely on path matchers to restrict access to sensitive admin panels, config files, or internal metrics, this vulnerability renders those rules useless against a competent attacker.
It is particularly dangerous because it requires no authentication and no complex memory corruption techniques. It works on the default configuration logic that many tutorials suggest. The only saving grace is that it requires the specific combination of a path-based blocklist and a file matcher (or try_files) that processes the same input.
The mitigation is straightforward: Update to Caddy v2.11.1. This version includes the patch that correctly sanitizes backslashes in the file matcher.
If you cannot update immediately, you must sanitize the input at the edge. If you are running Caddy behind a cloud load balancer or another proxy (like Nginx), configure that upstream proxy to reject URIs containing backslashes (%5C). Alternatively, you can attempt to rewrite the request within Caddy before it hits the matcher logic, but this is error-prone. The binary update is the only robust fix.
> [!NOTE] > This fix demonstrates the importance of "defense in depth." Relying solely on path matching for security is brittle. Whenever possible, move sensitive files out of the web root entirely, rather than relying on the web server to block access to them.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P| Product | Affected Versions | Fixed Version |
|---|---|---|
Caddy Caddy Web Server | < 2.11.1 | 2.11.1 |
| Attribute | Detail |
|---|---|
| CWE | CWE-20 (Improper Input Validation) |
| CVSS v4.0 | 6.9 (Medium) |
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| Exploit Status | PoC Available |
Improper Input Validation