Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
4 changes: 2 additions & 2 deletions test_testping1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
5 changes: 3 additions & 2 deletions testping1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading