HTBank
2 minutes to read
We are given this website:
This time we don’t have source code available, so we must poke around with the website.
Registering a new account
First of all, we can register a new account:
And then we can log in to access our dashboard:
Analyzing JavaScript files
We can withdraw money clicking the button:
But we can’t add money because the other button is not working.
Looking at the JavaScript files (/static/js/home.js
), only the withdraw action is implemented:
const withdraw = () => {
const formData = new FormData();
var account = $('#account_input').val();
var amount = $('#amount_input').val();
if (!$.trim(account).length > 0 || !$.trim(amount).length > 0) {
showMessage('All fields required!');
return;
}
formData.append('account', account);
formData.append('amount', amount);
fetch('/api/withdraw', {
method: 'POST',
body: formData
})
.then((res) => {
res.json()
.then((data) => {
if (data.message == 'OK') {
return window.location.reload();
}
return showMessage(data.message)
})
})
}
There is nothing more on the website, so we might guess we need to add money somehow, even if we have 0
coins.
HTTP Parameter Pollution
If we use Burp Suite to capture our requests, we can see this request to /api/withdraw
:
We can send this to Repeater and make the request:
Here, we must notice that the server has added cookies session
and connect.sid
, which are common in Express JS (Node.js). However, the server adds a header Server: Werkzeug/3.0.1 Python/3.11.6
, which is likely to be Flask.
This type of setup might be vulnerable to some attacks, because both server can have slight differences when parsing HTTP messages. One of this nuances is HTTP Parameter Pollution, which happens when the same parameter is repeated in the same request. Some web server will take the first ocurrence, some will take the last one, and some will consider all of them! For more information, you can read HTTP Parameter Pollution or watch HTTP Parameter Pollution Explained.
So, if we add another amount
parameter, the Flask server will only consider the first ocurrence, whereas Node.js will consider both of them:
As a result, the withdrawal is successful.
Flag
Now, if we go back to our dashboard, we will see the flag: