CVE-2026-27590

criticalpriority · track · 39EPSS 0.006 · 49 %ileEUUS
grimoire priorityP2 · Scheduled
Exploitable?
—
no known exploitation
Likely / severe?
0.61% (49th) EPSS
CVSS 9.8 · critical
Is it mine?
3 products
Check your exposure
Who says so?
4 sources
cnnvd · enisa_euvd · osv · nvd

EPSS · scored 2026-10-27

Caddy is an extensible server platform that uses TLS by default. Prior to version 2.11.1, Caddy's FastCGI path splitting logic computes the split index on a lowercased copy of the request path and then uses that byte index to slice the original path. This is unsafe for Unicode because `strings.ToLower()` can change UTF-8 byte length for some characters. As a result, Caddy can derive an incorrect `SCRIPT_NAME`/`SCRIPT_FILENAME` and `PATH_INFO`, potentially causing a request that contains `.php` to execute a different on-disk file than intended (path confusion). In setups where an attacker can control file contents (e.g., upload features), this can lead to unintended PHP execution of non-.php files (potential RCE depending on deployment). Version 2.11.1 fixes the issue.

01

cvss

Technical severity, 0–10. One row per scoring source.

9.8
010

base score 0–10 · higher = more severe · hover a vector to decode⚠ sources disagree · high–critical · 8.1–9.8

v3.19.8criticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Hprimary · NVD / NIST USexploitability 3.9 · impact 5.9
v4.08.9highCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:Psecondary · ENISA EU
v4.08.9highCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:P/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.08.1highCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/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

A stakeholder decision — act, attend, or track — from exploitation and impact.

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

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (3)

Products and version ranges known to be affected.

05

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-5r3v-vc8m-m96gCaddy: Unicode case-folding length expansion causes incorrect split_path index in FastCGI transport2026-02-24ghsa · osvUS

    ### Summary Caddy's FastCGI path splitting logic computes the split index on a lowercased copy of the request path and then uses that byte index to slice the original path. This is unsafe for Unicode because `strings.ToLower()` can change UTF-8 byte length for some characters. As a result, Caddy can derive an incorrect `SCRIPT_NAME`/`SCRIPT_FILENAME` and `PATH_INFO`, potentially causing a request that contains `.php` to execute a different on-disk file than intended (path confusion). In setups where an attacker can control file contents (e.g., upload features), this can lead to unintended PHP execution of non-.php files (potential RCE depending on deployment). ### Details The issue is in `github.com/caddyserver/caddy/modules/caddyhttp/fastcgi.Trasnport.splitPos()` (and the subsequent slicing in `buildEnv()`): ``` lowerPath := strings.ToLower(path) idx := strings.Index(lowerPath, strings.ToLower(split)) return idx + len(split) ``` The returned index is computed in the byte space of lowerPath, but `buildEnv()` applies it to the original path: - `docURI = path[:splitPos]` - `pathInfo = path[splitPos:]` - `scriptName = strings.TrimSuffix(path, fc.pathInfo)` - `scriptFilename = caddyhttp.SanitizedPathJoin(fc.documentRoot, fc.scriptName)` This assumes `lowerPath` and `path` have identical byte lengths and identical byte offsets, which is not true for some Unicode case mappings. Certain characters expand when lowercased (UTF-8 byte length increases), shifting the computed index. This creates a mismatch where `.php` is found in the lowercased string at an offset that does not correspond to the same position in the original string, causing the split point to land later/earlier than intended. ### PoC Create a small Go program that reproduces Caddy's `splitPos()` behavior (compute the `.php` split point on a lowercased path, then use that byte index on the original path): 1. Save this as `poc.go`: ```go package main import ( "fmt" "strings" ) func splitPos(path string, split string) int { lowerPath := strings.ToLower(path) idx := strings.Index(lowerPath, strings.ToLower(split)) if idx < 0 { return -1 } return idx + len(split) } func main() { // U+023A: Ⱥ (UTF-8: C8 BA). Lowercase is ⱥ (UTF-8: E2 B1 A5), longer in bytes. path := "/ȺȺȺȺshell.php.txt.php" split := ".php" pos := splitPos(path, split) fmt.Printf("orig bytes=%d\n", len(path)) fmt.Printf("lower bytes=%d\n", len(strings.ToLower(path))) fmt.Printf("splitPos=%d\n", pos) fmt.Printf("orig[:pos]=%q\n", path[:pos]) fmt.Printf("orig[pos:]=%q\n", path[pos:]) // Expected split: right after the first ".php" in the original string want := strings.Index(path, split) + len(split) fmt.Printf("expected splitPos=%d\n", want) fmt.Printf("expected orig[:]=%q\n", path[:want]) } ``` 2. Run it: ```console go run poc.go ``` Output on my side: ``` orig bytes=26 lower bytes=30 splitPos=22 orig[:pos]="/ȺȺȺȺshell.php.txt" orig[pos:]=".php" expected splitPos=18 expected orig[:]="/ȺȺȺȺshell.php" ``` Expected split is right after the first `.php` (`/ȺȺȺȺshell.php`). Instead, the computed split lands later and cuts the original path after `shell.php.txt`, leaving `.php` as the remainder. ### Impact Security boundary bypass/path confusion in script resolution. In typical deployments, `.php` extension boundaries are relied on to decide what is executed by PHP. This bug can cause Caddy/FPM to execute a different file than intended by confusing `SCRIPT_NAME`/`SCRIPT_FILENAME`. If an attacker can place attacker-controlled content into a file that can be resolved as `SCRIPT_FILENAME` (common in web apps with uploads or writable directories), this can lead to unintended PHP execution of non-.php files and potentially remote code execution. Severity depends on deployment and presence of attacker-controlled file writes, but the primitive itself is remotely triggerable via crafted URLs. This vulnerability was initially reported to FrankenPHP (https://github.com/php/frankenphp/security/advisories/GHSA-g966-83w7-6w38) by @AbdrrahimDahmani. The affected code has been copied/adapted from Caddy, which, according to research, is also affected. The patch is a port of the FrankenPHP patch.

  • GO-2026-4536Unicode case-folding causes incorrect split_path index in github.com/caddyserver/caddy/v22026-02-26vendor · osvUS

    Unicode case-folding causes incorrect split_path index in github.com/caddyserver/caddy/v2

06

also known as (5)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-02-24modified 2026-06-17
08

references (10)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan