Skip to content

Instantly share code, notes, and snippets.

@whokilleddb
Created December 8, 2022 16:20
Show Gist options
  • Select an option

  • Save whokilleddb/1db34257a59dc44764de06b27a36df02 to your computer and use it in GitHub Desktop.

Select an option

Save whokilleddb/1db34257a59dc44764de06b27a36df02 to your computer and use it in GitHub Desktop.
Intigriti Spot The Bug Solution 08/12

Intigriti Spot The Bug Solution 08/12

The given code snippet is vulnerable to DNS rebinding attack where an attacker can switch the IP addresses associated with domain names to pass certain security checks!

To understand this challenge, let's look at the source code:

First things first, there are two files:

  • app.js: Our friendly vulnerable application
  • local.js: A simple server which gives us our flag!

Looking at the code of local.js, we get:

const app = require('express')();
app.get('/flag', (req, res) => {
    res.send('flag{s4mpl3_fl4g}');
});
app.listen(3000, '127.0.0.1');

It's just a simple express application running on port 3000 which returns our flag when made a request to http://127.0.0.1:3000/flag

Next is the actual vulnerable code of app.js:

const app = require('express')();
const a = require('axios'); const d = require('dns');

function antiSSRF(req, res, next) {
    const url = new URL(req.query.file);
    d.lookup(url.hostname, (err, addr) => {
        if (addr === '123.61.186.155') next();
        else res.send('Not allowed!');
    })
}
app.get('/copy_file', antiSSRF, (req, res) => {
    setTimeout(function() {
        a.get(req.query.file).then((response) => {
            res.send('Done: ' + response.data);
        })
    }, 3000);
})
app.listen(3000, '0.0.0.0');

This application essentially runs a server which checks if the incoming request is trying to fetch a file from the IP 123.61.186.155 and then returns it's contents. Simple, right? NO! There is one major flaw which can be abused to bypass this check.

Let's break the code into two parts:

Part 1 would be:

function antiSSRF(req, res, next) {
    const url = new URL(req.query.file);
    d.lookup(url.hostname, (err, addr) => {
        if (addr === '123.61.186.155') next();
        else res.send('Not allowed!');
    })
}

And Part 2 is:

app.get('/copy_file', antiSSRF, (req, res) => {
    setTimeout(function() {
        a.get(req.query.file).then((response) => {
            res.send('Done: ' + response.data);
        })
    }, 3000);
})

Whenever a request is made, Part 1 is called for validation and if the checks pass, Part 2 is called! In part 1, the program tries to validate the IP address associated with the domain and if it passes, it proceeds to request the given file. Now here is how we can exploit this:

  • Have a user controlled domain name and initially make it point to 123.61.186.155
  • Submit the request payload as: http://attacker-controlled-domain:3000/flag
  • Part-1 of the source code takes this domain and correctly resolves the IP to the allowed value
  • Keep an eye out on your DNS logs! As soon as this name resolution happens, know that the part-1 checks have passed
  • Immediately change the IP pointed by the domain to 127.0.0.1
  • Now, when the program makes a request during execution of Part-2, it again initiates a lookup
  • This time, the address gets resolved to 127.0.0.1
  • The program fetches the flag value and returns the value to the user!

That's it! We got the flag! This is how DNS rebinding attacks work.

PS: I hope the fact that both the servers of app.js and local.js running on port 3000 isn't going to be a problem xD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment