fix(ssrf): block numeric-encoded IPv4 loopback/private addresses#164
Open
Mubashirrrr wants to merge 1 commit into
Open
fix(ssrf): block numeric-encoded IPv4 loopback/private addresses#164Mubashirrrr wants to merge 1 commit into
Mubashirrrr wants to merge 1 commit into
Conversation
The SSRF guard (is_ip_blocked / validate_url_safe) only canonicalized
hostnames via ipaddress.ip_address, which accepts the dotted-quad form
exclusively. HTTP clients (httpx) and the OS resolver also accept the
decimal ("2130706433"), hexadecimal ("0x7f000001"), octal
("017700000001") and short ("127.1") forms of an IPv4 address, all of
which resolve to 127.0.0.1. Those forms were reported as ALLOWED, so a
template URL such as http://2130706433/ bypassed the loopback/private
blocklist entirely and reached internal services — defeating the SSRF
protection.
Normalize the host literal through socket.inet_aton (which mirrors the C
resolver's acceptance of legacy numeric IPv4 forms) before the blocklist
check, so all numeric encodings of a blocked address are caught while
numeric encodings of public addresses remain allowed.
Adds regression tests for decimal/hex/octal/short loopback encodings (at
is_ip_blocked and validate_url_safe level) plus a public numeric address
that must still pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.
Bug (security — SSRF bypass)
flowsint-core/src/flowsint_core/templates/loader/yaml_loader.pyprotects outbound enricher requests against SSRF viavalidate_url_safe→is_ip_blocked.is_ip_blockedcanonicalizes the host withipaddress.ip_address, which accepts only the dotted-quad IPv4 form.However,
httpx(used byTemplateEnricher) and the OS resolver also accept legacy numeric IPv4 encodings that all resolve to127.0.0.1:21307064330x7f000001017700000001127.1ipaddress.ip_addressrejects all of these, sois_ip_blockedreturnedFalseand the URL was reported ALLOWED — bypassing the loopback/private/metadata blocklist. A template URL likehttp://2130706433/therefore reaches internal services.validate_url_safeis reached on the real request path:template_enricher.pyrenders a template URL and callsvalidate_url_safe(url)immediately before thehttpxrequest.Reproduction
End-to-end confirmation against a loopback listener:
httpx.get("http://2130706433:<port>/")andhttpx.get("http://0x7f000001:<port>/")both connect to127.0.0.1and return the internal response, whilevalidate_url_safe(pre-fix) raised nothing.Fix
Normalize the host literal through
socket.inet_aton(which mirrors the C resolver's acceptance of decimal/hex/octal/short IPv4 forms) before the blocklist check. All numeric encodings of a blocked address are now caught; numeric encodings of public addresses (e.g.0x08080808→8.8.8.8) remain allowed. Change is confined tois_ip_blockedplus a small_normalize_iphelper.Regression tests
Added to the existing
TestSSRFProtectionclass intests/templates/test_loader.py:test_is_ip_blocked_numeric_loopback_encodingsandtest_validate_url_safe_numeric_ip_bypass— fail before this change (numeric forms reported as not-blocked / ALLOWED), pass after.test_is_ip_blocked_numeric_public_still_allowed— guards against false positives on a public numeric address.tests/templates/test_loader.py: 43 passed on Python 3.12. (Pre-existing, unrelated failures/errors intests/templates/test_template_enricher.pyexist onmainindependently of this change and are untouched by it.)🤖 Generated with Claude Code