Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSRF Vulnerability Arising from Axios URL Parsing #6295

Open
Osb0rn3 opened this issue Mar 14, 2024 · 3 comments
Open

SSRF Vulnerability Arising from Axios URL Parsing #6295

Osb0rn3 opened this issue Mar 14, 2024 · 3 comments

Comments

@Osb0rn3
Copy link

Osb0rn3 commented Mar 14, 2024

Describe the bug

Hello everyone! I hope all is going smoothly for you. Despite numerous attempts to reach out regarding a security concern via this discussion thread, unfortunately, there has been no response. As a result, I've decided to create the issue and talk about it. There is a situation that axios URL parsing could let to SSRF vulnerability

To Reproduce

To replicate the vulnerability, simply execute the provided code snippet on your local machine and visit this link: http://localhost/?url=http:voorivex.team/../admin.

What's occurring here? When this code snippet is executed, the JavaScript native function URL parses the URL, returning voorivex.team as the hostname which is inside the whitelist. However, in Axios, this is interpreted as a path, causing Axios to utilize localhost as the host for paths. Consequently, this opens up the possibility to explore local endpoints.

Code snippet

const express = require('express');
const axios = require('axios');

const app = express();
const port = 80;

app.get('/', async (req, res) => {
    const whitelist = ['voorivex.team'];

    try {
        const { url } = req.query;
        const parsedUrl = new URL(url);

        if (whitelist.includes(parsedUrl.hostname)) {
            const response = await axios.get(url);

            res.json(response.data);
        } else {
            res.status(403).send('Forbidden: Hostname not in whitelist');
        }
    } catch (error) {
        if (error.request) {
            res.status(500).send('Internal Server Error');
        } else {
            res.status(400).send('Bad Request: Invalid URL');
        }
    }
});

app.get('/admin', async (req, res) => {
    const clientIP = req.ip;
    if (clientIP === '::1' || clientIP === '127.0.0.1' || clientIP === '::ffff:127.0.0.1') {
        res.send("Welcome to admin panel!")
    } else {
        res.status(403).send('Forbidden');
    }
})

app.listen(port, () => {
    console.log(`Server is running on http://localhost:${port}`);
});

Expected behavior

No response

Axios Version

1.6.7

Adapter Version

No response

Browser

No response

Browser Version

No response

Node.js Version

No response

OS

No response

Additional Library Versions

Express 4.18.3

Additional context/Screenshots

No response

@irsdl
Copy link

irsdl commented Mar 14, 2024

Here is the discussion in twitter/x: https://x.com/irsdl/status/1768057917826023568

It seems that all these are resolved as localhost:

  • http:google.com
  • http/:google.com
  • http\:google.com

@justindhillon
Copy link

You can fix this security vulnerability by modifying the code a bit:

const express = require('express');
const axios = require('axios');

const app = express();
const port = 80;

app.get('/', async (req, res) => {
    const whitelist = ['voorivex.team'];

    try {
        const { url } = req.query;
        const parsedUrl = new URL(url);

        if (whitelist.includes(parsedUrl.hostname)) {
            const response = await axios.get(parsedUrl.href);
            res.json(response.data);
        } else {
            res.status(403).send('Forbidden: Hostname not in whitelist');
        }
    } catch (error) {
        if (error.request) {
            res.status(500).send('Internal Server Error');
        } else {
            res.status(400).send('Bad Request: Invalid URL');
        }
    }
});

app.get('/admin', async (req, res) => {
    const clientIP = req.ip;
    if (clientIP === '::1' || clientIP === '127.0.0.1' || clientIP === '::ffff:127.0.0.1') {
        res.send("Welcome to admin panel!")
    } else {
        res.status(403).send('Forbidden');
    }
})

app.listen(port, () => {
    console.log(`Server is running on http://localhost:${port}`);
});

Changing axios.get(url) to axios.get(parsedUrl.href) fixes this issue. You can use this fix for now. I am working on an actual fix in axios right now. Thank you to @Osb0rn3 for bringing this up!

@justindhillon
Copy link

I have done some testing, and I have come to a conclusion. This problem is already fixed in the v0.x branch, and the fix is released in version 0.28. The fix is also in the v1.x branch (tested on ab3f0f9). And the fix will roll out on the next version of axios, most likely 1.6.9.

This issue should be marked as closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants