Skip to main content

Labstack Echo CVE-2026-118651

| EUVDEUVD-2026-112406 MEDIUM
Origin Validation Error (CWE-346)
2026-10-28 https://github.com/labstack/echo GHSA-99jh-6h7p-pp36
5.3
CVSS 3.1 · Vendor: https://github.com/labstack/echo
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Send request with forged X-Real-IP header
  2. Delivery
    Echo Proxy preserves header (IPExtractor nil, default)
  3. Exploit
    Spoofed client IP forwarded to upstream
  4. Execution
    Upstream IP allowlist/rate-limit evaluated against forged IP
  5. Impact
    Restricted endpoint reached and audit logs poisoned

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A07:2025 - Authentication Failures
ASVS 5.0
V6 - Authentication Origin validation
Exploitation Conditions

Exploitable only when ALL of the following hold: (1) the application uses Echo's Proxy() or ProxyWithConfig() reverse-proxy middleware; (2) Echo#IPExtractor is left unconfigured (nil), which is the out-of-the-box default from Echo.New(); and (3) at least one upstream service behind the proxy trusts the X-Real-IP header for a security decision - IP allowlist/blocklist, geo-restriction, rate limiting, or audit logging. The attacker simply supplies any X-Real-IP value and it is forwarded verbatim. Limiting factors that reduce real-world risk: setting IPExtractor (e.g. echo.ExtractIPDirect() or echo.ExtractIPFromXFFHeader()) fully closes the hole and is the documented workaround; upstreams that ignore X-Real-IP are unaffected; and the vulnerability yields no impact on Echo itself (no code execution, data read, or DoS) - its entire effect is forged client-IP identity reaching the upstream.

Impact

This is a genuine but moderate-severity, conditionally-exploitable issue rather than a top-priority emergency. The provided CVSS vector (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N = 5.3) correctly reflects remote, unauthenticated, low-complexity reach with only a low integrity impact (spoofed identity) and no confidentiality or availability loss. Per the input, PR:N confirms no authentication is required. The vuln.today decision label is 'this_month' (not act_now/emergency), which is consistent with a real-but-not-urgent fix. No EPSS score, no CISA KEV entry, and no SSVC decision fields were supplied, so automatability and exploitation-maturity signals are absent - status is therefore 'no public exploit identified at time of analysis' for weaponized code, though the GHSA advisory itself embeds a working proof-of-concept (see confidence notes). The key caveat is that real-world impact is entirely contingent on the deployment: the flaw only matters when Echo is used as a reverse proxy via Proxy()/ProxyWithConfig(), IPExtractor is left at its default (nil), AND the upstream service actually trusts X-Real-IP for access control, rate limiting, geo-restriction, or audit logging. Where any of those conditions is absent, the practical risk is negligible despite the network-reachable vector. Note also a tagging conflict: the input Tags label this 'RCE', which the CVSS impact metrics (C:N/I:L/A:N) and the vulnerability class (CWE-346 origin-validation / IP spoofing) directly contradict - this is not a code-execution bug.

Exploit ScenarioDerived

An attacker sends an ordinary HTTP request through an Echo-based reverse proxy and adds a forged header such as 'X-Real-IP: 203.0.113.1' matching a trusted corporate address. Because IPExtractor is unset by default, Echo leaves the attacker-supplied header untouched and relays it to the upstream, which then treats the request as originating from the trusted IP and grants access to an IP-allowlisted admin endpoint (and records the false IP in its logs). A full Go proof-of-concept demonstrating the spoofed value reaching the upstream is published in the GHSA advisory; given AV:N/AC:L/PR:N, exploitation is a single crafted request with no authentication.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Oct 28, 2026

Client IP spoofing in Labstack Echo's reverse-proxy middleware lets remote, unauthenticated attackers dictate the X-Real-IP value that upstream services behind the proxy receive, so upstream IP allowlists, geo-restrictions, rate limits and audit logs can be steered toward an attacker-chosen address. The flaw affects Echo v4 (4.0.0 through 4.16.2, still unpatched) and v5 before 5.4.0, and only manifests when Proxy() or ProxyWithConfig() is used with Echo#IPExtractor left unset and at least one upstream actually trusts X-Real-IP for a security decision. Exploitation is not confirmed in the wild (not in CISA KEV), although the vendor advisory publishes a proof of concept showing the header passing through unchanged; the net effect is forged client identity only, with no code execution, data disclosure or denial of service in Echo itself, and the 'RCE' tag carried by some feeds is not supported by the advisory or the assessed vector.

