CSGOJackpot is a gambling website that allows players to bet & win Counter Strike skins or textures. Why is this a big deal? the average pot per hour is around $20,000 dollars! So, being able to swipe this money would be a big deal.
The game works as follows:
- Start a new round with an empty pot.
- A player puts in their skins (up to 10 at a time). These skins are calculated for a cent value and this is the value that the user put into the pot.
- If less than 50 skins, go back to step 2.
- The site generates a random number that determines who the winner is.
- The player with the winning ticket wins the whole pot.
From reading the HTML on the website, the hacker noticed that the backend was likely a nodejs server. Additionally, because the 16 digits of the winner percentage were the same as the nodejs default output & published at the end of the round, it was assumed that this was just using Math.random()
.
Math.random
is being used? So, what is the big deal? This function uses Multiply-with-Carry for the pseudo randomness. Can this be predicted?
Apparently, all you need for breaking the randomness in JavaScript is TWO consecutive outputs!? What, that is unbelievable to me. I then realized that the attacker just BRUTE FORCED the 32-bit space (which only took about 30 seconds) in order to figure out WHAT the next number would be. Not an elegant solution but it does work well!
For more work into breaking JavaScript random, try
this and
this. .
However, this attack was not perfect... the attacker simply could not predict the next value. So, he assumed some OTHER calls to random must be made (which was the case). However, the pattern was inconsistent for how many calls to random had to be made. So, a deadend :(
But, from the ashes of this, a new attack arrives! The site claims to be provably fair by doing the following calculation: md5(blinding + winning percentage)
BEFOREhand & shows this to the user; this is called the commit. So, what's the issue?
The blinding is just two calls to Math.random
(converted to hex) and the winning percentage is the value guessed by the user. By using the previous bug (output from random for the winning percentage) in tandem with the commit being shown (THREE RANDOM numbers in a row!) it is possible to KNOW where the randomness currently lies.
The hacker (wisely) did not attempt to exploit this to win money. Instead, they had a Twitch stream where they showed him guessing the percentages LIVE. The bug was also recently fixed.
What is the moral of the story? Use cryptographically secure random number generators! Additionally, be careful with WHICH numbers you show to a user. Showing too many may break the ecosystem.