From 163d09aaf5181311866b6a0957b5f34b256f8c43 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 02:12:34 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Fi?= =?UTF-8?q?x=20Server-Side=20Request=20Forgery=20for=20Reserved=20IPs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous logic in `is_reachable` blocked loopback, link-local, multicast, and unspecified addresses but missed explicitly reserved subnets and the broadcast IP (`255.255.255.255`). This PR extends the SSRF protection to block IPs that resolve to `is_reserved`. Also includes tests in `test_testping1.py` and a journal entry in `.jules/sentinel.md`. Co-authored-by: ManupaKDU <95234271+ManupaKDU@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ test_testping1.py | 4 ++-- testping1.py | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 41ade79..a506e02 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -10,3 +10,7 @@ **Vulnerability:** The `is_reachable` utility function validated if an IP was correctly formatted, but did not restrict the semantic destination. This allowed Server-Side Request Forgery (SSRF) where the scanner could be manipulated into pinging loopback (`127.0.0.1`), link-local (e.g., AWS metadata `169.254.169.254`), or multicast addresses. **Learning:** Input validation is not just about syntax (like `ipaddress.ip_address`), but also about semantics and intended use. Even a simple `ping` utility can become an SSRF vector if it allows querying internal or restricted network ranges. **Prevention:** Always implement explicit allow-lists or block-lists for network destinations based on business logic. Use built-in library properties (like `ip_obj.is_loopback`, `ip_obj.is_link_local`) to robustly filter out non-routable or restricted administrative IP ranges before attempting network requests. +## 2024-05-24 - [Extended Server-Side Request Forgery Prevention in Utils] +**Vulnerability:** Even when blocking standard internal ranges (loopback, link-local, multicast, unspecified), other reserved IPs like the broadcast address (`255.255.255.255`) could still be targeted. Pinging broadcast addresses can lead to amplification attacks or unintended network noise. +**Learning:** Python's `ipaddress` module separates `is_multicast` from `is_reserved` (which includes broadcast addresses). A comprehensive SSRF defense must cover all non-standard routing destinations. +**Prevention:** Extend network block-lists to include `ip_obj.is_reserved` to catch broadcast addresses and other IETF-reserved network ranges that shouldn't be targeted in a standard scan. diff --git a/test_testping1.py b/test_testping1.py index 66a64ec..9af957a 100644 --- a/test_testping1.py +++ b/test_testping1.py @@ -98,8 +98,8 @@ def test_is_reachable_subprocess_timeout(self, mock_call): @patch('testping1.subprocess.call') def test_is_reachable_ssrf_prevention(self, mock_call): - """Test is_reachable prevents SSRF by rejecting loopback, multicast, etc.""" - ssrf_ips = ['127.0.0.1', '169.254.169.254', '224.0.0.1', '0.0.0.0'] + """Test is_reachable prevents SSRF by rejecting loopback, multicast, reserved, etc.""" + ssrf_ips = ['127.0.0.1', '169.254.169.254', '224.0.0.1', '0.0.0.0', '255.255.255.255'] for ip in ssrf_ips: with self.assertLogs(level='ERROR') as log: self.assertFalse(is_reachable(ip)) diff --git a/testping1.py b/testping1.py index d64f7db..ecea5df 100644 --- a/testping1.py +++ b/testping1.py @@ -37,8 +37,9 @@ def is_reachable(ip, timeout=1): return False # 🛡️ Sentinel: Prevent Server-Side Request Forgery (SSRF) - # Block loopback, link-local, multicast, and unspecified addresses from being pinged. - if ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_multicast or ip_obj.is_unspecified: + # Block loopback, link-local, multicast, unspecified, and reserved addresses from being pinged. + # reserved addresses include the broadcast address (255.255.255.255) + if ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_multicast or ip_obj.is_unspecified or ip_obj.is_reserved: logging.error(f"IP address not allowed for scanning: {ip}") return False