KEVWire

Security intelligence, with context.

Response snapshot Oct 29, 2026, 03:36 UTC

Back to the wire
Permalink
VulnerabilityCVE-2026-118650

Echo's middleware.Static: `%2F`-encoded path bypasses route/group middleware guarding a statically-served path

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.

At a glance

Exploited
Not known
Severity
7.5 High (CVSS 3.1)
Chance of exploitation
Not estimated
Sources
2 independent sources

What to do

GitHub lists first patched versions: github.com/labstack/echo/v5: 5.4.0; github.com/labstack/echo/v4: 4.16.0. Confirm the affected range and upgrade guidance in the exact advisory.

Read GitHub-reviewed advisory GHSA-375p-5qhx-8wq4 (opens a new tab)

What happened

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.

Continue reading

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.

Cleans (~L235) and Opens (~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):

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 docstring currently promises protection it does not provide.

Patches

Fixed in github.com/labstack/echo/v5 v5.4.0 and github.com/labstack/echo/v4 v4.16.0. With the default settings, the Static middleware resolves files from the same form of the path that the router matched, so percent-encoded paths such as /admin%2Fsecret.txt or /%61dmin/secret.txt no longer reach a file under a guarded route. Paths with ., .. or empty segments are no longer served.

Note: middleware registered with Echo#Use runs before route and group middleware, so route guards never protect files served by a global Static middleware. Keep protected files outside its root directory, or serve them with Echo#Static behind the guard.

Workarounds

Keep files that need protection outside the Static middleware's root directory.

Affected package ranges reported by GitHub: github.com/labstack/echo/v5: < 5.4.0 github.com/labstack/echo/v4: < 4.16.0

Scores and how they were measured

CVSS base severity

7.5

Version 3.1As of

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Measurement source (opens a new tab)

How bad it is if exploited. Not a prediction of exploitation or proof you are exposed.

EPSS probability

Not available

A model's estimate of exploitation in the next 30 days, not certainty or your own risk.

Related technology

Sources

  1. Primary source

    GitHub-reviewed advisory GHSA-375p-5qhx-8wq4 (opens a new tab)

    The public advisory supplies the description, affected package/version ranges, and any listed CVSS metric. GitHub review is not local review.

    Published Checked

  2. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-375p-5qhx-8wq4, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  3. Primary source

    NVD CVE-2026-118650 (opens a new tab)

    NVD record: description, CVSS (the CNA score is preferred), affected products, and publication date. Not evidence of exploitation.

    Published Checked

  4. Primary source

    NVD CVE API 2.0 (opens a new tab)

    The NVD API page that supplied this CVE record.

    Published Date not suppliedChecked

  5. Primary source

    NVD CVE API 2.0 (opens a new tab)

    The NVD API page that supplied this CVE record.

    Published Date not suppliedChecked

  6. Primary source

    NVD CVE API 2.0 (opens a new tab)

    The NVD API page that supplied this CVE record.

    Published Date not suppliedChecked

Cite this record

For factual claims, cite the primary sources above; cite KEVWire for its compilation, dates, and classifications.

APA

KEVWire. (2026, October 28). Echo's middleware.Static: `%2F`-encoded path bypasses route/group middleware guarding a statically-served path (CVE-2026-118650). https://kevwire.com/record/CVE-2026-118650

MLA

"Echo's middleware.Static: `%2F`-encoded path bypasses route/group middleware guarding a statically-served path (CVE-2026-118650)." KEVWire, 28 Oct. 2026, kevwire.com/record/CVE-2026-118650.

Chicago

KEVWire. "Echo's middleware.Static: `%2F`-encoded path bypasses route/group middleware guarding a statically-served path (CVE-2026-118650)." Last modified October 28, 2026. https://kevwire.com/record/CVE-2026-118650.

Evidence summary (plain text)

Echo's middleware.Static: `%2F`-encoded path bypasses route/group middleware guarding a statically-served path (Oct 28, 2026).
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.
CVSS 7.5 (version 3.1; evidence as of Oct 28, 2026; https://nvd.nist.gov/vuln/detail/CVE-2026-118650).
Sources:
GitHub-reviewed advisory GHSA-375p-5qhx-8wq4: https://github.com/advisories/GHSA-375p-5qhx-8wq4
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc
NVD CVE-2026-118650: https://nvd.nist.gov/vuln/detail/CVE-2026-118650
NVD CVE API 2.0: https://services.nvd.nist.gov/rest/json/cves/2.0?lastModStartDate=2026-10-28T20:10:59.549Z&lastModEndDate=2026-10-28T21:40:57.581Z&resultsPerPage=500&startIndex=0
NVD CVE API 2.0: https://services.nvd.nist.gov/rest/json/cves/2.0?lastModStartDate=2026-10-28T19:41:09.844Z&lastModEndDate=2026-10-28T21:10:56.744Z&resultsPerPage=500&startIndex=0
NVD CVE API 2.0: https://services.nvd.nist.gov/rest/json/cves/2.0?lastModStartDate=2026-10-28T19:06:02.311Z&lastModEndDate=2026-10-28T20:40:57.063Z&resultsPerPage=500&startIndex=500
Record ID: CVE-2026-118650.
Record origin: Ingested record.
AI evidence: Not assessed.
Known exploitation: Not assessed.
Zero-day status: Not assessed. KEV membership alone does not establish zero-day status.
About this data

This record was ingested from a data source. Retrieved: Oct 29, 2026, 03:20 UTC. Ingestion is not an independent editorial investigation.

A CISA KEV listing documents known exploitation, not necessarily a zero-day; absence from the catalog does not prove a vulnerability has never been exploited.

Technology tags indicate possible relevance, not verified exposure. Confirm the affected product, version, and configuration against your own inventory; KEVWire does not scan your systems.

A personal reading checklist on this device, not verified remediation.

Record JSON

Your account

Email alerts

Email alerts are coming as part of KEVWire Premium: join the waitlist. To follow your stack today, use its RSS feed, which honors your filters, or connect an AI agent through the API.

Suggest an improvement

Ideas, missing sources, corrections, and bugs are all welcome. No account needed. Suggestions are reviewed by a person before anything changes.

Record details

Record detail