From b6637aa88bfec937ced55e4d0d8a6604d0a65e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 20 Mar 2025 12:45:04 +0100 Subject: [PATCH 1/5] Add comments and missing ipv6 ranges --- library/vulnerabilities/ssrf/isPrivateIP.ts | 50 +++++++++++---------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/library/vulnerabilities/ssrf/isPrivateIP.ts b/library/vulnerabilities/ssrf/isPrivateIP.ts index e0b5158e2..30ec41e80 100644 --- a/library/vulnerabilities/ssrf/isPrivateIP.ts +++ b/library/vulnerabilities/ssrf/isPrivateIP.ts @@ -1,35 +1,37 @@ import { IPMatcher } from "../../helpers/ip-matcher/IPMatcher"; -// Taken from https://github.com/frenchbread/private-ip/blob/master/src/index.ts const PRIVATE_IP_RANGES = [ - "0.0.0.0/8", - "10.0.0.0/8", - "100.64.0.0/10", - "127.0.0.0/8", - "169.254.0.0/16", - "172.16.0.0/12", - "192.0.0.0/24", - "192.0.2.0/24", - "192.31.196.0/24", + "0.0.0.0/8", // "This" network (RFC 1122) + "10.0.0.0/8", // Private-Use Networks (RFC 1918) + "100.64.0.0/10", // Shared Address Space (RFC 6598) + "127.0.0.0/8", // Loopback (RFC 1122) + "169.254.0.0/16", // Link Local (RFC 3927) + "172.16.0.0/12", // Private-Use Networks (RFC 1918) + "192.0.0.0/24", // IETF Protocol Assignments (RFC 5736) + "192.0.2.0/24", // TEST-NET-1 (RFC 5737) + "192.31.196.0/24", // AS112 Redirection Anycast (RFC 7535) "192.52.193.0/24", - "192.88.99.0/24", - "192.168.0.0/16", - "192.175.48.0/24", - "198.18.0.0/15", - "198.51.100.0/24", - "203.0.113.0/24", - "240.0.0.0/4", - "224.0.0.0/4", - "255.255.255.255/32", + "192.88.99.0/24", // 6to4 Relay Anycast (RFC 3068) + "192.168.0.0/16", // Private-Use Networks (RFC 1918) + "192.175.48.0/24", // AS112 Redirection Anycast (RFC 7535) + "198.18.0.0/15", // Network Interconnect Device Benchmark Testing (RFC 2544) + "198.51.100.0/24", // TEST-NET-2 (RFC 5737) + "203.0.113.0/24", // TEST-NET-3 (RFC 5737) + "224.0.0.0/4", // Multicast (RFC 3171) + "240.0.0.0/4", // Reserved for Future Use (RFC 1112) + "255.255.255.255/32", // Limited Broadcast (RFC 919) ]; const PRIVATE_IPV6_RANGES = [ - "::/128", // Unspecified address - "::1/128", // Loopback address - "fc00::/7", // Unique local address (ULA) - "fe80::/10", // Link-local address (LLA) - "::ffff:127.0.0.1/128", // IPv4-mapped address + "::/128", // Unspecified address (RFC 4291) + "::1/128", // Loopback address (RFC 4291) + "fc00::/7", // Unique local address (ULA) (RFC 4193) + "fe80::/10", // Link-local address (LLA) (RFC 4291) + "::ffff:127.0.0.1/128", // IPv4-mapped address (RFC 4291) "::ffff:0:0", + "100::/64", // Discard prefix (RFC 6666) + "2001:db8::/32", // Documentation prefix (RFC 3849) + "3fff::/20", // Documentation prefix (RFC 9637) ]; const privateIp = new IPMatcher(); From 5ac7a4b55feed0bca15c9eed066c01635f44d564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 20 Mar 2025 14:15:51 +0100 Subject: [PATCH 2/5] Fix IPv4-mapped address support --- library/helpers/mapIPv4ToIPv6.test.ts | 13 +++++++++++++ library/helpers/mapIPv4ToIPv6.ts | 14 ++++++++++++++ .../ssrf/containsPrivateIPAddress.test.ts | 7 +++++++ library/vulnerabilities/ssrf/isPrivateIP.ts | 9 ++++++--- 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 library/helpers/mapIPv4ToIPv6.test.ts create mode 100644 library/helpers/mapIPv4ToIPv6.ts diff --git a/library/helpers/mapIPv4ToIPv6.test.ts b/library/helpers/mapIPv4ToIPv6.test.ts new file mode 100644 index 000000000..ed619a4af --- /dev/null +++ b/library/helpers/mapIPv4ToIPv6.test.ts @@ -0,0 +1,13 @@ +import * as t from "tap"; +import mapIPv4ToIPv6 from "./mapIPv4ToIPv6"; + +t.test("it works", async (t) => { + t.equal(mapIPv4ToIPv6("127.0.0.0"), "::ffff:127.0.0.0/128"); + t.equal(mapIPv4ToIPv6("127.0.0.0/8"), "::ffff:127.0.0.0/104"); + t.equal(mapIPv4ToIPv6("10.0.0.0"), "::ffff:10.0.0.0/128"); + t.equal(mapIPv4ToIPv6("10.0.0.0/8"), "::ffff:10.0.0.0/104"); + t.equal(mapIPv4ToIPv6("10.0.0.1"), "::ffff:10.0.0.1/128"); + t.equal(mapIPv4ToIPv6("10.0.0.1/8"), "::ffff:10.0.0.1/104"); + t.equal(mapIPv4ToIPv6("192.168.0.0/16"), "::ffff:192.168.0.0/112"); + t.equal(mapIPv4ToIPv6("172.16.0.0/12"), "::ffff:172.16.0.0/108"); +}); diff --git a/library/helpers/mapIPv4ToIPv6.ts b/library/helpers/mapIPv4ToIPv6.ts new file mode 100644 index 000000000..c8525042f --- /dev/null +++ b/library/helpers/mapIPv4ToIPv6.ts @@ -0,0 +1,14 @@ +/** + * Maps an IPv4 address to an IPv6 address. + * e.g. 127.0.0.1/8 -> ::ffff:127 + */ +export default function mapIPv4ToIPv6(ip: string): string { + if (!ip.includes("/")) { + // No CIDR suffix, assume /32 + return `::ffff:${ip}/128`; + } + + const parts = ip.split("/"); + const suffix = Number.parseInt(parts[1], 10); + return `::ffff:${parts[0]}/${suffix + 96}`; +} diff --git a/library/vulnerabilities/ssrf/containsPrivateIPAddress.test.ts b/library/vulnerabilities/ssrf/containsPrivateIPAddress.test.ts index 024c4f606..41bdc5bbc 100644 --- a/library/vulnerabilities/ssrf/containsPrivateIPAddress.test.ts +++ b/library/vulnerabilities/ssrf/containsPrivateIPAddress.test.ts @@ -31,6 +31,9 @@ const publicIPs = [ "fb00::", "fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "fec0::", + "::ffff:1.2.3.4", + "::ffff:172.1.2.3", + "::ffff:192.145.0.0", ]; const privateIPs = [ @@ -139,6 +142,10 @@ const privateIPs = [ "::1", "::ffff:0.0.0.0", "::ffff:127.0.0.1", + "::ffff:127.0.0.2", + "::ffff:10.0.0.1", + "::ffff:172.16.1.2", + "::ffff:192.168.2.2", "fe80::", "fe80::1", "fe80::abc:1", diff --git a/library/vulnerabilities/ssrf/isPrivateIP.ts b/library/vulnerabilities/ssrf/isPrivateIP.ts index 30ec41e80..9c3f77b03 100644 --- a/library/vulnerabilities/ssrf/isPrivateIP.ts +++ b/library/vulnerabilities/ssrf/isPrivateIP.ts @@ -1,4 +1,5 @@ import { IPMatcher } from "../../helpers/ip-matcher/IPMatcher"; +import mapIPv4ToIPv6 from "../../helpers/mapIPv4ToIPv6"; const PRIVATE_IP_RANGES = [ "0.0.0.0/8", // "This" network (RFC 1122) @@ -27,12 +28,14 @@ const PRIVATE_IPV6_RANGES = [ "::1/128", // Loopback address (RFC 4291) "fc00::/7", // Unique local address (ULA) (RFC 4193) "fe80::/10", // Link-local address (LLA) (RFC 4291) - "::ffff:127.0.0.1/128", // IPv4-mapped address (RFC 4291) - "::ffff:0:0", "100::/64", // Discard prefix (RFC 6666) "2001:db8::/32", // Documentation prefix (RFC 3849) "3fff::/20", // Documentation prefix (RFC 9637) -]; + "::ffff:0:0", +].concat( + // Add the IPv4-mapped IPv6 addresses + PRIVATE_IP_RANGES.map(mapIPv4ToIPv6) +); const privateIp = new IPMatcher(); From 0bd949d24546a44512439d8b09a3b7d8e2317506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 20 Mar 2025 14:18:52 +0100 Subject: [PATCH 3/5] Fix comment --- library/helpers/mapIPv4ToIPv6.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/helpers/mapIPv4ToIPv6.ts b/library/helpers/mapIPv4ToIPv6.ts index c8525042f..1b8e50319 100644 --- a/library/helpers/mapIPv4ToIPv6.ts +++ b/library/helpers/mapIPv4ToIPv6.ts @@ -1,6 +1,6 @@ /** * Maps an IPv4 address to an IPv6 address. - * e.g. 127.0.0.1/8 -> ::ffff:127 + * e.g. 127.0.0.0/8 -> ::ffff:127.0.0.0/104 */ export default function mapIPv4ToIPv6(ip: string): string { if (!ip.includes("/")) { From 9378eb4a93b144977518af1583daae9f78123914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 20 Mar 2025 15:04:51 +0100 Subject: [PATCH 4/5] Add comment, remove ::ffff:0:0 --- library/vulnerabilities/ssrf/isPrivateIP.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/vulnerabilities/ssrf/isPrivateIP.ts b/library/vulnerabilities/ssrf/isPrivateIP.ts index 9c3f77b03..4a55780c4 100644 --- a/library/vulnerabilities/ssrf/isPrivateIP.ts +++ b/library/vulnerabilities/ssrf/isPrivateIP.ts @@ -11,7 +11,7 @@ const PRIVATE_IP_RANGES = [ "192.0.0.0/24", // IETF Protocol Assignments (RFC 5736) "192.0.2.0/24", // TEST-NET-1 (RFC 5737) "192.31.196.0/24", // AS112 Redirection Anycast (RFC 7535) - "192.52.193.0/24", + "192.52.193.0/24", // Automatic Multicast Tunneling (RFC 7450) "192.88.99.0/24", // 6to4 Relay Anycast (RFC 3068) "192.168.0.0/16", // Private-Use Networks (RFC 1918) "192.175.48.0/24", // AS112 Redirection Anycast (RFC 7535) @@ -31,7 +31,6 @@ const PRIVATE_IPV6_RANGES = [ "100::/64", // Discard prefix (RFC 6666) "2001:db8::/32", // Documentation prefix (RFC 3849) "3fff::/20", // Documentation prefix (RFC 9637) - "::ffff:0:0", ].concat( // Add the IPv4-mapped IPv6 addresses PRIVATE_IP_RANGES.map(mapIPv4ToIPv6) From 4051df4eb780bfa93949e502a0599e2876266659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 20 Mar 2025 15:05:08 +0100 Subject: [PATCH 5/5] Update library/helpers/mapIPv4ToIPv6.ts Co-authored-by: BitterPanda --- library/helpers/mapIPv4ToIPv6.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/library/helpers/mapIPv4ToIPv6.ts b/library/helpers/mapIPv4ToIPv6.ts index 1b8e50319..fdaa69503 100644 --- a/library/helpers/mapIPv4ToIPv6.ts +++ b/library/helpers/mapIPv4ToIPv6.ts @@ -10,5 +10,6 @@ export default function mapIPv4ToIPv6(ip: string): string { const parts = ip.split("/"); const suffix = Number.parseInt(parts[1], 10); + // we add 96 to the suffix, since ::ffff: already is 96 bits, so the 32 remaining bits are decided by the IPv4 address return `::ffff:${parts[0]}/${suffix + 96}`; }