RemediationAI

Upgrade github.com/labstack/echo/v5 to v5.4.0, the vendor-released patch, which makes the Proxy middleware always set X-Real-IP from Context.RealIP(); read the v5.4.0 changelog first because that release also changes X-Forwarded-Proto/X-Forwarded-Ssl handling, JSONP callback validation, MethodOverride (POST can no longer be overridden to GET/HEAD/OPTIONS/TRACE/CONNECT), static-file path resolution and redirect encoding, and it documents that X-Real-IP sent upstream is now always Context.RealIP(), so an nginx to Echo Proxy to upstream chain must configure Echo#IPExtractor (for example echo.ExtractIPFromRealIPHeader()) to forward the real client address. For github.com/labstack/echo/v4 (4.0.0 through 4.16.2) no vendor-released patch was identified at time of analysis, so the documented workaround is to configure Echo#IPExtractor: use echo.ExtractIPDirect() when no proxy sits in front of the application, or echo.ExtractIPFromXFFHeader() / echo.ExtractIPFromRealIPHeader() with echo.TrustIPRange for fronting proxies on public addresses; with an extractor set, the v4 proxy middleware overwrites X-Real-IP. The trade-off to plan for is that the extractor must match the deployment topology exactly - trusting a header from an untrusted hop, or trusting a range that includes client-reachable addresses, just relocates the spoofing problem - and any upstream that still keys security decisions on X-Real-IP should be re-checked or switched to a value the proxy is known to set. Full guidance is at https://github.com/labstack/echo/security/advisories/GHSA-99jh-6h7p-pp36.

Affected VersionsENISA

Echo 4.0.0, < 5.4.0

Affected Packages

go github.com/labstack/echo/v4 no fix
go github.com/labstack/echo/v5 fix: 5.4.0
euvd echo fix: 5.4.0

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 3 packages in go, euvd

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

Echo is a Go HTTP web framework, and the issue lives in middleware/proxy.go inside ProxyConfig.ToMiddleware(), the per-request header preparation path of the built-in reverse proxy. Before dispatching a request, the code checks req.Header.Get(echo.HeaderXRealIP) == "" || c.Echo().IPExtractor != nil and only then rewrites X-Real-IP from Context.RealIP(); when a client-supplied X-Real-IP is already present and IPExtractor is nil (the state left by Echo.New()), the condition is false and the attacker's value is forwarded verbatim. This is a CWE-346 origin validation error: the proxy fails to establish that the value in a forwarding header originated from a trusted hop rather than from the client. The behavior is deliberate legacy compatibility - the source comment and ip.go both note that the unconfigured IPExtractor fallback 'is not a good choice' - and the fix in v5.4.0 makes the middleware always set X-Real-IP from Context.RealIP(), which resolves to the direct peer address unless an extractor is configured. The v4 branch is left unchanged by the vendor because v4's RealIP() already trusts X-Forwarded-For and X-Real-IP by design, and altering that default would collapse all clients behind a proxy into a single rate-limit bucket. The same v5.4.0 release also hardens Context.Scheme()/X-Forwarded-Proto handling, JSONP callbacks, MethodOverride, redirect encoding and Static path resolution, so upgrading carries behavioral side effects beyond this advisory.

Know when Go gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/labstack/echo) PRIMARY 5.3 MEDIUM
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
vuln.today AI 5.8 MEDIUM

Remote, unauthenticated, single-request trigger (AV:N/AC:L/PR:N/UI:N); impact is forged-IP integrity landing on a separate upstream system, so S:C with I:L and no C/A loss.

3.1 AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:L/SA:N

