Vulnerability GHSA-wchh-mrv2-5h4q
Summary
ezBookkeeping: Source-IP allowlist (MCP & API token) is bypassable via a spoofed `X-Forwarded-For` header
Details
Summary
A remote attacker from any IP address defeats the operator-configured source-IP allowlist (mcp_allowed_remote_ips / api_token_allowed_remote_ips) by sending a single X-Forwarded-For: <allowlisted-ip> header. The router is created with gin.New() and never calls SetTrustedProxies, so gin trusts every proxy by default and c.ClientIP() returns the attacker-supplied header verbatim. The same forged value is also written to Transaction.CreatedIp and the access log, so the attacker's real IP never appears in any record.
Details
cmd/webserver.go:104 builds the router with gin.New(). A grep of cmd/ and pkg/ confirms SetTrustedProxies is never called, leaving gin's default trusted-proxy range of 0.0.0.0/0 and ::/0. WebContext (pkg/core/context_web.go:39) embeds *gin.Context and does not override ClientIP().
Both allowlist middlewares match the operator allowlist against this attacker-controlled value:
// pkg/middlewares/mcp_server_ip_limit.go:19
if config.MCPAllowedRemoteIPs[i].Match(c.ClientIP()) { c.Next(); return }
The identical c.ClientIP() is consumed by the audit trail at pkg/api/transactions.go:1078 (createNewTransactionModel(..., c.ClientIP()) → CreatedIp) and the access log (pkg/middlewares/request_log.go:28), so one header both bypasses the gate and forges attribution.
PoC
Runtime PoC (no live deployment required) — pkg/middlewares/xff_bypass_runtime_test.go, pkg/middlewares/xff_chain_runtime_test.go:
go test ./pkg/middlewares/ -run 'TestACCESSCONTROL001_XFFBypass|TestCHAIN_AllowlistBypass' -v
Output:
[control] no XFF, RemoteAddr=1.2.3.4 -> status=400 (correctly blocked)
[exploit] XFF=10.0.0.1, RemoteAddr=1.2.3.4 -> status=200, ClientIP=10.0.0.1 (bypassed)
gate status = 200 | created_ip that would be persisted = "10.0.0.1"
Against a live instance with mcp_allowed_remote_ips=10.0.0.1:
curl -s -o /dev/null -w "%{http_code}\n" https://target/mcp # 403 (blocked)
curl -s -o /dev/null -w "%{http_code}\n" -H "X-Forwarded-For: 10.0.0.1" https://target/mcp # passes IP gate
Impact
Defeats an explicitly advertised, operator-configured security control on the MCP and API-token surfaces, from any source IP, with a single header and no authentication to the gate itself. It also destroys non-repudiation: every action is logged under the spoofed allowlisted IP, so audit trails point at the trusted host instead of the attacker. (The MCP/API actions themselves still require a valid token — this neutralizes the compensating IP control, not the JWT layer.)
Related Vulnerabilities
Other vulnerabilities affecting the same packages