CVE-2026-118656
Echo: Open Redirect via Control-Character Prefix Defeating sanitizeURI
Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, sanitizeURI checks only the first two bytes of a redirect target before deciding whether it begins with an absolute-path separator pair. When AddTrailingSlash or RemoveTrailingSlash is configured with a redirect status, a slash, TAB, separator path form can evade the guard, be emitted in the Location header, and be normalized by a browser into an attacker-controlled host. This issue is fixed in 4.16.0 and 5.4.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- Low
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.1
Echo: Open Redirect via Control-Character Prefix Defeating sanitizeURI
Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, sanitizeURI checks only the first two bytes of a redirect target before deciding whether it begins with an absolute-path separator pair. When AddTrailingSlash or RemoveTrailingSlash is configured with a redirect status, a slash, TAB, separator path form can evade the guard, be emitted in the Location header, and be normalized by a browser into an attacker-controlled host. This issue is fixed in 4.16.0 and 5.4.0.
- Record
CVE-2026-118656- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-601
- Updated
- 2026-11-02
EUVDEUVD-2026-112463
6.1
Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, sanitizeURI checks only the first two bytes of a redirect target before deciding whether it begins with an absolute-path separator pair. When AddTrailingSlash or RemoveTrailingSlash is configured with a redirect status, a slash, TAB, separator path form can evade the guard, be emitted in the Location header, and be normalized by a browser into an attacker-controlled host. This issue is fixed in 4.16.0 and 5.4.0.
- Record
EUVD-2026-112463- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-v753-g4cw-jm48
6.1
Echo has Open Redirect via Control-Character Prefix Defeating sanitizeURI
Summary
Echo's dedicated open-redirect guard, sanitizeURI, decides whether a redirect target is a local path by inspecting exactly two byte positions: uri[0] and uri[1]. If the value begins with a control character such as a tab or a newline, followed by a separator pair, the guard does not fire and the value is written to the Location header unchanged.
Browsers remove tab and newline characters from URLs before parsing them, and treat a backslash as a forward slash for HTTP and HTTPS URLs. A Location value of /\t/attacker.example is therefore resolved by the browser as an absolute URL on attacker.example. Confirmed end to end in a headless browser.
Technical background
The guard
middleware/slash.go:144 (duplicated verbatim at echo.go:958):
func sanitizeURI(uri string) string {
// double slash `\\`, `//` or even `\/` is absolute uri for browsers and by redirecting request to that uri
// we are vulnerable to open redirect attack. so replace all slashes from the beginning with single slash
if len(uri) > 1 && (uri[0] == '\\' || uri[0] == '/') && (uri[1] == '\\' || uri[1] == '/') {
uri = "/" + strings.TrimLeft(uri, `/\`)
}
return uri
}
The condition is anchored at absolute offsets 0 and 1. It cannot see a separator that has been shifted by one byte.
Data flow
RemoveTrailingSlash(middleware/slash.go:112-140) readsurl.Path, whichnet/httphas already percent-decoded, strips the trailing slash, and appends the raw query string:
path := url.Path
...
if l > 0 && strings.HasSuffix(path, "/") {
path = path[:l]
uri := path
if qs != "" {
uri += "?" + qs
}
if config.RedirectCode != 0 {
return c.Redirect(config.RedirectCode, sanitizeURI(uri)) // line 132
}
-
AddTrailingSlashdoes the same in the opposite direction atmiddleware/slash.go:67. -
A request for
/%09/attacker.example/x/hasurl.Pathequal to"/\t/attacker.example/x/"(a literal TAB byte).sanitizeURIevaluatesuri[0] == '\t', the condition is false, and the value passes through untouched. -
Context.Redirect(context.go:735) writes it to theLocationheader. Go's header writer removes CR and LF but leaves TAB, which is all that is required.
Conditional analysis
- Guard:
uri[0]anduri[1]are separators. Trust boundary: server-side sanitization of attacker-influenced path data. - Bypass: place a single control byte before the separator pair. The separator moves to index 1 and the guard's second condition (
uri[1]being a separator) is never reached because the first condition (uri[0]being a separator) already failed. - Browser-side trust boundary: the client's URL parser. It is adversary favourable: control characters are stripped and backslashes are normalized before scheme and authority parsing. The attacker does not need any server-side weakness beyond producing the malformed
Location.
Note that the newline variant does not survive: net/http replaces CR and LF in header values. TAB does survive, and TAB alone is sufficient because the browser strips it.
Proof of concept
Attached PoC: ECHO-2026-002_poc.zip (in this directory). Extract it and follow
ECHO-2026-002/README.md for the full walkthrough. The archive contains a
self-contained Go module (go.mod, go.sum, main.go, README.md).
Quick start:
unzip ECHO-2026-002_poc.zip
cd ECHO-2026-002
# edit go.mod: point the replace directive at your Echo checkout
sed -i 's|=> .*|=> /absolute/path/to/echo|' go.mod
GOFLAGS=-mod=mod go run . # in-process check
GOFLAGS=-mod=mod go run . -serve # real listener on 127.0.0.1:18002
Environment used for verification
- OS: Linux 6.18.33.2-microsoft-standard-WSL2, x86_64
- Go: go1.27.0 linux/amd64
- Target: Echo v5.3.1 (module
github.com/labstack/echo/v5) - Browser: headless Chromium…
- Record
GHSA-v753-g4cw-jm48- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-601
- Updated
- 2026-11-02
NVD
6.1
Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, sanitizeURI checks only the first two bytes of a redirect target before deciding whether it begins with an absolute-path separator pair. When AddTrailingSlash or RemoveTrailingSlash is configured with a redirect status, a slash, TAB, separator path form can evade the guard, be emitted in the Location header, and be normalized by a browser into an attacker-controlled host. This issue is fixed in 4.16.0 and 5.4.0.
- Record
CVE-2026-118656- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-601
- Updated
- 2026-11-02
OSV
6.1
Echo: Open Redirect via Control-Character Prefix Defeating sanitizeURI
Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, sanitizeURI checks only the first two bytes of a redirect target before deciding whether it begins with an absolute-path separator pair. When AddTrailingSlash or RemoveTrailingSlash is configured with a redirect status, a slash, TAB, separator path form can evade the guard, be emitted in the Location header, and be normalized by a browser into an attacker-controlled host. This issue is fixed in 4.16.0 and 5.4.0.
- Record
CVE-2026-118656- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-601
- Updated
- 2026-11-06
OSVDEBIAN-CVE-2026-118656
6.1
Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, sanitizeURI checks only the first two bytes of a redirect target before deciding whether it begins with an absolute-path separator pair. When AddTrailingSlash or RemoveTrailingSlash is configured with a redirect status, a slash, TAB, separator path form can evade the guard, be emitted in the Location header, and be normalized by a browser into an attacker-controlled host. This issue is fixed in 4.16.0 and 5.4.0.
- Record
DEBIAN-CVE-2026-118656- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Updated
- 2026-11-02
OSVGHSA-v753-g4cw-jm48
6.1
Echo has Open Redirect via Control-Character Prefix Defeating sanitizeURI
Summary
Echo's dedicated open-redirect guard, sanitizeURI, decides whether a redirect target is a local path by inspecting exactly two byte positions: uri[0] and uri[1]. If the value begins with a control character such as a tab or a newline, followed by a separator pair, the guard does not fire and the value is written to the Location header unchanged.
Browsers remove tab and newline characters from URLs before parsing them, and treat a backslash as a forward slash for HTTP and HTTPS URLs. A Location value of /\t/attacker.example is therefore resolved by the browser as an absolute URL on attacker.example. Confirmed end to end in a headless browser.
Technical background
The guard
middleware/slash.go:144 (duplicated verbatim at echo.go:958):
func sanitizeURI(uri string) string {
// double slash `\\`, `//` or even `\/` is absolute uri for browsers and by redirecting request to that uri
// we are vulnerable to open redirect attack. so replace all slashes from the beginning with single slash
if len(uri) > 1 && (uri[0] == '\\' || uri[0] == '/') && (uri[1] == '\\' || uri[1] == '/') {
uri = "/" + strings.TrimLeft(uri, `/\`)
}
return uri
}
The condition is anchored at absolute offsets 0 and 1. It cannot see a separator that has been shifted by one byte.
Data flow
RemoveTrailingSlash(middleware/slash.go:112-140) readsurl.Path, whichnet/httphas already percent-decoded, strips the trailing slash, and appends the raw query string:
path := url.Path
...
if l > 0 && strings.HasSuffix(path, "/") {
path = path[:l]
uri := path
if qs != "" {
uri += "?" + qs
}
if config.RedirectCode != 0 {
return c.Redirect(config.RedirectCode, sanitizeURI(uri)) // line 132
}
-
AddTrailingSlashdoes the same in the opposite direction atmiddleware/slash.go:67. -
A request for
/%09/attacker.example/x/hasurl.Pathequal to"/\t/attacker.example/x/"(a literal TAB byte).sanitizeURIevaluatesuri[0] == '\t', the condition is false, and the value passes through untouched. -
Context.Redirect(context.go:735) writes it to theLocationheader. Go's header writer removes CR and LF but leaves TAB, which is all that is required.
Conditional analysis
- Guard:
uri[0]anduri[1]are separators. Trust boundary: server-side sanitization of attacker-influenced path data. - Bypass: place a single control byte before the separator pair. The separator moves to index 1 and the guard's second condition (
uri[1]being a separator) is never reached because the first condition (uri[0]being a separator) already failed. - Browser-side trust boundary: the client's URL parser. It is adversary favourable: control characters are stripped and backslashes are normalized before scheme and authority parsing. The attacker does not need any server-side weakness beyond producing the malformed
Location.
Note that the newline variant does not survive: net/http replaces CR and LF in header values. TAB does survive, and TAB alone is sufficient because the browser strips it.
Proof of concept
Attached PoC: ECHO-2026-002_poc.zip (in this directory). Extract it and follow
ECHO-2026-002/README.md for the full walkthrough. The archive contains a
self-contained Go module (go.mod, go.sum, main.go, README.md).
Quick start:
unzip ECHO-2026-002_poc.zip
cd ECHO-2026-002
# edit go.mod: point the replace directive at your Echo checkout
sed -i 's|=> .*|=> /absolute/path/to/echo|' go.mod
GOFLAGS=-mod=mod go run . # in-process check
GOFLAGS=-mod=mod go run . -serve # real listener on 127.0.0.1:18002
Environment used for verification
- OS: Linux 6.18.33.2-microsoft-standard-WSL2, x86_64
- Go: go1.27.0 linux/amd64
- Target: Echo v5.3.1 (module
github.com/labstack/echo/v5) - Browser: headless Chromium…
- Record
GHSA-v753-g4cw-jm48- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-601
- Updated
- 2026-11-06
OSVUBUNTU-CVE-2026-118656
6.1
Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, sanitizeURI checks only the first two bytes of a redirect target before deciding whether it begins with an absolute-path separator pair. When AddTrailingSlash or RemoveTrailingSlash is configured with a redirect status, a slash, TAB, separator path form can evade the guard, be emitted in the Location header, and be normalized by a browser into an attacker-controlled host. This issue is fixed in 4.16.0 and 5.4.0.
- Record
UBUNTU-CVE-2026-118656- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Updated
- 2026-11-05
OSVGO-2026-6883 No score
Echo has Open Redirect via Control-Character Prefix Defeating sanitizeURI in github.com/labstack/echo
Echo has Open Redirect via Control-Character Prefix Defeating sanitizeURI in github.com/labstack/echo
- Record
GO-2026-6883- Updated
- 2026-11-06
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/labstack/echo/v5Go |
before 5.4.0 | 5.4.0 |
github.com/labstack/echo/v4Go |
before 4.16.0 | 4.16.0 |
golang-github-labstack-echoDebian:12 |
All versions, plus listed versions 4.11.1-14.11.1-24.12.0-14.2.1-3 |
None known |
golang-github-labstack-echoDebian:13 |
All versions, plus listed versions 4.12.0-1 |
None known |
golang-github-labstack-echoDebian:14 |
All versions, plus listed versions 4.12.0-1 |
None known |
golang-github-labstack-echo.v2Ubuntu:20.04:LTS |
All versions, plus listed versions 2.2.0-1 |
None known |
golang-github-labstack-echo.v3Ubuntu:20.04:LTS |
All versions, plus listed versions 3.3.5-1 |
None known |
golang-github-labstack-echoUbuntu:22.04:LTS |
All versions, plus listed versions 4.2.1-2 |
None known |
golang-github-labstack-echo.v2Ubuntu:22.04:LTS |
All versions, plus listed versions 2.2.0-2 |
None known |
golang-github-labstack-echo.v3Ubuntu:22.04:LTS |
All versions, plus listed versions 3.3.10-1 |
None known |
golang-github-labstack-echoUbuntu:24.04:LTS |
All versions, plus listed versions 4.11.1-2 |
None known |
golang-github-labstack-echoUbuntu:26.10 |
All versions, plus listed versions 4.12.0-1 |
None known |
golang-github-labstack-echoUbuntu:26.04:LTS |
All versions, plus listed versions 4.12.0-1 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, aliases | OSV | |
| facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-118656 is a medium vulnerability (CVSS 6.1) in Labstack Echo, published October 28, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.20%, the 9th percentile. Fixed in github.com/labstack/echo/v5 5.4.0.
References
github.com/labstack/echo/security/advisories/GHSA-v753-g4cw-jm48
github.com/labstack/echo/commit/b838391e2eade87ae7e5a09d7bb5087bec271ec1
github.com/labstack/echo/commit/fc410094b2f3415cf2bf5204082137b205bfd5af
github.com/labstack/echo/releases/tag/v4.16.0
github.com/labstack/echo/releases/tag/v5.4.0
github.com/advisories/GHSA-v753-g4cw-jm48
Left out here: the rest of 2 source descriptions. The JSON record has them.