diff --git a/.github/workflows/qa-tests.yml b/.github/workflows/qa-tests.yml index e70babe5f..277c7de03 100644 --- a/.github/workflows/qa-tests.yml +++ b/.github/workflows/qa-tests.yml @@ -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 diff --git a/aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6.py b/aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6.py new file mode 100644 index 000000000..8c1acee29 --- /dev/null +++ b/aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6.py @@ -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}" diff --git a/aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6_test.py b/aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6_test.py new file mode 100644 index 000000000..a37849ae0 --- /dev/null +++ b/aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6_test.py @@ -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 diff --git a/aikido_zen/vulnerabilities/ssrf/imds.py b/aikido_zen/vulnerabilities/ssrf/imds.py index c11230140..f04605a51 100644 --- a/aikido_zen/vulnerabilities/ssrf/imds.py +++ b/aikido_zen/vulnerabilities/ssrf/imds.py @@ -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 diff --git a/aikido_zen/vulnerabilities/ssrf/imds_test.py b/aikido_zen/vulnerabilities/ssrf/imds_test.py index 0f79d2f4a..969ddaa3a 100644 --- a/aikido_zen/vulnerabilities/ssrf/imds_test.py +++ b/aikido_zen/vulnerabilities/ssrf/imds_test.py @@ -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."""