Primary rating from Vendor (https://github.com/labstack/echo).

CVSS VectorVendor: https://github.com/labstack/echo

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
Low
Availability
None

DescriptionCVE.org

Summary

The Echo proxy middleware (middleware/proxy.go) preserves an incoming X-Real-IP header verbatim and forwards it to the upstream service when Echo#IPExtractor is not configured - which is the default. Because any HTTP client can supply an arbitrary X-Real-IP value, an attacker can cause Echo to relay a spoofed client IP address to every upstream service behind the proxy, undermining upstream IP-based access controls, rate limiting, audit logging, and geo-restriction.

Affected Component

  • middleware/proxy.go - ProxyConfig.ToMiddleware(), header-rewriting block (lines 351-355)

Description

The header-preservation condition passes spoofed values to upstream

ProxyConfig.ToMiddleware() (proxy.go:338-405) prepares forwarding headers before dispatching each request. The X-Real-IP rewrite logic at lines 351-355 reads:

go
// proxy.go:351-355
// Basically it's not good practice to unconditionally pass incoming x-real-ip header to upstream.
// However, for backward compatibility, legacy behavior is preserved unless you configure Echo#IPExtractor.
if req.Header.Get(echo.HeaderXRealIP) == "" || c.Echo().IPExtractor != nil {
    req.Header.Set(echo.HeaderXRealIP, c.RealIP())
}

The condition is TRUE (and therefore overwrites the header with a derived value) only when:

  • The header is absent - req.Header.Get(echo.HeaderXRealIP) == ""; or
  • IPExtractor is explicitly configured - c.Echo().IPExtractor != nil

The condition is FALSE - and the header is left unchanged - when the request arrives with an X-Real-IP header already set and IPExtractor is nil. Because Echo.New() leaves IPExtractor as nil, this is the out-of-the-box behaviour for every application that uses Proxy() or ProxyWithConfig() without extra configuration.

An attacker who sets X-Real-IP to any value causes Echo to forward exactly that value to the upstream, with no modification or validation.

The comment in the source code confirms intent and the security gap

The inline comment at line 351-352 explicitly states:

> "Basically it's not good practice to unconditionally pass incoming x-real-ip header to upstream. However, for backward compatibility, legacy behavior is preserved unless you configure Echo#IPExtractor."

The framework is aware that the default is insecure and retains it for backward compatibility. No warning appears in the Proxy() or ProxyWithConfig() documentation, so operators who follow the standard setup example receive a silently insecure proxy configuration.

This is structurally identical to the documented insecurity in ip.go:27:

> "Note: if you don't set Echo#IPExtractor explicitly, Echo fallback to legacy behavior, which is not a good choice."

Full execution chain

  1. Attacker sends GET /api/admin to the Echo proxy with header X-Real-IP: 203.0.113.1 (a trusted corporate IP)
  2. ToMiddleware() evaluates the guard at line 353: header is present (!= ""), IPExtractor is nil → condition is FALSE
  3. req.Header.Set is not called; the upstream request carries the original, attacker-supplied X-Real-IP: 203.0.113.1
  4. Upstream service checks X-Real-IP against its IP allowlist → finds 203.0.113.1 → grants access
  5. Attacker reaches an endpoint that should be restricted to the corporate IP range

Secondary impact path (audit poisoning):

  • All upstream log entries, rate-limit counters, and geo-restriction checks are evaluated against 203.0.113.1 rather than the real client IP, rendering audit trails and security analytics unreliable.

Proof of Concept

go
package main

import (
    "fmt"
    "log"
    "net/http"
    echo "github.com/labstack/echo/v5"
    "github.com/labstack/echo/v5/middleware"
)

func main() {
    // Upstream server: echoes back the X-Real-IP it received
    upstream := http.NewServeMux()
    upstream.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "upstream sees X-Real-IP: %s\n", r.Header.Get("X-Real-IP"))
    })
    go http.ListenAndServe(":9000", upstream)

    // Echo proxy - IPExtractor deliberately NOT set (the default)
    e := echo.New()
    targets := []*middleware.ProxyTarget{{URL: mustParse("http://127.0.0.1:9000")}}
    e.Use(middleware.Proxy(middleware.NewRoundRobinBalancer(targets)))
    log.Fatal(e.Start(":1323"))
}
bash
# Without spoofed header - upstream sees actual peer address
curl http://127.0.0.1:1323/
# upstream sees X-Real-IP: 127.0.0.1
# With spoofed header - upstream sees attacker-supplied value
curl -H 'X-Real-IP: 198.51.100.77' http://127.0.0.1:1323/
# upstream sees X-Real-IP: 198.51.100.77

The attacker-supplied IP 198.51.100.77 arrives at the upstream unchanged.

Impact

  • Upstream IP-based access controls (allowlists, blocklists, geo-restrictions) can be bypassed by supplying the expected IP in X-Real-IP
  • Upstream rate limiters that key on X-Real-IP are bypassable (same class as the Echo rate-limiter finding, now on every upstream service)
  • Audit logs, intrusion detection systems, and SIEM tools that rely on X-Real-IP record the attacker-chosen IP, not the real source - forensic trails are poisoned
  • Any upstream admin endpoint restricted by IP allowlist is reachable by any attacker who knows or guesses a trusted IP address

Recommended Remediation

Option 1: Always overwrite X-Real-IP from an authoritative source (preferred)

