Skip to content

fix(ssrf-gate): flag RFC 6598 shared address space (100.64.0.0/10) - #18

Open
Mr-Neutr0n wants to merge 1 commit into
PentesterFlow:mainfrom
Mr-Neutr0n:fix/ssrf-gate-rfc6598
Open

fix(ssrf-gate): flag RFC 6598 shared address space (100.64.0.0/10)#18
Mr-Neutr0n wants to merge 1 commit into
PentesterFlow:mainfrom
Mr-Neutr0n:fix/ssrf-gate-rfc6598

Conversation

@Mr-Neutr0n

Copy link
Copy Markdown

privateIPv4Reason in src/tools/privateHost.ts covers loopback, RFC1918, link-local/metadata and this-network, but has no case for RFC 6598 shared address space, 100.64.0.0/10. So the gate returns '' and web_fetch / http reach anything in that range with no prompt at all.

That range is not theoretical infrastructure. In practice it holds:

  • Tailscale / WireGuard overlays — every tailnet node gets a 100.64.0.0/10 address, and 100.100.100.100 is the MagicDNS resolver
  • Carrier-grade NAT segments
  • several cloud providers' internal service endpoints

So on any machine joined to a tailnet — pretty common on a testing box — the agent can reach internal hosts silently. That is the exact outcome the module header says it exists to prevent ("so the agent can't silently reach internal services").

Verified against main:

privateHostReason('100.100.100.100')  // '' -> no prompt
privateHostReason('100.64.0.1')       // '' -> no prompt

Scope

One range, one line. I deliberately did not add 198.18.0.0/15, 192.0.0.0/24, multicast or 240/4 — they are reserved but nobody runs internal HTTP services on them, and padding the gate with ranges that never fire only trains people to ignore it. 100.64/10 earns its place because real infrastructure lives there.

This also does not change the permissive posture. It adds a prompt, not a block — same as every other range here, and consistent with the H2 decision in AUDIT.md that reaching internal addresses is often the goal. Under YOLO it is auto-approved as before.

Tests

src/tools/privateHost.ts had no test file, so this adds one — 24 cases covering the existing ranges as well as the new one, including the boundaries that matter:

  • 100.63.255.255 and 100.128.0.1 must stay unflagged (just outside the /10)
  • 172.15.0.1 / 172.32.0.1 must stay unflagged (existing 172.16/12 boundary, previously untested)
  • IPv4-mapped IPv6, trailing FQDN dot, bracketed IPv6 — all previously untested normalizations
  • parseHTTPURL scheme rejection

The three 100.64/10 cases fail on main and pass with the change; the other 21 pass both ways, so they document existing behaviour rather than assert the fix.

Full suite: 63 files, 681 tests, all passing.


I use AI assistance in my workflow; the behaviour above is from runs against main at 117c95c.

privateIPv4Reason had no case for 100.64.0.0/10, so a web_fetch or http
request to a tailnet host, a CGNAT address, or a cloud provider's internal
endpoint in that range passed the gate with no prompt. Adds the range and
the module's first test file.
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