github.com/labstack/echo/v5 · Echo's middleware.Static: `%2F`-encoded path bypasses route/group middleware guarding a statically-served path
Affects github.com/labstack/echo/v5Source GCVE Database
Mentioned vulnerabilities
Not known to be exploited · CVSS 7.5 High : upgrade to 5.4.0 soon.
Data as of 28 Oct 2026, 21:14 UTC
- Patch
- Available – fixed in 5.4.0, 4.16.0
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NExploitation evidence
Disclosed: evidence found.
CVE published 28 Oct 2026
Detectable: no evidence on record.
No public nuclei template indexed
Weaponised: no evidence on record.
No public exploit code indexed in Metasploit or Exploit-DB
Exploited in the wildExploited: no evidence on record.
Not in CISA KEV and no source reports exploitation
Observed by OffSeq MirageSeen by Mirage: no evidence on record.
No attack traffic recorded by OffSeq Mirage sensors
A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.
What to do
Upgrade github.com/labstack/echo/v5 to 5.4.0; github.com/labstack/echo/v4 to 4.16.0.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
| Gogithub.com/labstack/echo/v5 | Affected
| Fixed in
|
| Gogithub.com/labstack/echo/v4 | Affected
| Fixed in
|
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
Description
## Summary When `middleware.Static` is mounted globally (`e.Use(middleware.Static(...))`) and an application relies on a **route- or group-level middleware guard** to restrict a path that also falls under the served static root, a `%2F`-encoded request bypasses that guard while the file is still served. This is the same encoding/decoding mismatch fixed for `StaticFS`/`StaticDirectoryHandler` in CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq, but the fix did not cover the `middleware.Static` path. In addition, the `StaticConfig.EnablePathUnescaping` docstring states the default configuration prevents exactly this bypass, which is inaccurate for the global-mount case. **This is not path traversal:** no file outside the static root is accessed. The served file lives inside the static root; the issue is that access-control expectations set by a route guard are inconsistent between the literal and `%2F`-encoded forms of the same path. ## Details Root cause — the router and the static handler disagree on how to interpret `%2F`: - The router matches routes on the **raw, still-encoded** path, so `GET /admin%2Fsecret.txt` does **not** match a `/admin/...` route or group; any middleware attached to that route/group is therefore skipped. - `middleware.Static` selects its path source in `middleware/static.go` (~L219): with a global `e.Use` mount there is no wildcard route, so it uses `c.Request().URL.Path`, which `net/http` has already **decoded** (`%2F` → `/`). It then `path.Clean`s (~L235) and `Open`s (~L259) `admin/secret.txt`, serving the file. Net effect: for the literal path the guard runs; for the `%2F`-encoded path the guard is skipped but the same file is served. Relevant code: - `middleware/static.go` ~L219 (`p := c.Request().URL.Path`), ~L235 (`path.Clean`), ~L259 (`currentFS.Open`) - `StaticConfig.EnablePathUnescaping` docstring (`static.go` ~L60-67) — claims the default prevents the `/admin%2fprivate.txt` → `/admin/*` guard bypass; this is not true for the global mount. Relationship to prior advisories: - **CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq** (patched 5.2.0): same `%2F` router-vs-handler mismatch, but fixed for `StaticFS`/`StaticDirectoryHandler`. The `middleware.Static` path was not covered — this report is that gap. - **CVE-2026-25766 / GHSA-pgvm-wxw2-hrv9** (patched 5.0.3): a *different* issue (Windows backslash traversal **outside** the root). Referenced only to disambiguate; not the same as this report. ## PoC Environment: `golang:1.25`, module using the official release (`go get github.com/labstack/echo/v5@v5.3.1`, no local `replace`). Static root `public/` contains a marker file `public/admin/secret.txt` = `SECRET`. Minimal app (the pattern under test: static served globally + a group guard intended to protect `/admin`): ```go package main import ( "net/http" "github.com/labstack/echo/v5" "github.com/labstack/echo/v5/middleware" ) func main() { e := echo.New() e.Use(middleware.Static("public")) // serves ./public globally (default filesystem) admin := e.Group("/admin", func(next echo.HandlerFunc) echo.HandlerFunc { return func(c *echo.Context) error { return c.String(http.StatusUnauthorized, "auth required") } }) admin.GET("/secret.txt", func(c *echo.Context) error { return c.String(http.StatusOK, "handler") }) e.Start(":1323") } ``` Requests (unauthenticated): ``` # Literal: route matches -> group guard runs curl -i http://localhost:1323/admin/secret.txt # => 401 # %2F-encoded: route does NOT match -> guard skipped, but middleware.Static serves the file curl -i --path-as-is http://localhost:1323/admin%2Fsecret.txt # => 200, body: SECRET ``` Observed on `v5.3.1`: literal → `401`; `%2F` → `200` with the file contents. For contrast, serving via the (patched) `e.StaticFS("/")` returns `404` for the same `%2F` request, showing the `middleware.Static` path was not covered by the CVE-2026-55677 fix. ## Impact An unauthenticated remote user can retrieve a file served by `middleware.Static` even when the application attached a route/group middleware guard to that path, by substituting `/` with `%2F`. The consequence is limited to files already inside the served static root (CWE-863 authorization inconsistency; CWE-436 interpretation conflict) — **there is no access to files outside the static root**. **Honest caveat on validity / severity:** this only has security impact for applications that (a) serve a directory statically to the public *and* (b) place a sensitive file inside that same directory while relying on a *route-level* guard to protect it. That combination is arguably a misconfiguration — a statically-served directory is public by design — so maintainers may reasonably classify this as a documentation fix (correct the `EnablePathUnescaping` claim) or won't-fix rather than a code vulnerability. It is filed primarily because (1) the router-vs-handler `%2F` inconsistency is the exact class fixed in CVE-2026-55677 but left uncovered for `middleware.Static`, and (2) the doc
References
- github.com/labstack/echo/security/advisories/GHSA-375p-5qhx-8wq4 (opens in a new tab)
- github.com/labstack/echo/commit/b838391e2eade87ae7e5a09d7bb5087bec271ec1 (opens in a new tab)
- github.com/labstack/echo/commit/fc410094b2f3415cf2bf5204082137b205bfd5af (opens in a new tab)
- github.com/labstack/echo (opens in a new tab)
- github.com/labstack/echo/releases/tag/v4.16.0 (opens in a new tab)
- github.com/labstack/echo/releases/tag/v5.4.0 (opens in a new tab)