highCVE-2026-54729Jul 31, 2026

CVE-2026-54729: dssrf SSRF Bypass via DNS NXDOMAIN on 1.1.1.1

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A bug in the dssrf npm library lets an attacker reach internal services like localhost when the server uses the 1.1.1.1 DNS resolver, because the library skips a safety check whenever DNS returns no…

Packagedssrf
Ecosystemnpm
Affected<= 1.0.4
Fixed in1.0.5
CVE-2026-54729: dssrf SSRF Bypass via DNS NXDOMAIN on 1.1.1.1

The problem

dssrf's is_url_safe() calls dns.resolve4() to resolve hostnames before checking whether the resulting IP is private or loopback. When the configured resolver is 1.1.1.1 (Cloudflare), it returns NXDOMAIN for localhost because that name has no public record.

With no address returned and no dns.lookup fallback, is_url_safe() sees an empty result set and incorrectly returns true, treating the URL as safe. An attacker can then supply http://localhost/admin (or any loopback/internal hostname) and the application will make the outbound request.

Proof of concept

A working proof-of-concept for CVE-2026-54729 in dssrf, with the exact payload below.

javascript
import { is_url_safe } from 'dssrf';
import dns from 'dns';

// Simulate a server whose DNS resolver is 1.1.1.1
dns.setServers(['1.1.1.1']);

const TARGET = 'http://localhost/admin';
const result = await is_url_safe(TARGET);

// On dssrf <= 1.0.4 with 1.1.1.1 as resolver:
// result === true  ->  SSRF bypass succeeded
console.log(result ? 'BYPASSED: dssrf treated localhost as SAFE' : 'Blocked');

The root cause is a missing OS-resolver fallback. dns.resolve4('localhost') against 1.1.1.1 returns NXDOMAIN because that resolver does not answer for RFC-6761 special-use names. The library treated an empty address list as "no internal IP found" rather than as an error, so it returned safe.

The patch at commit 668c217 adds a dns.lookup call as a fallback whenever dns.resolve4 yields no addresses. dns.lookup always consults the OS resolver (which maps localhost to 127.0.0.1), so the loopback address is found, classified as private, and the URL is correctly rejected.

CWE-918.

The fix

Upgrade dssrf to version 1.0.5 or later. The fix is in commit 668c21792cd1252baf779a176aa652e2b4c0067d (PR #102). If you cannot upgrade immediately, force the Node.js process to use a system resolver (e.g., dns.setServers(['127.0.0.53'])) and add network-level egress controls so internal endpoints are not reachable from the application host regardless of library behavior.

Reporter not attributed.

References: [1][2][3][4]

Related research