# Caddy: FastCGI header normalization bypass in `forward_auth copy_headers`

Caddy's forward_auth copy_headers deletes exact header names, but php_fastcgi maps '-' to '_', so a client-sent Remote_Groups alias survives and injects trusted identity headers into PHP.

- Payout: Research
- Severity: high
- Program: Caddy
- Found by: Vincent550102
- Disclosed: 2026-06-16
- CVE: CVE-2026-52845
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-f59h-q822-g45g
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-f59h-q822-g45g
- Tags: area:web domain:web impact:integrity impact:privesc kind:research platform:ghsa sev:high tech:go tech:php technique:parser-differential technique:source-review vuln:access-control vuln:auth-bypass

**Target:** Caddy reverse proxy forward_auth + php_fastcgi
**Root cause:** forward_auth copy_headers uses http.Header.Del() for exact-name deletion, but FastCGI export normalizes '-' to '_' via strings.NewReplacer, so underscore aliases collide with trusted CGI variables.
**Steps:**
1. Configure Caddy with `forward_auth` + `copy_headers Remote-User Remote-Groups` in front of `php_fastcgi`.
2. Auth gateway returns only `Remote-User: alice`; Caddy deletes client `Remote-Groups` exactly.
3. Client sends `Remote_Groups: admin` alongside the request.
4. FastCGI export maps `Remote_Groups` to `HTTP_REMOTE_GROUPS=admin`, which the PHP app trusts.
5. Observe `HTTP_REMOTE_GROUPS=admin` in backend despite gateway never returning it.
**How found:** Reporter compared forward_auth's exact http.Header.Del() deletion against FastCGI's hyphen-to-underscore normalizer and noticed the two namespaces diverge, then built a local Caddy+fcgi harness to confirm the collision.
**Exploitation:** Attacker sends underscore-alias headers (e.g. `Remote_Groups: admin`) that bypass exact-name deletion in forward_auth; after FastCGI normalization they become trusted HTTP_* variables consumed by apps like Firefly III remote_user_guard or MediaWiki Auth_remoteuser, granting role injection or user impersonation.
**Escalation:** Probabilistic user impersonation when both gateway and client supply colliding identity headers (Remote-User vs Remote_User) mapping to same HTTP_REMOTE_USER.
**Insight:** When a proxy deletes headers by exact name but a downstream protocol normalizes names (hyphen/underscore, case), test alias variants that survive deletion yet collide after normalization — especially CGI/FastCGI HTTP_* exports.

## Proof of concept (copied exactly from the original report)

**Curl request injecting Remote_Groups underscore alias**

```
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"
```

**Caddyfile with forward_auth copy_headers and php_fastcgi**

```
cat > "$tmpdir/Caddyfile" <<EOF
{
	admin off
	auto_https off
	debug
}

:9082 {
	log
	root * $tmpdir/www
	forward_auth 127.0.0.1:19011 {
		uri /auth
		copy_headers Remote-User Remote-Groups
	}
	php_fastcgi 127.0.0.1:19010
}
EOF
```

