~ 3 min read

Don't Be Fooled by Multicast, SSRF Bypass in private-ip

share on
A deep dive into an SSRF bypass vulnerability in the popular npm package `private-ip`. Learn how a blind spot in its private IP validation logic can expose your application to potential SSRF attacks.

Greetings, developers and fellow appsec practitioners! Another SSRF (Server-Side Request Forgery) vulnerability report for us to learn from. In this one, an SSRF bypass vulnerability I discovered in a popular npm package called private-ip. This package helps developers determine if an IP address falls within a private range. However, a clever attacker can exploit a blind spot in its logic to bypass these safeguards. Let’s dissect the issue and explore its practical implications.

Understanding private-ip: Your Ally in SSRF Prevention (Almost)

SSRF vulnerabilities arise when an application makes unintended requests to external servers based on user-controlled input. Attackers can leverage this to trick the application into fetching internal resources, performing unauthorized actions, or even launching denial-of-service attacks.

The private-ip package aims to mitigate this risk by identifying private IP addresses. Developers can integrate it into their applications to validate user-provided URLs or IP addresses. If the input resolves to a private IP, it’s likely an internal resource, and the application can handle it accordingly.

However, a critical vulnerability renders this validation incomplete.

đź‘‹ Just a quick break

I'm Liran Tal and I'm the author of the newest series of expert Node.js Secure Coding books. Check it out and level up your JavaScript

Node.js Secure Coding: Defending Against Command Injection Vulnerabilities
Node.js Secure Coding: Prevention and Exploitation of Path Traversal Vulnerabilities

The Vulnerability: A Gap in the Private IP Net

The crux of the issue lies in how private-ip defines private IP ranges. The package relies on a hardcoded list of IP ranges considered “private.” Unfortunately, this list omits an important category: multicast IP addresses.

Here’s the relevant code snippet from private-ip (for the technically curious):

const PRIVATE_IP_RANGES = [
// ... List of private IP ranges
'224.0.0.0/4' // This range (multicast) is missing!
];

Multicast addresses (ranging from 224.0.0.0 to 239.255.255.255) are a special class of IP addresses used for one-to-many communication. While not technically “private” in the traditional sense, an attacker could potentially craft a request leveraging a multicast address to exploit an SSRF vulnerability in the application.

Consider the following real-world example - imagine an application utilizes private-ip to validate incoming requests. An attacker could send a request with a hostname resolving to a multicast IP address. Since private-ip wouldn’t flag this as suspicious, the application might unknowingly process the request, potentially leading to unauthorized access or internal resource leakage.

A Reference Case: CVE-2020-28360

This vulnerability in the ip npm package (a similar package for IP address utilities) serves as a cautionary tale. An attacker could exploit an insufficient regular expression to bypass its private IP validation and potentially launch SSRF attacks. This incident highlights the importance of thorough validation logic when dealing with IP addresses.

SSRF Practical Example and Proof of Concept (PoC)

Let’s solidify our understanding with some code examples:

// Using private-ip
const isPrivateIP = require('private-ip');
console.log(isPrivateIP('127.0.0.1')); // True (correctly identified)
console.log(isPrivateIP('10.0.0.1')); // True (correctly identified)
console.log(isPrivateIP('239.255.255.250')); // **False Negative!** (incorrectly identified)

As you can see, private-ip incorrectly classifies the multicast address 239.255.255.250 as a non-private IP. This oversight could lead to potential SSRF vulnerabilities in applications relying on this package for input validation.

For educational purposes only, here’s a PoC demonstrating the bypass:

  1. Install the private-ip package:
Terminal window
npm install private-ip
  1. Define an app.js file with the programmatic API of private-ip as follows:
import is_ip_private from 'private-ip'
import ipaddr from 'ipaddr.js'
// -- private-ip
console.log(is_ip_private('127.0.0.1'))
console.log(is_ip_private('10.0.0.0'))
console.log(is_ip_private('169.254.0.1'))
console.log(is_ip_private('239.255.255.250'))
// -- ipaddr.js
console.log(ipaddr.parse('127.0.0.1').range())
console.log(ipaddr.parse('10.0.0.0').range())
console.log(ipaddr.parse('169.254.0.1').range())
console.log(ipaddr.parse('239.255.255.250').range())
  1. Run the app.js file:
Terminal window
node app.js
  1. Observe that the private-ip package does not detect the multicast IP address 239.255.255.250 as a private IP address and returns true for all 4 checks.

As a case of comparison, the ipaddr.js package correctly identifies the multicast IP address as a non-private IP address.

import ipaddr from 'ipaddr.js'
// -- ipaddr.js
console.log(ipaddr.parse('127.0.0.1').range())
console.log(ipaddr.parse('10.0.0.0').range())
console.log(ipaddr.parse('169.254.0.1').range())
console.log(ipaddr.parse('239.255.255.250').range())

It would result in the following stdout:

Terminal window
loopback
private
linkLocal
multicast

Node.js Security Newsletter

Subscribe to get everything in and around the Node.js security ecosystem, direct to your inbox.

    JavaScript & web security insights, latest security vulnerabilities, hands-on secure code insights, npm ecosystem incidents, Node.js runtime feature updates, Bun and Deno runtime updates, secure coding best practices, malware, malicious packages, and more.