CVE-2026-79199
Overview
Files Changed
net/base/ip_address.ccnet/base/ip_address_unittest.cc
Patch
From d81fbc06ef3c378671f1dd6d7668583456ae294c Mon Sep 17 00:00:00 2001
From: Kenichi Ishibashi <bashi@chromium.org>
Date: Mon, 29 Jun 2026 21:33:17 -0700
Subject: [PATCH] net: recognize IPv4-mapped IPv6 multicast in IsMulticast()
IPAddress::IsMulticast() classified ::ffff:224.0.0.0/4 as non-multicast
because for a 16-byte address it only inspects ip_address_[0], which is
0x00 for IPv4-mapped IPv6 addresses.
Add an IsIPv4MappedIPv6() branch that checks the embedded IPv4 leading
byte at offset 12, mirroring the existing handling in
IPAddress::IsLinkLocal(), and extend IPAddressTest.IsMulticast with
mapped-form boundary cases.
Bug: 513719741
Change-Id: I7a7e429a52d1d2aaf06e115f1cea578176f01037
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8018062
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1654567}
---
diff --git a/net/base/ip_address.cc b/net/base/ip_address.cc
index 1c6cb9e..8406d19f 100644
--- a/net/base/ip_address.cc
+++ b/net/base/ip_address.cc
@@ -231,11 +231,19 @@
}
bool IPAddress::IsMulticast() const {
+ // 224.0.0.0/4
if (IsIPv4()) {
uint8_t first_byte = ip_address_[0];
return first_byte >= 224 && first_byte <= 239;
}
+ // [::ffff:224.0.0.0]/100
+ if (IsIPv4MappedIPv6()) {
+ uint8_t first_byte = ip_address_[12];
+ return first_byte >= 224 && first_byte <= 239;
+ }
+
+ // [ff00::]/8
if (IsIPv6()) {
return ip_address_[0] == 0xff;
}
diff --git a/net/base/ip_address_unittest.cc b/net/base/ip_address_unittest.cc
index c008dcd..76685fc 100644
--- a/net/base/ip_address_unittest.cc
+++ b/net/base/ip_address_unittest.cc
@@ -348,6 +348,25 @@
ASSERT_TRUE(ipv6_non_multicast.AssignFromIPLiteral("fe80::1"));
EXPECT_FALSE(ipv6_non_multicast.IsMulticast());
+ IPAddress ipv4_mapped_multicast;
+ ASSERT_TRUE(ipv4_mapped_multicast.AssignFromIPLiteral("::ffff:224.0.0.1"));
+ EXPECT_TRUE(ipv4_mapped_multicast.IsMulticast());
+
+ IPAddress ipv4_mapped_last_multicast;
+ ASSERT_TRUE(
+ ipv4_mapped_last_multicast.AssignFromIPLiteral("::ffff:239.255.255.255"));
+ EXPECT_TRUE(ipv4_mapped_last_multicast.IsMulticast());
+
+ IPAddress ipv4_mapped_non_multicast;
+ ASSERT_TRUE(
+ ipv4_mapped_non_multicast.AssignFromIPLiteral("::ffff:223.255.255.255"));
+ EXPECT_FALSE(ipv4_mapped_non_multicast.IsMulticast());
+
+ IPAddress ipv4_mapped_after_multicast;
+ ASSERT_TRUE(
+ ipv4_mapped_after_multicast.AssignFromIPLiteral("::ffff:240.0.0.0"));
+ EXPECT_FALSE(ipv4_mapped_after_multicast.IsMulticast());
+
IPAddress invalid;
EXPECT_FALSE(invalid.IsMulticast());
}
Regression Test / PoC
diff --git a/net/base/ip_address_unittest.cc b/net/base/ip_address_unittest.cc
index c008dcd..76685fc 100644
--- a/net/base/ip_address_unittest.cc
+++ b/net/base/ip_address_unittest.cc
@@ -348,6 +348,25 @@
ASSERT_TRUE(ipv6_non_multicast.AssignFromIPLiteral("fe80::1"));
EXPECT_FALSE(ipv6_non_multicast.IsMulticast());
+ IPAddress ipv4_mapped_multicast;
+ ASSERT_TRUE(ipv4_mapped_multicast.AssignFromIPLiteral("::ffff:224.0.0.1"));
+ EXPECT_TRUE(ipv4_mapped_multicast.IsMulticast());
+
+ IPAddress ipv4_mapped_last_multicast;
+ ASSERT_TRUE(
+ ipv4_mapped_last_multicast.AssignFromIPLiteral("::ffff:239.255.255.255"));
+ EXPECT_TRUE(ipv4_mapped_last_multicast.IsMulticast());
+
+ IPAddress ipv4_mapped_non_multicast;
+ ASSERT_TRUE(
+ ipv4_mapped_non_multicast.AssignFromIPLiteral("::ffff:223.255.255.255"));
+ EXPECT_FALSE(ipv4_mapped_non_multicast.IsMulticast());
+
+ IPAddress ipv4_mapped_after_multicast;
+ ASSERT_TRUE(
+ ipv4_mapped_after_multicast.AssignFromIPLiteral("::ffff:240.0.0.0"));
+ EXPECT_FALSE(ipv4_mapped_after_multicast.IsMulticast());
+
IPAddress invalid;
EXPECT_FALSE(invalid.IsMulticast());
}
Original Bug Report
Bypass of Direct Sockets Local Network permissions via IPv4-mapped IPv6 literals
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: Chromium’s net::IPAddress::IsMulticast() incorrectly returns false for IPv4-mapped IPv6 multicast addresses, such as ::ffff:239.255.255.250. In the Direct Sockets API, this flaw allows these addresses to bypass the LOCAL_NETWORK permission prompt and the direct-sockets-private Permissions Policy. A potential attacker could exploit this to silently discover and interact with devices on the user’s local network via multicast protocols.
Affected files:
net/base/ip_address.cccontent/browser/direct_sockets/direct_sockets_service_impl.cc
Estimated timestamp from git blame: 2025-09-03
Potential Vulnerability: Direct Sockets Permission Bypass
A potential vulnerability has been identified in how Chromium classifies IPv4-mapped IPv6 multicast addresses, leading to a security check bypass in the Direct Sockets API.
Root Cause
The primary issue resides in net::IPAddress::IsMulticast() within net/base/ip_address.cc. The current implementation for IPv6 addresses only checks if the first byte is 0xff. However, for IPv4-mapped IPv6 addresses (which follow the format ::ffff:a.b.c.d), the first byte is 0x00 regardless of whether the mapped IPv4 address is a multicast address.
// net/base/ip_address.cc
bool IPAddress::IsMulticast() const {
if (IsIPv4()) {
uint8_t first_byte = ip_address_[0];
return first_byte >= 224 && first_byte <= 239;
}
if (IsIPv6()) {
return ip_address_[0] == 0xff; // Returns false for IPv4-mapped multicast
}
return false;
}
This classification is critical for the Direct Sockets API in content/browser/direct_sockets/direct_sockets_service_impl.cc. The function RequiresPrivateNetworkAccess relies on IsMulticast() to identify endpoints that require high-privilege permissions:
// content/browser/direct_sockets/direct_sockets_service_impl.cc
bool RequiresPrivateNetworkAccess(const net::AddressList& addresses) {
return std::ranges::any_of(
addresses.endpoints(), [](const net::IPEndPoint& ip_endpoint) {
return network::IPAddressToIPAddressSpace(ip_endpoint.address()) ==
network::mojom::IPAddressSpace::kLocal ||
ip_endpoint.address().IsMulticast();
});
}
Because IsMulticast() returns false for mapped addresses, and network::IPAddressToIPAddressSpace() treats these multicast ranges as kPublic (since they are not in the standard private unicast ranges), RequiresPrivateNetworkAccess returns false. This causes the Browser process to skip the LOCAL_NETWORK permission prompt and the direct-sockets-private Permissions Policy check.
Potential Trigger Path
- A potential attacker creates an Isolated Web App (IWA) with the
direct-socketspermission enabled. - The IWA executes JavaScript to open a UDP socket to an IPv4-mapped IPv6 multicast address literal:
new UDPSocket({ remoteAddress: '::ffff:239.255.255.250', remotePort: 1900 }). - The Browser process resolves the literal and calls
GetRequiredPermissions, which returns an empty list due to theIsMulticast()flaw. - The Browser skips user permission prompts and policy enforcement, granting the socket access.
- On dual-stack systems (Linux, macOS, Windows), the underlying OS networking stack will correctly emit IPv4 multicast packets for these mapped addresses.
Impact
An attacker could silently perform local network reconnaissance (e.g., via SSDP or mDNS) and interact with local network devices without the user’s knowledge or consent, bypassing the intended security boundaries of the Direct Sockets API.
Suggested Fix
Update net::IPAddress::IsMulticast() in net/base/ip_address.cc to explicitly handle IPv4-mapped IPv6 addresses by checking the mapped IPv4 portion:
bool IPAddress::IsMulticast() const {
if (IsIPv4()) {
uint8_t first_byte = ip_address_[0];
return first_byte >= 224 && first_byte <= 239;
}
if (IsIPv4MappedIPv6()) {
return ConvertIPv4MappedIPv6ToIPv4(*this).IsMulticast();
}
if (IsIPv6()) {
return ip_address_[0] == 0xff;
}
return false;
}
Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a
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.