For the complete documentation index, see llms.txt. This page is also available as Markdown.

74580 bc insight websocket proxy trusts spoofed forwarded ip for rate limits

Submitted on Apr 23rd 2026 at 15:36:59 UTC by @iam0x04 for Audit Comp | Base Azul

  • Report ID: #74580

  • Report Type: Blockchain/DLT

  • Report severity: Insight

  • Target: https://github.com/base/base/releases/tag/v0.8.0-rc.15

  • Impacts:

Description

Brief/Intro

The Flashblocks websocket proxy trusts X-Forwarded-For unconditionally from every connecting client. Because the per-IP rate limiter keys on this caller-controlled header value, the limit is trivially bypassed by any client malicious or not.

The security control does not function as designed for any direct connection.

Vulnerability Details

The binary binds publicly by default and sets:

  • listen_addr = 0.0.0.0:8545

  • instance_connection_limit = 100

  • per_ip_connection_limit = 10

  • ip_addr_http_header = X-Forwarded-For

When a websocket request arrives, websocket_handler in crates/infra/websocket-proxy/src/server.rs:226 derives the rate-limit key:

connect_addr comes from Axum's ConnectInfo<SocketAddr> (the real TCP peer), but as soon as the configured header (default X-Forwarded-For, wired at bin/websocket-proxy/src/main.rs:77) is present, the real peer address is discarded and replaced by whatever the client put in the header.

extract_addr at crates/infra/websocket-proxy/src/server.rs:276 parses the last comma-separated value of that header and returns it verbatim as the client IP:

There is no trusted-proxy allowlist and no check that the immediate peer is a known load balancer before consulting the header. The resulting client_addr is then passed directly to the rate limiter:

Impact Details

1. Per-IP limit bypass (self-serving)

Any client can set arbitrary X-Forwarded-For values to exceed their own per-IP quota.

2. Targeted connection block (victim framing)

An attacker who knows a target's real IP address can open per_ip_limit connections with X-Forwarded-For: <victim IP>. The rate limiter increments the victim's counter in active_connections. When the real victim subsequently connects, their quota is already exhausted and they receive 429 IP limit exceeded.

References

https://github.com/base/base/blob/de349fc9e8bf61531ce36ca57572345b03b2b097/bin/websocket-proxy/src/main.rs#L59-L65

https://github.com/base/base/blob/de349fc9e8bf61531ce36ca57572345b03b2b097/crates/infra/websocket-proxy/src/server.rs#L226-L238

https://github.com/base/base/blob/de349fc9e8bf61531ce36ca57572345b03b2b097/crates/infra/websocket-proxy/src/server.rs#L276-L296

Proof of Concept

1

1. mock upstream

2

2. Build websocket-proxy (bin/websocket-proxy), and run with reduced limits for fast demo

3

3. run PoC

Observed output

Phase 1 proves the per-IP limit enforces correctly without the header. Phase 2 proves one real IP consumes the entire instance cap via spoofed headers. Phase 3 proves legitimate users are fully locked out.

4

4. target blocked

Phase 1 proves the per-IP limit enforces correctly without the header. Phase 2 proves one real IP consumes the entire instance cap via spoofed headers. Phase 3 proves legitimate users are fully locked out.

Was this helpful?