


























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.
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.
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.
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.
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:
private-ip package: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())
app.js file: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:
loopback
private
linkLocal
multicast
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。