Never Winter Bank


You have been hired to run a pentest on this bank's old out of date system. Can you find the vulnerability in the code and drain this user's account?


This challenge requires you to identify a vulnerability in the web application that allows you to drain another user’s account, implying some sort of logic error is involved.

To start, let’s inspect the web server for any interesting files or behavior. Generally, we recommend trying to access /robots.txt to see if there are any files that the web developer do not want search engines to index which is usually a good sign of something interesting to probe. In this case, the /robots.txt file does show interesting results:

Disallow: *
/robots.txt content

So let’s try to access that hidden file and you’ll find that there is an interesting code snippets here that helps us understand what the logic error might be.

// TODO auditor says something is wrong with this code....
if (parseInt(amount) < account.amount) {
  if ((account.amount - parseInt(amount)) < account.minimum) {
    return res.status(400).send('Error: Account is not allowed to have a balance lower than 10');
  var transferAmount = parseInt(amount, 10);
  account.amount -= transferAmount;

/dev/rel.js content

The thing that stands out the most is the use of parseInt, specifically that its usage is inconsistent. The first two times that parseInt is used, it is called only with one argument, but the third time that parseInt is used, it is called with two arguments. Referring to the parseInt() documentation, we can see that there are two different function prototypes: parseInt(string) and parseInt(string, radix). The latter function prototype dictates a specific radix be used when parsing and in the above code snippet, we see that it uses 10 as the radix, meaning that the parsing function will treat the string parameter as a base 10 integer. However, the former usage does not specify any base system to use so the input actually becomes ambiguous. In older versions of JavaScript runtimes, there is a vulnerability that exists such that you can actually pass in confusing strings into parseInt.

Let’s visualize this here:

console.log(parseInt('01000')); // 1. returns 512

console.log(parseInt('01000', 10)); // 2. return 1000

The above code snippet shows that if we do not include a specific radix, the parseInt function will actually treat strings that start with 0 as an octal (base 8) string, meaning that it’ll parse the string "01000" as octal rather than as decimal (base 10).

Octal 01000 == Decimal 512
Octal 01000 == Decimal 512

This leads to different data values for the same input string depending on if you specify a radix base or not. This is precisely the vulnerability that we will take advantage of.

In the original /dev/rel.js code snippet, we can see that the conditionals (if statements) that check if you have enough balance is using parseInt without a radix and only does the transfer itself actually use parseInt with a specific base 10 radix. This means that if we can send in a string that is parsed initially as an octal number, we can successfully pass the check and transfer more money than we actually have in the account. For example, if we use "01000" as the string, that means the code will only check if we have $512 to cover the transaction, but it’ll actually transfer $1000 in the end.


What is the path of the leaked file?

Look in the /robots.txt endpoint for the leaked file path

What is the flag?

Drain the account and a flag will appear on your screen!

©️ 2024 Cyber Skyline. All Rights Reserved. Unauthorized reproduction or distribution of this copyrighted work is illegal.