Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 275/275 feeds/APIs · 469/469 docs · 8/9 advisories · checked 03:42 UTC Customize Public modeDiscuss ChangeIntel on Discord

Echo: Reverse Proxy Forwards Attacker-Supplied X-Real-IP to Upstream by Default

Collected exploitation and prediction evidence

Risk source coverage

CISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.

No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.

cve-program · CVE-2026-118651

Published

Echo is a Go web framework. From 4.0.0 until 5.4.0, the Proxy middleware can preserve an attacker-supplied X-Real-IP header and forward it to an upstream service when Echo.IPExtractor is not configured. A remote client can therefore make upstream services receive a spoofed client address, undermining IP-based access controls, rate limiting, audit logging, or geographic restrictions that trust Echo's forwarded identity. This issue is fixed in Echo 5.4.0.

Source published
28 Oct 2026 19:30:01 UTC
Source modified
29 Oct 2026 20:53:54 UTC
Observed here
29 Oct 2026 21:48:56 UTC
Source terms
CVE Program Terms of Use

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.

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

euvd · EUVD-2026-112406

Published

Echo is a Go web framework. From 4.0.0 until 5.4.0, the Proxy middleware can preserve an attacker-supplied X-Real-IP header and forward it to an upstream service when Echo.IPExtractor is not configured. A remote client can therefore make upstream services receive a spoofed client address, undermining IP-based access controls, rate limiting, audit logging, or geographic restrictions that trust Echo's forwarded identity. This issue is fixed in Echo 5.4.0.

EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.

Original publisher date fields

datePublished: Oct 28, 2026, 7:30:01 PM

dateUpdated: Oct 29, 2026, 8:53:54 PM

Source published
Oct 28, 2026, 7:30:01 PM (timezone not stated)
Source modified
Oct 29, 2026, 8:53:54 PM (timezone not stated)
Observed here
6 Nov 2026 00:43:54 UTC
Source terms
ENISA EUVD · source terms and attribution apply

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.

EUVD CVSS 3.1 · 5.3 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N

nvd · CVE-2026-118651

Published

Echo is a Go web framework. From 4.0.0 until 5.4.0, the Proxy middleware can preserve an attacker-supplied X-Real-IP header and forward it to an upstream service when Echo.IPExtractor is not configured. A remote client can therefore make upstream services receive a spoofed client address, undermining IP-based access controls, rate limiting, audit logging, or geographic restrictions that trust Echo's forwarded identity. This issue is fixed in Echo 5.4.0.

NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.

Source analysis status: Undergoing Analysis

Weakness classifications: CWE-346

Original publisher date fields

published: 2026-10-28T20:17:22.073

lastModified: 2026-10-29T21:17:16.260

Source published
28 Oct 2026 20:17:22 UTC
Source modified
29 Oct 2026 21:17:16 UTC
Observed here
30 Oct 2026 21:45:28 UTC
Source terms
NIST NVD usage terms; underlying source attribution retained

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · [email protected] · Secondary · 5.3 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N

osv · GHSA-99jh-6h7p-pp36

Published

## 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](https://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.

Source published
28 Oct 2026 19:53:24 UTC
Source modified
5 Nov 2026 23:25:35 UTC
Observed here
5 Nov 2026 23:38:32 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

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

Publisher rating · MODERATE

osv · GO-2026-6875

Published

Reverse Proxy Forwards Attacker-Supplied X-Real-IP to Upstream by Default in github.com/labstack/echo

Source published
5 Nov 2026 22:54:32 UTC
Source modified
5 Nov 2026 23:15:15 UTC
Observed here
5 Nov 2026 23:38:32 UTC
Source terms
OSV · upstream source license applies

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · OSV · upstream source license applies. Source text is retained; field grouping and change labels are provided by ChangeIntel.

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 7 Nov 03:42 UTC · 275 of 275 readable · 274 fully read · documentation 469/469 current