Listen to this Post
Intro
Every special-use classification method in the ip-address library (e.g., isLoopback(), isPrivate(), isLinkLocal()) is built on the `isInSubnet` function. This function contains a guard that returns `false` if the address’s own subnet mask is shorter than the reference range’s mask. The mask is taken directly from the CIDR suffix on the parsed input. By appending a suffix such as /0, an attacker can suppress all special-use classification. The library still returns the real internal target via `correctForm()` and address, making the address fully usable for connecting.
An application that relies on these checks for network trust-boundary decisions—for example, a filter intended to block Server-Side Request Forgery (SSRF)—may therefore treat an internal target as external and allow the request. SSRF is an attack where a user-supplied address coaxes the server into making a request to an internal destination the user could not otherwise reach, such as a loopback service or a cloud metadata endpoint.
The `isInSubnet` guard is correct for subnet containment (e.g., a `/0` network is not inside a /8), but it is wrong for classification, which asks about the address itself and must not depend on the caller’s prefix. `/0` is shorter than every reference prefix in the special-use tables (loopback /8, link-local /16, CGNAT /10, ULA /7, multicast /4), so for a classification call the bit comparison is never reached and the method returns false. The underlying bit comparison is correct, and `mask(n)` already returns the first `n` bits of the full parsed address independently of subnetMask—the defect is solely that the containment guard sits in the classification path. `/0` is the universal case, but any suffix shorter than the specific range being tested has the same effect: `10.0.0.5/7` defeats `isPrivate()` for 10.0.0.0/8. This also defeats the fix released in 10.2.1 for GHSA-22jq-vg5j-6vgg, as the normalization is reached through isInSubnet, so `::ffff:127.0.0.1/0` reverts to being reported as non-internal.
DailyCVE Form:
Platform: ip-address library
Version: 10.1.1 – 10.2.2
Vulnerability: Classification bypass via CIDR
Severity: Critical
date: 2026-08-03
Prediction: Patch available 2026-08-03
What Undercode Say:
Analytics show the vulnerability stems from isInSubnet‘s short-circuit logic. The classification API was introduced for `Address4` in 10.1.1 and extended to `Address6` in 10.2.0. Every classifier is affected on both `Address4` and Address6, with the sole exception being `Address6.isLinkLocal()` for native `fe80::/10` addresses. `getType()` returns `Global unicast` for all IPv6 cases. A CIDR suffix is not legal in a URL host, so exploitation requires an application that accepts a bare address string and passes it to the constructor before classifying. The following command demonstrates the issue:
npm i [email protected]
const { Address4, Address6 } = require('ip-address');
function isBlocked(host) {
try {
const a = new Address4(host);
return a.isPrivate() || a.isLoopback() || a.isLinkLocal() || a.isCGNAT()
|| a.isMulticast() || a.isUnspecified() || a.isBroadcast();
} catch {}
try {
const a = new Address6(host);
return a.isPrivate() || a.isLoopback() || a.isLinkLocal() || a.isULA()
|| a.isMulticast() || a.isUnspecified();
} catch {}
return false;
}
for (const h of ['127.0.0.1', '10.0.0.1', '::1',
'127.0.0.1/0', '10.0.0.5/7', '169.254.169.254/0',
'::1/0', '::ffff:127.0.0.1/0', '64:ff9b::7f00:1/0']) {
console.log(isBlocked(h) ? 'BLOCK ' : 'ALLOW ', h, '->', new (h.includes(':') ? Address6 : Address4)(h).correctForm());
}
Exploit:
An attacker supplies a special-use IP address with a CIDR suffix shorter than the reference prefix, e.g., 127.0.0.1/0, 10.0.0.5/7, 169.254.169.254/0, ::1/0, ::ffff:127.0.0.1/0, or 64:ff9b::7f00:1/0. The application passes this string to the `Address4` or `Address6` constructor. The classification methods (e.g., isLoopback(), isPrivate()) call isInSubnet. The guard in `isInSubnet` returns `false` because the address’s mask (e.g., /0) is shorter than the reference mask. The address is misclassified as non-internal and allowed. The `correctForm()` method still returns the real internal IP, so the connection reaches the internal target.
Protection:
Upgrade to the patched release. In the fix, classification no longer consults the address’s own prefix. A new `isHostInSubnet()` compares the address’s host bits against the reference range only. Every classifier (isLoopback, isPrivate, isLinkLocal, isCGNAT, isMulticast, isUnspecified, isBroadcast, isULA, isMapped4, isTeredo, is6to4, isDocumentation, getType, and the IPv4-mapped/NAT64 normalization behind embeddedIPv4) uses it. `isInSubnet` keeps its subnet-containment semantics unchanged. After upgrading, `new Address4(‘127.0.0.1/0’).isLoopback()` returns true. If you cannot upgrade immediately, strip the suffix before classifying by re-parsing addressMinusSuffix:
const parsed = new Address4(userInput); const host = new Address4(parsed.addressMinusSuffix); // classify this one
Impact:
Every special-use classification method is affected on both `Address4` and Address6. An application that relies on these checks for SSRF protection can be tricked into allowing requests to internal destinations. Attackers can reach loopback services, cloud metadata endpoints (e.g., IMDS), and other internal network resources. The vulnerability is rated as Critical. A robust SSRF guard must resolve the hostname, validate the resolved IP against the socket it connects to, and account for DNS rebinding and redirects. These checks should be treated as one layer, not the only one.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

