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 applicationlocal.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.jsandlocal.jsrunning on port 3000 isn't going to be a problem xD