Описание
Caddy: MatchHost becomes case-sensitive for large host lists (>100), enabling host-based route/auth bypass
Summary
Caddy's HTTP host request matcher is documented as case-insensitive, but when configured with a large host list (>100 entries) it becomes case-sensitive due to an optimized matching path. An attacker can bypass host-based routing and any access controls attached to that route by changing the casing of the Host header.
Details
In Caddy v2.10.2, the MatchHost matcher states it matches the Host value case-insensitively:
modules/caddyhttp/matchers.go:type MatchHost matches requests by the Host value (case-insensitive).
However, in MatchHost.MatchWithError, when the host list is considered "large" (len(m) > 100):
MatchHost.large()returns true forlen(m) > 100(modules/caddyhttp/matchers.go, around thelarge()helper).- The matcher takes a "fast path" using binary search over the sorted host list, and checks for an exact match using a case-sensitive string comparison (
m[pos] == reqHost). - After the fast path fails, the fallback loop short-circuits for large lists by breaking as soon as it reaches the first non-fuzzy entry. For configs comprised of exact hostnames only (no wildcards/placeholders), this prevents the
strings.EqualFold(reqHost, host)check from ever running.
Net effect: with a host list length of 101 or more, changing only the casing of the incoming Host header can cause the host matcher to not match when it should.
Suggested fix
- Normalize exact hostnames to lower-case during
MatchHost.Provision(at least for non-fuzzy entries). - Normalize the incoming request host (
reqHost) to lower-case before the large-list binary search + equality check, so the optimized path stays case-insensitive.
Reproduced on:
- Stable release:
v2.10.2-- this is the release I reference in the repro below. - Dev build:
v2.11.0-beta.2. - Master tip: commit
58968b3fd38cacbf4b5e07cc8c8be27696dce60f.
PoC
Prereqs:
- bash, curl
- A pre-built Caddy binary available at
/opt/caddy-2.10.2/caddy(editCADDY_BINin the script if needed)
Script (Click to expand)
Expected output (Click to expand)
Impact
This is a route/auth bypass in Caddy's request-matching layer. Any internet-exposed Caddy deployment that relies on host matchers with large host lists (>100) to select protected routes (e.g. applying basicauth, forward_auth, respond deny rules, or protecting reverse_proxy backends) can be bypassed by varying the case of the Host header, allowing unauthorized access to sensitive endpoints depending on upstream configuration.
The reproduction is minimal per the reporting guidance; a realistic "full" scenario is Caddy fronting a multi-tenant app and doing forward_auth/basicauth/deny for /admin only when host is in a big (>100) allowlist, but the default handler still reverse_proxying to the same app. Then sending Host: H050.TEST skips the guarded route in Caddy, yet the upstream still treats it as the same tenant host --> /admin is reachable without the intended guard.
AI Use Disclosure
A custom AI agent pipeline was used to discover the vulnerability, after which was manually reproduced and validated each step. The entire report was ran through an LLM for editing.
Disclosure/crediting
Asim Viladi Oglu Manizada
Ссылки
- https://github.com/caddyserver/caddy/security/advisories/GHSA-x76f-jf84-rqj8
- https://nvd.nist.gov/vuln/detail/CVE-2026-27588
- https://github.com/caddyserver/caddy/commit/eec32a0bb5a11651c6a7b04ce82dc50610f2b27e
- https://github.com/caddyserver/caddy/releases/tag/v2.11.1
- https://pkg.go.dev/vuln/GO-2026-4541
Пакеты
github.com/caddyserver/caddy/v2
< 2.11.1
2.11.1
Связанные уязвимости
Caddy is an extensible server platform that uses TLS by default. Prior to version 2.11.1, Caddy's HTTP `host` request matcher is documented as case-insensitive, but when configured with a large host list (>100 entries) it becomes case-sensitive due to an optimized matching path. An attacker can bypass host-based routing and any access controls attached to that route by changing the casing of the `Host` header. Version 2.11.1 contains a fix for the issue.
Caddy is an extensible server platform that uses TLS by default. Prior to version 2.11.1, Caddy's HTTP `host` request matcher is documented as case-insensitive, but when configured with a large host list (>100 entries) it becomes case-sensitive due to an optimized matching path. An attacker can bypass host-based routing and any access controls attached to that route by changing the casing of the `Host` header. Version 2.11.1 contains a fix for the issue.
Caddy is an extensible server platform that uses TLS by default. Prior ...