From 941396f2650245f0a5df01fec1760b25aab7cdc2 Mon Sep 17 00:00:00 2001 From: BitterPanda Date: Mon, 1 Dec 2025 11:15:40 +0100 Subject: [PATCH 1/4] remove test_stored_ssrf from qa skip list --- .github/workflows/qa-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 9d16ce161c92b0bd5c627fbd530ac567aecd2f19 Mon Sep 17 00:00:00 2001 From: BitterPanda Date: Mon, 1 Dec 2025 14:35:28 +0100 Subject: [PATCH 2/4] ADD ipv6 to ipv4 mapped ip for stored ssrf --- aikido_zen/vulnerabilities/ssrf/imds_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aikido_zen/vulnerabilities/ssrf/imds_test.py b/aikido_zen/vulnerabilities/ssrf/imds_test.py index 0f79d2f4a..5ad94f32a 100644 --- a/aikido_zen/vulnerabilities/ssrf/imds_test.py +++ b/aikido_zen/vulnerabilities/ssrf/imds_test.py @@ -6,11 +6,12 @@ def test_returns_true_for_imds_ip_addresses(): assert is_imds_ip_address("169.254.169.254") is True assert is_imds_ip_address("fd00:ec2::254") is True - def test_returns_false_for_non_imds_ip_addresses(): assert is_imds_ip_address("1.2.3.4") is False 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 # --- Tests --- def test_trusted_hostname_returns_none(): From ea1bdebfe14c3bfd963b78e8941d5e53655067b1 Mon Sep 17 00:00:00 2001 From: bitterpanda Date: Mon, 1 Dec 2025 14:46:53 +0100 Subject: [PATCH 3/4] Add map_ipv4_to_ipv6 function with tests --- .../helpers/ip_matcher/map_ipv4_to_ipv6.py | 9 +++++++++ .../ip_matcher/map_ipv4_to_ipv6_test.py | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6.py create mode 100644 aikido_zen/helpers/ip_matcher/map_ipv4_to_ipv6_test.py 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 From 97d85c3986d48222120eb9d4dc9a7835c8472995 Mon Sep 17 00:00:00 2001 From: bitterpanda Date: Mon, 1 Dec 2025 14:54:40 +0100 Subject: [PATCH 4/4] add ipv4-mapped ipv6 addresses to imds check --- aikido_zen/vulnerabilities/ssrf/imds.py | 35 +++++--------------- aikido_zen/vulnerabilities/ssrf/imds_test.py | 4 +++ 2 files changed, 13 insertions(+), 26 deletions(-) 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 5ad94f32a..969ddaa3a 100644 --- a/aikido_zen/vulnerabilities/ssrf/imds_test.py +++ b/aikido_zen/vulnerabilities/ssrf/imds_test.py @@ -6,12 +6,16 @@ def test_returns_true_for_imds_ip_addresses(): assert is_imds_ip_address("169.254.169.254") is True assert is_imds_ip_address("fd00:ec2::254") is True + def test_returns_false_for_non_imds_ip_addresses(): assert is_imds_ip_address("1.2.3.4") is False 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():