Skip to content

fix(services): reject non-routable ENR targets in DAS Guardian scan - #817

Open
damilolaedwards wants to merge 1 commit into
ethpandaops:masterfrom
damilolaedwards:fix/dasguardian-ssrf-ip-filter
Open

fix(services): reject non-routable ENR targets in DAS Guardian scan#817
damilolaedwards wants to merge 1 commit into
ethpandaops:masterfrom
damilolaedwards:fix/dasguardian-ssrf-ip-filter

Conversation

@damilolaedwards

@damilolaedwards damilolaedwards commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

POST /api/v1/das-guardian/scan takes an arbitrary ENR from any unauthenticated caller (enabled by default, no auth required) and had dora's server dial whatever IP:port the ENR encoded, via eth-das-guardian's ConnectNode -> host.Connect. Neither the handler, services.DasGuardian, nor the library itself filtered the target, confirmed by driving the actual library dial code end-to-end against a local victim listener and observing the connection arrive.

This let an anonymous caller use dora's server as an outbound proxy: internal network reconnaissance (loopback/RFC1918 targets), or cloud metadata endpoints (169.254.169.254) on AWS/GCP/Azure deployments.

Fix

Validate the parsed ENR's IP in both services.DasGuardian.ScanNode and ScanNodeWithCallback (the two entry points a request can take, covering the plain and random-slot-selection modes) before it ever reaches the dial path. Rejects loopback, RFC1918/ULA private ranges, link-local (which covers the cloud metadata range), unspecified, and multicast addresses using net.IP's standard classifiers.

Tests

  • A table test covering every rejected class plus an ordinary public address that must still pass.
  • An end-to-end test that drives the real, fixed ScanNode against a loopback target and asserts no connection ever reaches a victim listener, the same scenario used to prove the bug in the first place.

Confirmed the end-to-end test fails to build without validateScanTarget (the protection doesn't exist), then restored the fix. go build, go vet, gofmt, and the full services test suite (including -race) are clean.

POST /api/v1/das-guardian/scan takes an arbitrary ENR from any unauthenticated
caller (enabled by default, no auth required) and had dora's server dial
whatever IP:port the ENR encoded, via eth-das-guardian's ConnectNode ->
host.Connect. Neither the handler, services.DasGuardian, nor the library itself
filtered the target - confirmed by driving the actual library dial code
end-to-end against a local victim listener and observing the connection arrive.

This let an anonymous caller use dora's server as an outbound proxy: internal
network reconnaissance (loopback/RFC1918 targets), or cloud metadata endpoints
(169.254.169.254) on AWS/GCP/Azure deployments.

Fix: validate the parsed ENR's IP in both services.DasGuardian.ScanNode and
ScanNodeWithCallback (the two entry points a request can take, covering the
plain and random-slot-selection modes) before it ever reaches the dial path.
Rejects loopback, RFC1918/ULA private ranges, link-local (which covers the
cloud metadata range), unspecified, and multicast addresses using net.IP's
standard classifiers.

Tests: a table test covering every rejected class plus an ordinary public
address that must still pass, and an end-to-end test that drives the real fixed
ScanNode against a loopback target and asserts no connection ever reaches a
victim listener - the same scenario the original PoC used to prove the bug.
Confirmed the end-to-end test fails to build without validateScanTarget (the
protection doesn't exist), then restored the fix. go build, go vet, gofmt, and
the full services test suite (including -race) are clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant