GETS Request

Challenge Description


I did not solve the challenge in time. I found the solution on discord, later. This write-up helps you understand the detailed solution.


  • 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 like n[]. 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');
  // 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!');

  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

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}`)


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.


The request to get the primes count -

There are exactly 192 primes under 1000

Trying larger number (more than 8 digits) as input -

Requested n too large!

Send the same number as an array -

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 -

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.




  • 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.