Feb 25, 2026·6 min read·45 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
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.