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
2 changes: 1 addition & 1 deletion .github/workflows/qa-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ jobs:
dockerfile_path: ./zen-demo-python/Dockerfile
app_port: 8080
sleep_before_test: 10
skip_tests: test_stored_ssrf,test_bypassed_ip_for_geo_blocking,test_demo_apps_generic_tests,test_path_traversal,test_wave_attack
skip_tests: test_bypassed_ip_for_geo_blocking,test_demo_apps_generic_tests,test_path_traversal,test_wave_attack
9 changes: 9 additions & 0 deletions aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def map_ipv4_to_ipv6(ip: str) -> str:
if "/" not in ip:
# No CIDR suffix, assume /32
return f"::ffff:{ip}/128"

parts = ip.split("/")
suffix = int(parts[1])
# Add 96 to the suffix, since ::ffff: is 96 bits
return f"::ffff:{parts[0]}/{suffix + 96}"
19 changes: 19 additions & 0 deletions aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest
from .map_ipv4_to_ipv6 import map_ipv4_to_ipv6


@pytest.mark.parametrize(
"ip, expected",
[
("127.0.0.0", "::ffff:127.0.0.0/128"),
("127.0.0.0/8", "::ffff:127.0.0.0/104"),
("10.0.0.0", "::ffff:10.0.0.0/128"),
("10.0.0.0/8", "::ffff:10.0.0.0/104"),
("10.0.0.1", "::ffff:10.0.0.1/128"),
("10.0.0.1/8", "::ffff:10.0.0.1/104"),
("192.168.0.0/16", "::ffff:192.168.0.0/112"),
("172.16.0.0/12", "::ffff:172.16.0.0/108"),
],
)
def test_map_ipv4_to_ipv6(ip, expected):
assert map_ipv4_to_ipv6(ip) == expected
35 changes: 9 additions & 26 deletions aikido_zen/vulnerabilities/ssrf/imds.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,24 @@
is_imds_ip_address, is_trusted_hostname, resolves_to_imds_ip
"""

from aikido_zen.helpers.ip_matcher import IPMatcher
from aikido_zen.helpers.ip_matcher.map_ipv4_to_ipv6 import map_ipv4_to_ipv6

class BlockList:
"""A list of IP's that shouldn't be accessed"""

def __init__(self):
self.blocked_addresses = {"ipv4": set(), "ipv6": set()}

def add_address(self, address, address_type):
"""Add an address to this list"""
if address_type in self.blocked_addresses:
self.blocked_addresses[address_type].add(address)

def check(self, address, address_type=None):
"""Check if the IP is on the list"""
if address_type:
return address in self.blocked_addresses.get(address_type, set())
return any(
address in addresses for addresses in self.blocked_addresses.values()
)


# Create an instance of BlockList
imds_addresses = BlockList()
imds_addresses = IPMatcher()

# Block the IP addresses used by AWS EC2 instances for IMDS
imds_addresses.add_address("169.254.169.254", "ipv4")
imds_addresses.add_address("fd00:ec2::254", "ipv6")
imds_addresses.add("169.254.169.254")
imds_addresses.add("fd00:ec2::254")
imds_addresses.add(map_ipv4_to_ipv6("169.254.169.254"))

# Block the IP address used by Alibaba Cloud
imds_addresses.add_address("100.100.100.200", "ipv4")
imds_addresses.add("100.100.100.200")
imds_addresses.add(map_ipv4_to_ipv6("100.100.100.200"))


def is_imds_ip_address(ip):
"""Checks if the IP is an imds ip"""
return imds_addresses.check(ip) or imds_addresses.check(ip, "ipv6")
return imds_addresses.has(ip)


# Trusted hostnames for Google Cloud
Expand Down
5 changes: 5 additions & 0 deletions aikido_zen/vulnerabilities/ssrf/imds_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ def test_returns_false_for_non_imds_ip_addresses():
assert is_imds_ip_address("example.com") is False


def test_is_imds_ip_address_ipv6_mapped():
assert is_imds_ip_address("::ffff:169.254.169.254") is True
assert is_imds_ip_address("::ffff:100.100.100.200") is True


# --- Tests ---
def test_trusted_hostname_returns_none():
"""Test that trusted hostnames always return None."""
Expand Down
Loading