Investigate in your agent.

Turn this advisory into a focused investigation. Ask your agent to check your project’s versions, explain the evidence, and suggest a compatible fix.

Your agent needs remote MCP support and access to https://cve.new/mcp.

Read the setup guide

Skip to content
Check your stack
CVE-2026-118654medium5.9

Echo static file middleware route protection bypass via encoded dot-dot traversal

Impact

Echo v5.2.0 has a route protection bypass vulnerability, allowing an unauthenticated attacker to read protected files through a static filesystem mount. The attacker must have access to the static filesystem and use encoded dot-dot path segments. The impact is that protected files can be accessed through the public static route.

Summarized by @cf/meta/llama-3.3-70b-instruct-fp8-fast. Verify details against the original sources.
Applies when

The high-severity variant works with the default router configuration and requires only encoded dot-dot path segments. The UseEscapedPathForMatching=true variant is lower severity because it requires a non-default router configuration.

Affected packages

Start with the recorded fix for your release branch. Check your installed version and test compatibility before upgrading. Fixing this advisory does not rule out other vulnerabilities in the release.

Affected products

Vendor version evidence. Product records are separate from automatic dependency matching.

labstack · GitHub_M

echo

2 ranges

Compare with this publisher’s ranges. This does not inspect your installation.

affected

< 4.16.0

>= 5.0.0, < 5.4.0

unaffected

unknown

Other versions: unknown

Original source description

Summary

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.

Impact

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.

Affected versions

Tested vulnerable:

  • github.com/labstack/echo/v5 v5.2.0
  • Current master at 5786024ea4d85c01784796e8aa7aac53e007b39b

No patched version is currently known.

Policy and version checks

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:

  • Current master: 5786024ea4d85c01784796e8aa7aac53e007b39b
  • Latest release: v5.2.0, published June 14 2026
  • Package: github.com/labstack/echo/v5

The 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.

Reproduction

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

Proof of concept evidence

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.

Root cause

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:

  1. The static route wildcard accepts public/%2E%2E/admin/secret.txt.
  2. The encoded-separator guard sees no %2F or %5C and allows the request.
  3. The handler unescapes the wildcard to public/../admin/secret.txt.
  4. path.Clean resolves it to admin/secret.txt.
  5. The file is served through the public static route instead of the protected /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.

Remediation

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:

  1. Extend the pre-unescape guard to reject encoded dot segments such as %2E and %2E%2E in path components.
  2. After unescaping, reject any wildcard where cleaning changes the path in a way that removes . or .. components.
  3. For 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.

Patches

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).

Workarounds

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.

Inside the record

Evidence available

Package identity
Version evidence
Remediation guidance · not supplied
Weakness classification
Additional publishers · not supplied

Describes the evidence in this view, not whether the product is safe.

Original records

CVE-2026-118654

View the latest unmodified CVE JSON from the official source. It may be newer than this imported page.

cve.new JSON →

Underlying weaknesses

The source’s classification of what caused the vulnerability.

CWE-22 ↗

CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

GitHub_M

Related →
Contributing organizations · 1
GitHub_M

Assigning organization

2026-10-28

What changed

Follow changes
No substantive changes have been recorded here yet. History starts when cve.new observes a change after tracking is enabled; earlier edits are not reconstructed.

Large field values are marked as truncated previews. Original source snapshots remain retained.

References

6 sources