Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
The probability is the direct output of the EPSS model, and conveys an overall sense of the threat of exploitation in the wild. The percentile measures the EPSS probability relative to all known EPSS scores. Note: This data is updated daily, relying on the latest available EPSS model version. Check out the EPSS documentation for more details.
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applicationsUpgrade github.com/dunglas/frankenphp to version 1.11.2 or higher.
Affected versions of this package are vulnerable to Incorrect Behavior Order: Validate Before Canonicalize via the splitPos function. An attacker can cause unintended script execution by crafting a request path containing specific multi-byte Unicode characters, which manipulates the calculation of SCRIPT_FILENAME and may result in the execution of arbitrary files as scripts.
Note:
This is only exploitable if user-uploaded files are stored within the document root or a reachable path.
This vulnerability can be mitigated by ensuring user-uploaded files are stored outside of the public document root and by implementing strict WAF rules to reject requests containing specific multi-byte Unicode characters in the URL path.
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.
// We construct a path where the byte expansion shifts the index.
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)
// Current Unsafe Behavior:
fmt.Printf("orig[:pos] (Calculated Script)=%q\n", path[:pos])
fmt.Printf("orig[pos:] (Calculated PathInfo)=%q\n", path[pos:])
// Expected Safe Behavior:
want := strings.Index(path, split) + len(split)
fmt.Printf("expected splitPos=%d\n", want)
fmt.Printf("expected orig[:]=%q\n", path[:want])
}