Live › vulnerability › CVE-2026-118654
Go to dashboardEcho's static file handlers normalize dot segments after route matching, so a request can reach a file through a public static route even when the direct path is protected by route middleware. An unauthenticated attacker can read files the application meant to guard when those files sit under a static filesystem mount.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-28 20:35 UTC. All times on this page are UTC.
Applications written in Go with Echo that serve files through static mounts whose directory also holds files guarded only by route-level middleware. Apps that serve no static mounts, or that keep protected files outside the mounted directory, are not exposed through this path. The sources do not give more detail on which configurations are exploitable.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| github.com/labstack/echo/v5 | go | < 5.4.0 | 5.4.0 |
| github.com/labstack/echo/v4 | go | < 4.16.0 | 4.16.0 |
go.mod; affected versions are below 4.16.0 and below 5.4.0.e.Static, e.File or middleware.StaticWithConfig mounts that serve a directory also containing files protected by route-level middleware./static/../protected/file) and see whether it is returned.4.16.0 or 5.4.0 or later.Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
Echo v5.2.0 contains a bypass of the fix for GHSA-vfp3-v2gw-7wfq. The fix rejects encoded path separators such as %2F and %5C before static file path unescaping, but two traversal forms still bypass route-level middleware protections for static files.
The primary bypass uses encoded dot-dot segments. A request such as /public/%2E%2E/admin/secret.txt passes the encoded-separator guard, is unescaped to public/../admin/secret.txt, then path.Clean resolves it to admin/secret.txt. If /admin/* is protected by middleware but the static filesystem is mounted at /, the protected file is returned through the public static route.
A second bypass exists when UseEscapedPathForMatching=true. In that mode, the router normalizes encoded separators before the static handler checks the wildcard. The handler receives a decoded wildcard with no % characters, so the encoded-separator guard does not run on the original encoded path.
An unauthenticated attacker can read files that an application intended to protect with route-level middleware, when those files are also reachable through a static filesystem mount after path normalization.
The high-severity variant works with the default router configuration and requires only encoded dot-dot path segments. The direct protected route returns 403, while the public static route returns the protected file content.
The UseEscapedPathForMatching=true variant is lower severity because it requires a non-default router configuration.
Tested vulnerable:
github.com/labstack/echo/v5 v5.2.0master at 5786024ea4d85c01784796e8aa7aac53e007b39bNo patched version is currently known.
GitHub Private Vulnerability Reporting is enabled for labstack/echo. The repository has a root SECURITY.md and accepts security reports through GitHub Security Advisories.
The vulnerable code was revalidated on both current master and the latest release tag:
master: 5786024ea4d85c01784796e8aa7aac53e007b39bv5.2.0, published June 14 2026github.com/labstack/echo/v5The same focused Go tests pass on the checked out v5.2.0 / master commit, proving the bypass remains present in the latest released version and latest main branch at the time of reporting.
The following application shape reproduces the high-severity bypass:
e := echo.New()
g := e.Group("/admin", func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
return echo.NewHTTPError(http.StatusForbidden)
}
})
g.GET("/*", handler)
e.StaticFS("/", fsysWithAdminSecret)
Control request:
GET /admin/secret.txt
Observed result:
403 Forbidden
Bypass request:
GET /public/%2E%2E/admin/secret.txt
Observed result:
200 OK
TOP-SECRET
For the non-default router variant:
e := echo.New()
e.router = echo.NewRouter(echo.RouterConfig{
UseEscapedPathForMatching: true,
})
g := e.Group("/admin", authMiddlewareReturning403)
g.GET("/*", handler)
e.StaticFS("/", fsysWithAdminSecret)
Bypass request:
GET /public%2F..%2Fadmin%2Fsecret.txt
Observed result:
200 OK
TOP-SECRET
I added focused tests against the current Echo codebase. The tests exercise both StaticDirectoryHandler and the static middleware path.
Command:
go test -run TestHostile -v ./...
Observed result:
=== RUN TestHostile_StaticDirectoryHandler_EncodedSeparatorsBlocked
--- PASS: TestHostile_StaticDirectoryHandler_EncodedSeparatorsBlocked (0.00s)
=== RUN TestHostile_StaticDirectoryHandler_EncodedDotDotBypass
--- PASS: TestHostile_StaticDirectoryHandler_EncodedDotDotBypass (0.00s)
=== RUN TestHostile_UseEscapedPathForMatching_Bypass
--- PASS: TestHostile_UseEscapedPathForMatching_Bypass (0.00s)
PASS
ok github.com/labstack/echo/v5 0.013s
=== RUN TestHostile_StaticMiddleware_EncodedSeparatorsBlocked
--- PASS: TestHostile_StaticMiddleware_EncodedSeparatorsBlocked (0.01s)
=== RUN TestHostile_StaticMiddleware_EncodedDotDotBypass
--- PASS: TestHostile_StaticMiddleware_EncodedDotDotBypass (0.00s)
=== RUN TestHostile_StaticMiddleware_DisablePathUnescaping
--- PASS: TestHostile_StaticMiddleware_DisablePathUnescaping (0.00s)
PASS
ok github.com/labstack/echo/v5/middleware 0.012s
The control tests also confirm that the new fix blocks encoded separators such as %2F, %5C, double-encoded slash values, null bytes, and overlong slash encodings.
The fix for GHSA-vfp3 uses a denylist-style guard that checks for encoded path separators before unescaping the static wildcard. This blocks %2F and %5C, but does not block encoded traversal segments such as %2E%2E.
The vulnerable flow is:
public/%2E%2E/admin/secret.txt.%2F or %5C and allows the request.public/../admin/secret.txt.path.Clean resolves it to admin/secret.txt./admin/* route.For UseEscapedPathForMatching=true, the router decodes the path before the static handler sees the wildcard, so the handler cannot detect that encoded separators were used in the original request.
Do not rely only on detecting encoded separators. Reject encoded traversal sequences before unescaping, and reject normalized wildcard values that cross route or static mount boundaries.
Possible fixes:
%2E and %2E%2E in path components.. or .. components.UseEscapedPathForMatching=true, run the static-file guard against the original escaped request path before router normalization, or preserve metadata that the matched wildcard came from encoded separators.Fixed in github.com/labstack/echo/v5 v5.4.0 and github.com/labstack/echo/v4 v4.16.0. The Static middleware and StaticDirectoryHandler (used by Echo.Static, Echo.StaticFS, Group.Static and Group.StaticFS) no longer serve paths with a ., .. or empty segment, including after path unescaping. The encoded variant of the first vector (/public/%2E%2E/admin/secret.txt) was already fixed in v5.2.1 and v4.15.4 (GHSA-vfp3-v2gw-7wfq).
Put a reverse proxy in front of the application that normalizes request paths (removes dot segments and merges slashes), or keep protected files outside the served directory.
This page as .md · .json · in your agent: get_signal("CVE-2026-118654")
NVD data is provided by NIST and is public domain · GitHub Advisory Database, CC-BY 4.0. Posts quoted belong to their authors.