fix(fetch_url): block cloud-metadata via IPv4-mapped IPv6 in SSRF guard#463
Open
steps-re wants to merge 1 commit into
Open
fix(fetch_url): block cloud-metadata via IPv4-mapped IPv6 in SSRF guard#463steps-re wants to merge 1 commit into
steps-re wants to merge 1 commit into
Conversation
The fetch_url SSRF guard guarantees cloud-metadata endpoints (e.g. 169.254.169.254) are ALWAYS blocked, even when CAI_FETCH_ALLOW_INTERNAL is set for an authorised internal pentest. That guarantee could be bypassed with the IPv4-mapped IPv6 form of the IMDS address, e.g. http://[::ffff:169.254.169.254]/ (or its hex form ::ffff:a9fe:a9fe), because the metadata block is a plain string comparison against the bare IPv4 literal. With allow_internal=True the private/reserved check is skipped, so nothing caught the mapped form and the request reached IMDS. Unwrap IPv4-mapped IPv6 addresses to their embedded IPv4 before the metadata and private-range checks in both the literal-IP and DNS-resolved paths. This also hardens the private-range check on Python versions that do not classify mapped addresses as private/link-local. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Mike German <mike@stepsventures.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
fetch_urlSSRF guard can be bypassed to reach cloud-metadata endpoints (AWS/GCP/Azure IMDS) via the IPv4-mapped IPv6 form of the address.Details
_check_ssrfdocuments that metadata endpoints like169.254.169.254are "ALWAYS blocked, regardless ofCAI_FETCH_ALLOW_INTERNAL." That block was a string comparison against the bare IPv4 literal, so it could be evaded:http://[::ffff:169.254.169.254]/http://[::ffff:a9fe:a9fe]/(hex form)CAI_FETCH_ALLOW_INTERNAL=trueis a documented mode for authorized internal pentests; in that mode the private-range check is intentionally skipped, leaving the metadata block as the only protection — which this bypasses.Fix
Add
_unwrap_mapped_ip()to unwrap IPv4-mapped IPv6 addresses to their embedded IPv4 before the metadata and private-range checks, in both the literal-IP and DNS-resolved paths. This also hardens the private-range check on Python versions that don't classify mapped addresses as private/link-local. Behavior for allowed/public addresses is unchanged.Verification
[test]extras, Python 3.13.allow_internal=True,_check_ssrf("http://[::ffff:169.254.169.254]/")returned ALLOWED.allow_internalstates; mapped loopback blocked by default; public IPs still allowed.pytest tests/tools/web/test_fetch_url.py→ 71 passed (69 baseline + 2 new regression tests).ruff==0.9.2clean on changed files.Framing: this is reachable only when the operator has opted into
CAI_FETCH_ALLOW_INTERNAL, so it hardens the explicit "metadata always blocked" guarantee rather than a remote-by-default exploit.