Remove the conditional that preserves the incoming header. Echo, as the edge proxy, is the only authoritative source for the real client IP:

go
// proxy.go - always set X-Real-IP from an authoritative source
req.Header.Set(echo.HeaderXRealIP, c.RealIP())

For this to be safe, operators using Proxy() should also configure IPExtractor:

go
e.IPExtractor = echo.ExtractIPDirect() // no upstream proxy
// or
e.IPExtractor = echo.ExtractIPFromXFFHeader() // behind a trusted upstream LB

Option 2: Document and warn in Proxy() godoc

If backward compatibility must be preserved, add a prominent security warning to Proxy() and ProxyWithConfig():

go
// Proxy returns a Proxy middleware.
//
// SECURITY: By default, an incoming X-Real-IP header supplied by the client
// is forwarded to the upstream unchanged. This allows any client to spoof its
// IP identity to upstream services. Configure Echo#IPExtractor before using
// this middleware to ensure X-Real-IP is derived from a trusted source:
//
//   e.IPExtractor = echo.ExtractIPDirect()
//
// See https://echo.labstack.com/guide/ip-address for details.
func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {

Credit

This vulnerability was discovered and reported by bugbunny.ai.

Patches

Fixed in github.com/labstack/echo/v5 v5.4.0: the Proxy middleware always sets X-Real-IP from Context.RealIP(), which is the address of the direct peer unless Echo#IPExtractor is configured.

github.com/labstack/echo/v4 is not changed, because v4's Context.RealIP() without Echo#IPExtractor trusts X-Forwarded-For and X-Real-IP by design, and changing that default would merge all clients behind a proxy into one rate-limit bucket.

Workarounds

Configure Echo#IPExtractor for your deployment, for example e.IPExtractor = echo.ExtractIPDirect() when there is no proxy in front of the application, or echo.ExtractIPFromXFFHeader() / echo.ExtractIPFromRealIPHeader() (with TrustIPRange for proxies on public addresses). With an extractor set, the Proxy middleware overwrites X-Real-IP in v4 too.

Threat Intelligence Overview

Prevalence
Medium
popular Go web framework, but only proxy-middleware deployments are affected
Threat Intel
1
1 source
Data Quality
4 sources
Confidence: Medium

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC No
EPSS N/A
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
None
System Exposure· derived
Open
Automatable· derived
Yes
Human Impact· derived
Medium

Priority Score

26
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +26
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

More in Go

View all
CVE-2023-44487 HIGH POC
7.5 Oct 10

Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour

CVE-2016-5386 HIGH
8.1 Jul 19

The net/http package in Go through 1.6 does not attempt to address RFC 3875 section 4.1.18 namespace conflicts and there

CVE-2025-68121 CRITICAL POC
10.0 Feb 05

Critical certificate validation bypass in Go crypto/tls during session resumption. If ClientCAs or RootCAs fields are mu

CVE-2019-14809 CRITICAL POC
9.8 Aug 13

net/url in Go before 1.11.13 and 1.12.x before 1.12.8 mishandles malformed hosts in URLs, leading to an authorization by

CVE-2026-90942 CRITICAL POC
9.3 Sep 14

Casdoor through 4.4.0 exposes its instance-wide built-in JWT signing private key unmasked through the /api/get-certs and

CVE-2026-101090 CRITICAL POC
9.3 Sep 27

Host header injection in Nezha 2.2.3's OAuth2 redirect endpoint (/api/v1/oauth2/{provider}) allows an unauthenticated re

CVE-2026-78428 HIGH POC
8.8 Sep 17

Session cross-wiring in SUSE NeuVector before 5.6.1 allows an unauthenticated remote attacker to obtain another user's a

CVE-2026-92791 HIGH POC
8.7 Sep 16

Unauthenticated path traversal in Uber Kraken through 0.1.29 allows remote attackers to read files outside the configure

CVE-2018-7187 HIGH POC
8.8 Feb 16

The "go get" implementation in Go 1.9.4, when the -insecure command-line option is used, does not validate the import pa

CVE-2026-16651 HIGH POC
8.7 Sep 21

Denial of service in Temporal Server allows an authenticated caller holding at least namespace read permission to crash

CVE-2026-82405 HIGH POC
8.7 Sep 23

Account takeover in Klever-Go's KleverUpdateAccountPermission built-in (all versions prior to 1.7.20) allows an attacker

CVE-2026-82409 HIGH POC
8.4 Sep 23

Maliciously crafted on-chain account names processed by Klever-Go prior to 1.7.20 are spliced into Elasticsearch _bulk r

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy