Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Network
DescriptionIncorrect authorization in Network
ComponentNetwork
Bug ClassLogic Error
Tracker517364411
Fix commit085f44318aea (chromium/src) +106/-28
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
for
net/base/port_util_unittest.cc
modified
TEST
net/base/port_util_unittest.cc
modified
for
net/socket/tcp_client_socket_unittest.cc
modified
TestSocketPerformanceWatcher
net/socket/tcp_client_socket_unittest.cc
modified

Files Changed

  • net/base/port_util.cc
  • net/base/port_util_unittest.cc
  • net/socket/tcp_client_socket_unittest.cc
From 085f44318aea6217b5130ad12e8332e1be10599e Mon Sep 17 00:00:00 2001
From: Adam Rice <ricea@chromium.org>
Date: Wed, 08 Jul 2026 08:38:42 -0700
Subject: [PATCH] Restrict abuse ports on 0.0.0.0 and :: as they are localhost aliases.

On POSIX-like OSs, 0.0.0.0 and :: can be used as aliases for localhost,
but the implementation of the kRestrictAbusePortsOnLocalhost feature
didn't check for that. This CL modifies IsPortAllowedForIpEndpoint to
also restrict ports on zero addresses.

It also updates tests in net/socket/udp_socket_unittest.cc,
net/socket/tcp_client_socket_unittest.cc, and net/base/port_util_unittest.cc
to verify that the ports are blocked for all of 127.0.0.0/8, ::1,
0.0.0.0, and ::.

Implemented by Gemini.

Bug: 517364411
Change-Id: I1064c920a7cb98c262c77ae9b03febafa5193829
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7967099
Reviewed-by: Yoichi Osato <yoichio@chromium.org>
Commit-Queue: Adam Rice <ricea@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1658792}
---

diff --git a/net/base/port_util.cc b/net/base/port_util.cc
index 49d0f744..41cbfcaa 100644
--- a/net/base/port_util.cc
+++ b/net/base/port_util.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "net/base/features.h"
+#include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/parse_number.h"
 #include "url/url_constants.h"
@@ -154,6 +155,17 @@
 
 constinit bool g_need_to_reset_restrict_localhost_ports = false;
 
+// Returns true if connecting to `address` may reach a service on the local
+// host. In addition to the loopback ranges this includes the unspecified
+// address, which many operating systems route to loopback, and IPv4-mapped
+// IPv6 forms of those addresses.
+bool MayConnectToLocalhost(const IPAddress& address) {
+  const IPAddress unmapped = address.IsIPv4MappedIPv6()
+                                 ? ConvertIPv4MappedIPv6ToIPv4(address)
+                                 : address;
+  return unmapped.IsLoopback() || unmapped.IsZero();
+}
+
 }  // namespace
 
 bool IsPortValid(int port) {
@@ -206,7 +218,10 @@
   }
 
   // This function currently restricts only on localhost.
-  if (!endpoint.address().IsLoopback()) {
+  // 0.0.0.0 and :: are treated as localhost aliases on some platforms.
+  // On other platforms, connections to those addresses fail, so applying
+  // the same restriction is harmless.
+  if (!MayConnectToLocalhost(endpoint.address())) {
     return true;
   }
 
diff --git a/net/base/port_util_unittest.cc b/net/base/port_util_unittest.cc
index 0523bd9..2a04b07 100644
--- a/net/base/port_util_unittest.cc
+++ b/net/base/port_util_unittest.cc
@@ -57,22 +57,40 @@
   ReloadLocalhostRestrictedPortsForTesting();
   IPAddress public_address(8, 8, 8, 8);
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 12345)));
+  EXPECT_TRUE(IsPortAllowedForIpEndpoint(
+      IPEndPoint(ConvertIPv4ToIPv4MappedIPv6(public_address), 12345)));
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv4Localhost(), 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv6Localhost(), 443)));
   histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 0);
+
+  const IPAddress localhost_addresses[] = {
+      IPAddress::IPv4Localhost(),
+      IPAddress(127, 0, 0, 2),
+      IPAddress::IPv4AllZeros(),
+      IPAddress::IPv6Localhost(),
+      IPAddress::IPv6AllZeros(),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
+  };
+
+  int expected_count = 0;
   for (int port : {12345, 23456, 34567}) {
-    EXPECT_FALSE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv4Localhost(), port)));
-    EXPECT_FALSE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv6Localhost(), port)));
+    for (const auto& address : localhost_addresses) {
+      EXPECT_FALSE(IsPortAllowedForIpEndpoint(IPEndPoint(address, port)));
+      expected_count++;
+    }
   }
-  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 6);
-  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 12345, 2);
-  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 23456, 2);
-  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 34567, 2);
+  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts",
+                                    expected_count);
+  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 12345,
+                                     std::size(localhost_addresses));
+  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 23456,
+                                     std::size(localhost_addresses));
+  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 34567,
+                                     std::size(localhost_addresses));
 }
 
 TEST(NetUtilTest, RestrictedAbusePortsLocalhostTestNoParamSet) {
@@ -82,17 +100,29 @@
   ReloadLocalhostRestrictedPortsForTesting();
   IPAddress public_address(8, 8, 8, 8);
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 12345)));
+  EXPECT_TRUE(IsPortAllowedForIpEndpoint(
+      IPEndPoint(ConvertIPv4ToIPv4MappedIPv6(public_address), 12345)));
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv4Localhost(), 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv6Localhost(), 443)));
   histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 0);
+
+  const IPAddress localhost_addresses[] = {
+      IPAddress::IPv4Localhost(),
+      IPAddress(127, 0, 0, 2),
+      IPAddress::IPv4AllZeros(),
+      IPAddress::IPv6Localhost(),
+      IPAddress::IPv6AllZeros(),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
+  };
+
   for (int port : {12345, 23456, 34567}) {
-    EXPECT_TRUE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv4Localhost(), port)));
-    EXPECT_TRUE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv6Localhost(), port)));
+    for (const auto& address : localhost_addresses) {
+      EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(address, port)));
+    }
   }
   histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 0);
 }
diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc
index 7ce8d38..493a666c 100644
--- a/net/socket/tcp_client_socket_unittest.cc
+++ b/net/socket/tcp_client_socket_unittest.cc
@@ -339,16 +339,33 @@
       {{"localhost_restrict_ports",
         base::NumberToString(server_address.port())}});
   ReloadLocalhostRestrictedPortsForTesting();
-  TCPClientSocket socket(AddressList(server_address), nullptr, nullptr, nullptr,
-                         NetLogSource(), handles::kInvalidNetworkHandle);
+  const IPAddress addresses_to_test[] = {
+      IPAddress::IPv4Localhost(),
+      IPAddress(127, 0, 0, 2),
+      IPAddress::IPv4AllZeros(),
+      IPAddress::IPv6Localhost(),
+      IPAddress::IPv6AllZeros(),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
+  };
 
-  TestCompletionCallback connect_callback;
-  int connect_result = socket.Connect(connect_callback.callback());
-  EXPECT_THAT(connect_callback.GetResult(connect_result),
-              IsError(ERR_UNSAFE_PORT));
-  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 1);
+  int expected_count = 0;
+  for (const auto& address : addresses_to_test) {
+    TCPClientSocket socket(
+        AddressList(IPEndPoint(address, server_address.port())), nullptr,
+        nullptr, nullptr, NetLogSource(), handles::kInvalidNetworkHandle);
+
+    TestCompletionCallback connect_callback;
+    int connect_result = socket.Connect(connect_callback.callback());
+    EXPECT_THAT(connect_callback.GetResult(connect_result),
+                IsError(ERR_UNSAFE_PORT));
+    expected_count++;
+  }
+
+  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts",
+                                    expected_count);
   histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts",
-                                     server_address.port(), 1);
+                                     server_address.port(), expected_count);
 }
 
 class TestSocketPerformanceWatcher : public SocketPerformanceWatcher {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/net/base/port_util_unittest.cc b/net/base/port_util_unittest.cc
index 0523bd9..2a04b07 100644
--- a/net/base/port_util_unittest.cc
+++ b/net/base/port_util_unittest.cc
@@ -57,22 +57,40 @@
   ReloadLocalhostRestrictedPortsForTesting();
   IPAddress public_address(8, 8, 8, 8);
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 12345)));
+  EXPECT_TRUE(IsPortAllowedForIpEndpoint(
+      IPEndPoint(ConvertIPv4ToIPv4MappedIPv6(public_address), 12345)));
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv4Localhost(), 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv6Localhost(), 443)));
   histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 0);
+
+  const IPAddress localhost_addresses[] = {
+      IPAddress::IPv4Localhost(),
+      IPAddress(127, 0, 0, 2),
+      IPAddress::IPv4AllZeros(),
+      IPAddress::IPv6Localhost(),
+      IPAddress::IPv6AllZeros(),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
+  };
+
+  int expected_count = 0;
   for (int port : {12345, 23456, 34567}) {
-    EXPECT_FALSE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv4Localhost(), port)));
-    EXPECT_FALSE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv6Localhost(), port)));
+    for (const auto& address : localhost_addresses) {
+      EXPECT_FALSE(IsPortAllowedForIpEndpoint(IPEndPoint(address, port)));
+      expected_count++;
+    }
   }
-  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 6);
-  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 12345, 2);
-  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 23456, 2);
-  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 34567, 2);
+  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts",
+                                    expected_count);
+  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 12345,
+                                     std::size(localhost_addresses));
+  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 23456,
+                                     std::size(localhost_addresses));
+  histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts", 34567,
+                                     std::size(localhost_addresses));
 }
 
 TEST(NetUtilTest, RestrictedAbusePortsLocalhostTestNoParamSet) {
@@ -82,17 +100,29 @@
   ReloadLocalhostRestrictedPortsForTesting();
   IPAddress public_address(8, 8, 8, 8);
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 12345)));
+  EXPECT_TRUE(IsPortAllowedForIpEndpoint(
+      IPEndPoint(ConvertIPv4ToIPv4MappedIPv6(public_address), 12345)));
   EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(public_address, 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv4Localhost(), 443)));
   EXPECT_TRUE(
       IsPortAllowedForIpEndpoint(IPEndPoint(IPAddress::IPv6Localhost(), 443)));
   histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 0);
+
+  const IPAddress localhost_addresses[] = {
+      IPAddress::IPv4Localhost(),
+      IPAddress(127, 0, 0, 2),
+      IPAddress::IPv4AllZeros(),
+      IPAddress::IPv6Localhost(),
+      IPAddress::IPv6AllZeros(),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
+  };
+
   for (int port : {12345, 23456, 34567}) {
-    EXPECT_TRUE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv4Localhost(), port)));
-    EXPECT_TRUE(IsPortAllowedForIpEndpoint(
-        IPEndPoint(IPAddress::IPv6Localhost(), port)));
+    for (const auto& address : localhost_addresses) {
+      EXPECT_TRUE(IsPortAllowedForIpEndpoint(IPEndPoint(address, port)));
+    }
   }
   histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 0);
 }
diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc
index 7ce8d38..493a666c 100644
--- a/net/socket/tcp_client_socket_unittest.cc
+++ b/net/socket/tcp_client_socket_unittest.cc
@@ -339,16 +339,33 @@
       {{"localhost_restrict_ports",
         base::NumberToString(server_address.port())}});
   ReloadLocalhostRestrictedPortsForTesting();
-  TCPClientSocket socket(AddressList(server_address), nullptr, nullptr, nullptr,
-                         NetLogSource(), handles::kInvalidNetworkHandle);
+  const IPAddress addresses_to_test[] = {
+      IPAddress::IPv4Localhost(),
+      IPAddress(127, 0, 0, 2),
+      IPAddress::IPv4AllZeros(),
+      IPAddress::IPv6Localhost(),
+      IPAddress::IPv6AllZeros(),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
+  };
 
-  TestCompletionCallback connect_callback;
-  int connect_result = socket.Connect(connect_callback.callback());
-  EXPECT_THAT(connect_callback.GetResult(connect_result),
-              IsError(ERR_UNSAFE_PORT));
-  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 1);
+  int expected_count = 0;
+  for (const auto& address : addresses_to_test) {
+    TCPClientSocket socket(
+        AddressList(IPEndPoint(address, server_address.port())), nullptr,
+        nullptr, nullptr, NetLogSource(), handles::kInvalidNetworkHandle);
+
+    TestCompletionCallback connect_callback;
+    int connect_result = socket.Connect(connect_callback.callback());
+    EXPECT_THAT(connect_callback.GetResult(connect_result),
+                IsError(ERR_UNSAFE_PORT));
+    expected_count++;
+  }
+
+  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts",
+                                    expected_count);
   histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts",
-                                     server_address.port(), 1);
+                                     server_address.port(), expected_count);
 }
 
 class TestSocketPerformanceWatcher : public SocketPerformanceWatcher {
diff --git a/net/socket/udp_socket_unittest.cc b/net/socket/udp_socket_unittest.cc
index b4d177c2..be44ded 100644
--- a/net/socket/udp_socket_unittest.cc
+++ b/net/socket/udp_socket_unittest.cc
@@ -400,14 +400,30 @@
       {{"localhost_restrict_ports",
         base::NumberToString(server_address.port())}});
   ReloadLocalhostRestrictedPortsForTesting();
-  // Setup the client.
-  auto client = std::make_unique<UDPClientSocket>(
-      DatagramSocket::DEFAULT_BIND, NetLog::Get(), NetLogSource(),
-      handles::kInvalidNetworkHandle);
-  EXPECT_THAT(client->Connect(server_address), IsError(ERR_UNSAFE_PORT));
-  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 1);
+  const IPAddress addresses_to_test[] = {
+      IPAddress::IPv4Localhost(),
+      IPAddress(127, 0, 0, 2),
+      IPAddress::IPv4AllZeros(),
+      IPAddress::IPv6Localhost(),
+      IPAddress::IPv6AllZeros(),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
+      ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
+  };
+
+  int expected_count = 0;
+  for (const auto& address : addresses_to_test) {
+    auto client = std::make_unique<UDPClientSocket>(
+        DatagramSocket::DEFAULT_BIND, NetLog::Get(), NetLogSource(),
+        handles::kInvalidNetworkHandle);
+    EXPECT_THAT(client->Connect(IPEndPoint(address, server_address.port())),
+                IsError(ERR_UNSAFE_PORT));
+    expected_count++;
+  }
+
+  histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts",
+                                    expected_count);
   histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts",
-                                     server_address.port(), 1);
+                                     server_address.port(), expected_count);
 }
 
 TEST_F(UDPSocketTest, ConnectUsingNetworkRestrictedPort) {
Loading diff…

Original Bug Report

reported by vm...@google.com

Bypass of localhost port restrictions via unspecified addresses (0.0.0.0 and ::)

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: The localhost port restriction mechanism designed to block connections to unsafe local services on POSIX platforms can be bypassed using unspecified IP addresses (0.0.0.0 and ::). Because Chromium’s port authorization checks rely on IPAddress::IsLoopback(), which does not recognize unspecified IP addresses, the blocklist is not applied. On POSIX operating systems, the kernel routes connections to unspecified addresses directly to the loopback interface, allowing access to restricted localhost ports.

Affected files:

  • net/base/port_util.cc
  • net/base/ip_address.cc

Estimated timestamp from git blame: 2025-05-09

Summary

The kRestrictAbusePortsOnLocalhost feature is a security mitigation designed to prevent network requests to restricted ports on localhost. However, this mitigation can potentially be bypassed on POSIX platforms (such as Linux, Android, and macOS) by using unspecified IP addresses like 0.0.0.0 or ::.

Root Cause Analysis

The bypass occurs in IsPortAllowedForIpEndpoint() (net/base/port_util.cc), which checks if the endpoint address is a loopback address:

bool IsPortAllowedForIpEndpoint(const IPEndPoint& endpoint) {
  if (!base::FeatureList::IsEnabled(features::kRestrictAbusePortsOnLocalhost)) {
    return true;
  }

  // This function currently restricts only on localhost.
  if (!endpoint.address().IsLoopback()) {
    return true;
  }
  ...

However, IPAddress::IsLoopback() (net/base/ip_address.cc) only matches IPv4 loopback (127.0.0.0/8) and IPv6 loopback (::1):

bool IPAddress::IsLoopback() const {
  // 127.0.0.1/8
  if (IsIPv4())
    return ip_address_[0] == 127;

  // ::1
  if (IsIPv6()) {
    for (size_t i = 0; i + 1 < ip_address_.size(); ++i) {
      if (ip_address_[i] != 0)
        return false;
    }
    return ip_address_.back() == 1;
  }

  return false;
}

Because of this, 0.0.0.0 and :: are not recognized as loopback. On POSIX systems (Linux, Android, macOS), calling connect(2) or sendto(2) on a socket with 0.0.0.0 or :: as the destination address is rewritten by the kernel route lookup to loopback. Consequently, packets are delivered to services bound to loopback, but Chromium’s port restriction mechanism treats the endpoint as a non-loopback address and does not apply the port restrictions.

Potential Trigger Path

Note: These are potential steps to trigger the issue, as we have not verified them dynamically using functional exploit code.

  1. The RestrictAbusePortsOnLocalhost feature is active (e.g., on Android, restricting a port like 30987).
  2. A page served from a loopback origin (e.g., 127.0.0.1 or WebView content classified as kLoopback) makes a request to http://0.0.0.0:30987/ or targets 0.0.0.0:30987 via WebRTC (P2PSocketUdp::DoSend).
  3. Local Network Access permits the request because 0.0.0.0 is mapped to IPAddressSpace::kLoopback.
  4. TCPClientSocket::DoConnect or P2PSocketUdp::DoSend calls IsPortAllowedForIpEndpoint() on {0.0.0.0, 30987}.
  5. Since 0.0.0.0 is not recognized as a loopback IP by IsLoopback(), the function returns true, allowing the connection.
  6. The host kernel routes the connection to 127.0.0.1:30987, successfully reaching the restricted local service.

Suggested Fix

Ensure that IsPortAllowedForIpEndpoint() also treats unspecified/zero IP addresses (0.0.0.0 and ::) as restricted loopback equivalents. This can be done by checking endpoint.address().IsZero() in IsPortAllowedForIpEndpoint:

bool IsPortAllowedForIpEndpoint(const IPEndPoint& endpoint) {
  if (!base::FeatureList::IsEnabled(features::kRestrictAbusePortsOnLocalhost)) {
    return true;
  }

  // This function currently restricts only on localhost.
  if (!endpoint.address().IsLoopback() && !endpoint.address().IsZero()) {
    return true;
  }

Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker
Links in the report