VYPR
Medium severity6.5GHSA Advisory· Published May 12, 2026· Updated May 13, 2026

CVE-2026-42175

CVE-2026-42175

Description

requests-hardened is a library that overrides the default behaviors of the requests library, and adds new security features. Prior to , the SSRF protection in requests-hardened fails to block IP addresses within the RFC 6598 Shared Address Space (100.64.0.0/10). An attacker who can supply arbitrary URLs to requests-hardened could exploit this gap to access internal services hosted within 100.64.0.0/10. This is for example relevant in environments such as AWS EKS where 100.64.0.0/10 is commonly used as the default pod CIDR. The impact is environment-dependent, deployments that utilize the affected CIDR range for internal networking are exposed to SSRF bypass, while others may not be affected. This vulnerability is fixed in .

Affected products

1

Patches

2
a266b3958bb1

fix: test failures (#61)

1 file changed · +11 2
  • requests_hardened/ip_filter.py+11 2 modified
    @@ -11,12 +11,21 @@
     logger = logging.getLogger(__name__)
     
     # Additional CIDRs that should be blocked
    -EXTRA_BLOCKED_NET_RANGES: tuple[ipaddress.IPv4Network | ipaddress.IPv6Network, ...] = (
    +EXTRA_BLOCKED_NET_RANGES: tuple[
    +    Union[ipaddress.IPv4Network, ipaddress.IPv6Network], ...
    +] = (
         ipaddress.ip_network("192.88.99.0/24"),  # 6to4 relay anycast
         ipaddress.ip_network("100.64.0.0/10"),  # CG-NAT, can route to internal workloads
         ipaddress.ip_network("5f00::/16"),  # IPv6 Segment Routing
         ipaddress.ip_network("64:ff9b::/96"),  # used for IPv6 & IPv4 translation (NAT64)
         ipaddress.ip_network("2001:20::/28"),  # ORCHIDv2 (overlay identifiers)
    +
    +    # Fixes https://github.com/python/cpython/issues/113171 for outdated CPython
    +    # installations.
    +    ipaddress.ip_network("192.0.0.0/24"),
    +    ipaddress.ip_network("64:ff9b:1::/48"),
    +    ipaddress.ip_network("2002::/16"),
    +    ipaddress.ip_network("3fff::/20"),
     )
     
     
    @@ -25,7 +34,7 @@ class InvalidIPAddress(requests.RequestException):
     
     
     def _is_ip_in_extra_blocked_ranges(
    -    ip: ipaddress.IPv4Address | ipaddress.IPv6Address,
    +    ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address],
     ) -> bool:
         """Checks whether a given IP is part of the additional disallowed ranges."""
     
    
b7403f88d3b3

Merge commit from fork

2 files changed · +100 4
  • requests_hardened/ip_filter.py+25 3 modified
    @@ -10,11 +10,31 @@
     
     logger = logging.getLogger(__name__)
     
    +# Additional CIDRs that should be blocked
    +EXTRA_BLOCKED_NET_RANGES: tuple[ipaddress.IPv4Network | ipaddress.IPv6Network, ...] = (
    +    ipaddress.ip_network("192.88.99.0/24"),  # 6to4 relay anycast
    +    ipaddress.ip_network("100.64.0.0/10"),  # CG-NAT, can route to internal workloads
    +    ipaddress.ip_network("5f00::/16"),  # IPv6 Segment Routing
    +    ipaddress.ip_network("64:ff9b::/96"),  # used for IPv6 & IPv4 translation (NAT64)
    +    ipaddress.ip_network("2001:20::/28"),  # ORCHIDv2 (overlay identifiers)
    +)
    +
     
     class InvalidIPAddress(requests.RequestException):
         pass
     
     
    +def _is_ip_in_extra_blocked_ranges(
    +    ip: ipaddress.IPv4Address | ipaddress.IPv6Address,
    +) -> bool:
    +    """Checks whether a given IP is part of the additional disallowed ranges."""
    +
    +    for net in EXTRA_BLOCKED_NET_RANGES:
    +        if ip in net:
    +            return True
    +    return False
    +
    +
     def get_ip_address(
         hostname: str, port: int, allow_loopback: bool
     ) -> Tuple[Union[ipaddress.IPv4Address, ipaddress.IPv6Address], int]:
    @@ -40,12 +60,14 @@ def get_ip_address(
         # private ranges, such as ::ffff:0:0/96, ::/128.
         # Because of that, we need to check the IPv4 address instead of the IPv6 one.
         # https://github.com/python/cpython/commit/ed391090cc8332406e6225d40877db6ff44a7104
    -    if ip.version == 6 and (ipv4 := ip.ipv4_mapped) is not None:
    -        ip = ipv4
    +    if ip.version == 6:
    +        ip = cast(ipaddress.IPv6Address, ip)
    +        if (ipv4 := ip.ipv4_mapped) is not None:
    +            ip = ipv4
     
         if allow_loopback and ip.is_loopback:
             return ip, port
    -    elif ip.is_private:
    +    elif ip.is_private or ip.is_multicast or _is_ip_in_extra_blocked_ranges(ip):
             logger.warning(
                 "Forbidden IP address: %s for hostname %s",
                 ip,
    
  • tests/test_ssrf_filter.py+75 1 modified
    @@ -3,7 +3,7 @@
     import socket
     import sys
     from socket import AddressFamily, SocketKind
    -from typing import Tuple
    +from typing import Iterator, Tuple
     from unittest import mock
     
     import pytest
    @@ -23,6 +23,9 @@
     from .utils import mock_getaddrinfo
     
     
    +IPAddress = ipaddress.IPv4Address | ipaddress.IPv6Address
    +
    +
     @pytest.mark.parametrize(
         "ip_addr",
         [
    @@ -41,6 +44,13 @@
             "::ffff:192.168.2.1",
             "::ffff:192.0.2.0",  # broadcast address
             "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
    +        # RFC 6598 - Shared Address Space (https://github.com/python/cpython/issues/119812)
    +        "100.64.0.1",
    +        "::ffff:100.64.0.1",
    +        "::ffff:6440:1",  # Compressed 100.64.0.1
    +        "0000:0000:0000:0000:0000:ffff:6440:0001",  # Expanded 100.64.0.1
    +        "100.64.0.0",  # first address of 100.64.0.0/10
    +        "100.127.255.255",  # last address for 100.64.0.0/10
         ],
     )
     @pytest.mark.fake_resolver(enabled=False)
    @@ -55,6 +65,70 @@ def test_blocks_private_ranges(ip_addr: str):
                 SSRFFilter.send_request("GET", "https://test.local")
     
     
    +@pytest.mark.parametrize(
    +    "cidr",
    +    [
    +        "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.88.99.0/24",
    +        "192.168.0.0/16",
    +        "198.18.0.0/15",
    +        "198.51.100.0/24",
    +        "203.0.113.0/24",
    +        "224.0.0.0/4",
    +        "233.252.0.0/24",
    +        "240.0.0.0/4",
    +        "255.255.255.255/32",
    +        "::/128",
    +        "::1/128",
    +        "::ffff:0:0/96",
    +        "64:ff9b::/96",
    +        "64:ff9b:1::/48",
    +        "100::/64",
    +        "2001::/32",
    +        "2001:20::/28",
    +        "2001:db8::/32",
    +        "2002::/16",
    +        "3fff::/20",
    +        "5f00::/16",
    +        "fc00::/7",
    +        "fe80::/10",
    +        "ff00::/8",
    +    ],
    +)
    +@pytest.mark.fake_resolver(enabled=False)
    +def test_blocks_all_reserved_ip_address_ranges(cidr: str):
    +
    +    # Tests against 3 addresses, e.g., for 127.0.0.0/8:
    +    # - first_addr=127.0.0.0 (network address)
    +    # - host_address=127.0.0.1 (first address)
    +    # - last_addr=127.255.255.255 (broadcast)
    +    net = ipaddress.ip_network(cidr)
    +    first_addr, last_addr = net.network_address, net.broadcast_address
    +
    +    # list[IPv4Address|IPv6Address] is only for Python <= 3.12.
    +    # Should be removed once 3.12 is EOL (~Nov 2028)
    +    hosts: Iterator[IPAddress] | list[IPAddress] = net.hosts()
    +    host_address = hosts[0] if isinstance(hosts, list) else next(hosts)
    +
    +    for addr in [first_addr, host_address, last_addr]:
    +        assert isinstance(addr, (ipaddress.IPv4Address, ipaddress.IPv6Address))
    +
    +        with mock_getaddrinfo(str(addr)):
    +            # (!!) We expect no connections to be made inside this test.
    +            #      If 'pytest_socket.SocketBlockedError' exception is raised,
    +            #      then something is really wrong as it means the test most likely tried
    +            #      to connect a private IP address.
    +            with pytest.raises(InvalidIPAddress):
    +                SSRFFilter.send_request("GET", "https://test.local")
    +
    +
     @pytest.mark.parametrize(
         "resolve_to_ip_addr, expected_sock_ip_addr",
         [
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.