Feb 25, 2026·6 min read·5 visits
Fiber v3 on Windows fails to properly sanitize paths containing double-encoded backslashes. The middleware checks for malicious characters *before* decoding the URL, allowing attackers to sneak directory traversal sequences past the filter. Once decoded, the Windows kernel happily interprets the backslashes, granting read access to the entire filesystem.
A high-severity Path Traversal vulnerability in the Fiber Go framework (v3) allows remote attackers to read arbitrary files on Windows servers. By exploiting a logic flaw in the static middleware's sanitization routine, attackers can bypass security checks using double-encoded backslashes, escaping the web root and accessing sensitive system files.
Fiber bills itself as the "Express of Go," promising speed, ease of use, and a familiar developer experience. It’s a fantastic framework—until you deploy it on Windows and try to serve static files. You see, the problem with cross-platform abstractions is that they eventually leak, and when they do, they usually leak file contents.
This specific vulnerability, CVE-2026-25891, is a textbook example of "It works on my machine (which is a MacBook)." The developers relied on Go's standard path package to clean URLs. This is standard practice in the Go community. The path package is strictly POSIX-compliant; it treats forward slashes (/) as separators and everything else as just another character.
But Windows? Windows is the chaotic evil of file systems. It accepts both forward slashes and backslashes (\) as separators. When Fiber's middleware blindly trusts a POSIX cleaning function to sanitize inputs for a Windows file system, it creates a massive logic gap. It's like locking your front door (checking for /../) but leaving the back door ( \..\ ) wide open because your security guard doesn't believe back doors exist.
The root cause here is a beautiful disaster involving three distinct failures: an Order-of-Operations error, a Package Mismatch, and a Recursive Decoding Loop.
First, the Order-of-Operations: The vulnerable code in sanitizePath attempted to filter out dangerous characters—specifically the backslash \—before it finished decoding the URL. It checks the raw input, says "looks clean," and then unescapes the characters. This is the security equivalent of checking a passenger's luggage before they've finished packing it.
Second, the Recursive Decoding Loop: Fiber implements a loop that repeatedly calls url.PathUnescape until no more changes occur. This is intended to handle weirdly encoded URLs, but it serves as a weapon for attackers. If I send %255C, the first pass turns it into %5C. The second pass turns it into \. Since the security check happened before this loop, the lethal backslash appears only after the guard has left the room.
Third, the Package Mismatch: After decoding, the code calls path.Clean(). Remember, this is the POSIX version. If you feed ..\..\Windows to path.Clean(), it shrugs and says, "That's a valid filename with some weird backslashes in it." It doesn't collapse the path. It doesn't remove the ... It just passes it along to the OS. When that string hits the Windows syscalls, the OS treats those backslashes as valid directory separators, and suddenly you're traversing directories.
Let's look at the "smoking gun" in middleware/static/static.go. The vulnerability stems from where the checks are placed relative to the decoding logic.
The Vulnerable Logic (Simplified):
// 1. Check for backslashes prematurely
if strings.Contains(path, "\\") {
return nil, ErrInvalidPath
}
// 2. Decode the path in a loop (The attacker's playground)
for {
decoded, err := url.PathUnescape(path)
if decoded == path {
break
}
path = decoded
}
// 3. Clean using POSIX rules (Ineffective against Windows paths)
path = pathpkg.Clean("/" + path)Do you see the issue? If I send %255C, step 1 sees safe ASCII characters. Step 2 decodes it down to \. Step 3 ignores it. The variable path now contains a backslash traversal.
The Fix (Commit 5913370):
The maintainers fixed this by moving the validation after the decoding loop and, crucially, normalizing the path using filepath (which is OS-aware) instead of just path.
// 1. Decode FIRST
for {
decoded, err := url.PathUnescape(path)
if decoded == path {
break
}
path = decoded
}
// 2. NOW check for malicious characters
if strings.Contains(path, "\\") || strings.Contains(path, "\x00") {
return nil, ErrInvalidPath
}
// 3. Normalize for the specific OS
normalized := filepath.ToSlash(path)
// 4. Clean and Check again
path = pathpkg.Clean("/" + normalized)By ensuring the validation happens on the final representation of the string, the bypass is closed.
Exploiting this is trivially easy once you understand the decoding logic. We don't need fancy tools; a standard web browser or curl is enough. We are targeting a Windows server running Fiber v3.0.0.
The Objective: Read C:\Windows\win.ini.
The Obstacle: The application is serving files from ./public, and we need to go up three levels to reach the drive root.
The Technique: Double URL Encoding.
..\..\..\Windows\win.ini%2E%2E%5C%2E%2E%5C%2E%2E%5CWindows%5Cwin.ini (Blocked by basic filters often, or decoded to \ too early).%255C.When we send %255C, the server receives it. The initial check scans for \ (0x5C). It finds nothing. The loop runs:
%25 becomes %. Result: %5C.%5C becomes \. Result: \.The Final Payload:
curl "http://target-server.com/static/%255C..%255C..%255C..%255CWindows%255Cwin.ini"The server resolves this to .\public\..\..\..\Windows\win.ini. The Windows I/O manager canonicalizes this to C:\Windows\win.ini and serves the file content. Congratulations, you have just compromised the server's confidentiality.
This isn't just about reading win.ini. If an attacker can read arbitrary files on a web server, they own the data.
main.go, config.go), revealing logic flaws and hardcoded secrets..env files or config.json. These files often contain database credentials, API keys, and signing secrets. With a single request: GET /static/%255C..%255C.env.The CVSS score is 7.7 (High), but in the context of a cloud environment where a single credential leak can compromise an entire AWS/Azure account, the real-world impact is critical.
If you are running Fiber v3 on Windows, you are currently exposed. The remediation is straightforward, but urgency is required.
Immediate Action: Update to Fiber v3.1.0 immediately. The patch introduces strict validation that normalizes paths specifically for the underlying OS before attempting to serve files.
Defense in Depth:
C:\Windows? Run your application as a low-privilege user that only has read access to the specific ./public directory.%255C (double-encoded backslash) or %5C (backslash). While you're at it, block .. in URL paths entirely if your application structure permits it.CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P| Product | Affected Versions | Fixed Version |
|---|---|---|
fiber gofiber | >= 3.0.0, < 3.1.0 | 3.1.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) |
| Attack Vector | Network (Remote) |
| CVSS v4.0 | 7.7 (High) |
| Exploit Status | Proof-of-Concept Available |
| Platform | Windows |
| Component | Static Middleware |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.