In recent years, the gaming industry has grown significantly, especially casino games and sports betting. Online casinos consolidate their position as one of the main sources of entertainment in many countries worldwide, which evidently involves a notable rise in their turnover. For instance, in Spain alone, the gaming industry generated revenue of around €4,567 million in the third quarter of 2019.
It is not surprising that both traditional and online casino games have become a target of players who seek to detect vulnerabilities, enabling them to overcome the house edge and ‘break the bank’.
Over the last few years, I have been carrying out extensive research into online casino games focusing specifically on roulette. As a result, I have composed a detailed guide, which will provide its readers with a thorough understanding of the subject. The content of the guide includes information on classification of online roulette, potential vulnerabilities and the ways to detect them. It is worth mentioning that screenshots of various online games have been created to illustrate some of the explanations and therefore, are not real. Neither the research nor this guideline are aimed at any particular online casino.
The information mentioned above may be of interest to penetration testers and those who build and maintain online roulette systems since a comprehensive analysis and timely detection of weaknesses can have a significant impact on the aspect of safety of gaming experiences. It is important to highlight that this guide describes vulnerabilities which have not been published before (refer to Live Automatic Roulette – What to look for?).
European Roulette is one the most popular casino games. A roulette wheel has 37 numbered pockets, which have alternate colours (black and red). One of the pockets, so-called ‘zero pocket’ is green in colour (American roulette has an additional pocket called ‘double-zero’).
Briefly, a bet is placed on a colour or on a number. The dealer spins the ball and if it lands on the selected number/colour then the winnings are paid.
There are various types of bets [1]. Some of them have better odds of winning but lower payouts and vice versa. The most common are the following:
Straight/Single number: it consists in betting on a single number
Colour (Red/Black)
In both cases, the expected value is negative. Specifically, 0.027€ would be lost in case of a 1€ bet.
This advantage which casinos have is known as “the house edge” and it can be calculated using the following simple formula:
Expected Value = 1/37 * (36-37) ≈ -0.027€ (1€ for every 37€)
It is worth mentioning that none of the bet systems that are widely discussed on the Internet such as Martingale [2] can guarantee winning at roulette as it is mathematically impossible.
There are typically three different kinds of roulette which might be found in an online casino. What is important is that potential vulnerabilities vary depending on the type. Despite the fact that they could be named differently, they are similar.
This is a traditional casino roulette, which is shown live and might be accessed on the Internet. It follows all the conventional procedures and includes some elements of user interface to make the table and the wheel visible to a player. The following image shows what this roulette looks like.
Figure 1 – On-live roulette
The virtual roulette is a piece of software that emulates a real roulette. Once a player places their bet, the software generates a random winning number which ranges between 0 and 36. To do this, a pseudorandom number generator (PRNG) is used [3]. A user is shown a video/animation of a spinning ball falling in the pocket whose number was generated.
Figure 2 – Virtual roulette
Live automatic roulette is similar to virtual roulette to some extent. The main difference is that the winning number is not generated by a PRNG but a different, more advanced software is used instead. Specifically, the behaviour of a real roulette is simulated including all the necessary physics of the process like if it were a video game. The initial acceleration of the ball, among others, is a variable to prevent the winner number from being predicted.
Figure 3 – Live auto roulette
Generally, online casino games are secure and are assessed frequently. However, it is worth double-checking periodically to make sure that no findings have been overlooked in previous assessments.
The following sections of this guide contain the common checks which should be conducted during any online casino assessment. These checks are mainly focused on the roulette game. However, some of them might be valid for slots, monopoly live and similar games.
Likewise in any other web application assessment, injection flaws should be checked. Finding an SQL-injection issue would be especially useful as it might allow access, on top of everything else, to the whole winning numbers history. This could then be analysed in order to find potential vulnerabilities such as weak PRNG function.
It is often possible to see requests such as the one below which return a specific quantity of winning numbers determined by a parameter (in this case ‘limit’).
GET /winningNumbers.php?limit=500
It should be checked if it is possible to obtain any quantity of winning numbers. If so, the application might be vulnerable to denial of services attacks and it could allow for obtaining a sufficient quantity of winning numbers to analyse.
Although unlikely, it might be possible to tamper with the sum of money that a player wins (or loses) if this data is sent by a browser (or by casino’s software). Note that this information might be sent through websockets.
Typically, games are not hosted by the same server as the casino. A casino sends a session token, on top of the other information related to the user, to the server which hosts the games. This token might be predictable (e.g. numbers in sequence) and, as a result of this, an attacker would be able to hijack other user game sessions. This could allow an attacker to make other users lose money or to close their sessions.
TOCTOU is a software bug that occurs when an application checks the state of a resource before using it, but the resource’s state changes between the check and the use in a way that invalidates or changes the results of the check.
When a player requests to place a bet the server checks if their account have enough balance. Otherwise, the request is rejected. The server-side code should look like the following code:
Instruction 1: if (accountBalance >= betAmount) { Instruction 2: accountBalance = accountBalance – betAmount; Instruction 3: } else{ reject();
By sending simultaneous bet requests, it might be possible to place a bet which exceeds the total amount of money in the player’s wallet. This will occur if the instruction 1 is executed twice before instruction 2 is executed as this way both ‘if checks’ hold true and their statement blocks execute. The following diagram shows an example of a player who managed to bet 200€ having only 100€ in their balance account:
Thread 1 | Thread 2 | ||
Instruction | Variables | Instruction | Variables |
if (accountBalance >= betAmount) { | accountBalance = 100 betAmount = 100 | ||
if (accountBalance >= betAmount) { | accountBalance = 100 betAmount = 100 | ||
accountBalance = accountBalance – betAmount; | accountBalance = 0 betAmount = 100 | ||
accountBalance = accountBalance – betAmount; | accountBalance = -100 betAmount = 100 |
Every casino sets betting limits which depend on the kind of bet (they may be inside or outside). It is worth checking if it is possible to tamper with the size of the bet so that it exceeds the limits. It is especially interesting to exceed the maximum, as this is one of the security measures that protects the roulette from players using the Martingale method.
Normally, only multiples of 0.10 cents can be bet. This depends on the lowest chip value. If 0.16 cents are bet, the application might round the bet up to 0.20 cents. As a result, the higher bet is placed but only 0.16 cents might be deducted from the player’s balance. This would give an extra 0.10 cents to the player.
Roulette bets can be placed before the dealer closes the betting session. This usually happens a few seconds after the ball is spun by the dealer. The roulette board is then deactivated as the following figures show so that nobody could neither place any additional bet nor change them.
Figure 4 – Betting session is open (“Place your bets”)
Figure 5 – Betting session is due to be closed (“Bets closing”)
Figure 6 – Betting session is closed (“Bets closed”)
The HTTP request for placing bets should be identified and launched once the bets are closed and before the ball lands on one of the pockets. If the server did not reject the request then a skilled player could predict where the ball is going to land (and bet on the winning number) just a second before it happens.
A biased wheel is a wheel where one or more numbers are more likely to win. It is the result of a manufacturing defect. Nowadays, it is unlikely to find a biased wheel as the procedures of their quality control are perfectly designed to identify and eliminate any defect. However, some of them may yet be biased due to normal wear and tear or the table is not being levelled.
First, a significant quantity of winning numbers should be collected to make sure that our data is reliable. 10.000 numbers is a sufficient quantity but ideally, 30.000 (or more) should be tracked. Online casinos provide the history of their roulettes’ winning numbers which facilitates accumulation of the necessary quantity of numbers.
Then, a frequency table should be compiled like the following:
Number | Frequency |
12 | 3.02 |
36 | 2.93 |
21 | 2.92 |
13 | 2.9 |
… | … |
24 | 2.46 |
11 | 2.23 |
Every number should appear with 2.7% frequency. So, if this number is higher than 2.7%, the wheel might be biased. However, the variance and the number of sampling units should be taken into account. For example, a frequency of 3.02% would be perfectly normal for a sample of 10.000 numbers and this would not necessarily mean that the roulette is biased.
The following table can be used to know if a roulette is biased depending on the sample size and the highest and lowest frequencies.
Sample size | Frequency |
5000 | > 3.82% |
5000 | < 1.68% |
10000 | > 3.52% |
10000 | < 1.97% |
30000 | > 3.1% |
30000 | < 2.22% |
Following the previous example, a frequency of 3.02% would not necessarily mean that the roulette is biased, as it should be higher than 3.52% for a sample of 10,000 numbers. A frequency 1.92% is not significant either as it should be lower than 1.97%.
Several studies demonstrated that it was possible to exploit the deterministic nature of the game of roulette for profit [4]. Therefore, it is recommended that betting should not be allowed after the dealer has launched the ball (if this was allowed).
In order to analyse a PRNG it is necessary to generate a considerable quantity of numbers. The simplest and free way to do that is to use the ‘play money mode’ that online casinos usually offer.
The next step would consist of identifying the request(s) involved in the betting function. This request should be launched many times in an automated way, the responses should be collected and the winning numbers extracted.
An example of the request to make a bet of 1€ and the response from the server are given below:
GET https://exampleOnlineCasino.com/GGPHTTPHandler/?JSON={"JSONClientGameRequest":{"GGPPlayerSessionID":"XXXX","Message":{"RouletteAddBetRequest":{"Code":"CS_ROULETTE_ADD_BET","Version":"1","Bets":{"Bet":[{"@typeId":"123","@amount":"100","@seatId":"1"}]},"UserInfo":{"MinLimit":"100","MaxLimit":"200000","MaxPerSpotBet":"60000","GameTableId":"1","Chips":"100,500,2500,10000,50000","ExtraData":"norm"}}}}}&callback=json3&noCache=1561533554331 […]
json4('{"JSONClientGameResponse":{"Code":"GGP_CLIENT_GAME_RESPONSE","ErrorCode":"0","Message":{"RouletteSpinResponse":{"Code":"SC_ROULETTE_SPIN","Version":"1","Err":{"Code":"0","Description":null},"Spin":{"@autoPlay":"false","@totalAutoPlayRounds":"0","@autoPlayRoundsLeft":"0","@turbo":"0","@result":"36"},"Bets":{"Bet":{"@typeId":"123","@amount":"100","@winAmount":"200","@seatId":"1","@amountLeft":"59900"}},"HCNs":{"HCN":{"@id":"36","@wins":"1","@lrn":"0"}}}},"OperatorData":{"PlayerBalance":"99900"}}}');
Bitmaps
Sometimes, it is possible to detect that a PRNG does not generate really random numbers by using bitmaps. For example, the following pictures shows a bitmap created by using numbers generate by the well-known weak function Rand() (PHP 5.4.7 / Windows XP). An obvious pattern can be noticed so it can be deduced that the generated numbers could be predicted.
Figure 7 – PHP Rand() on Windows Bitmap
Despite the fact that detecting weak PRNGs is not trivial by using bitmaps, it is worth giving a try. The PHP script below could be used for such a purpose.
<?php header("Content-type: image/png"); $im = imagecreatetruecolor(512, 512) or die("Cannot Initialize new GD image stream"); $white = imagecolorallocate($im, 255, 255, 255); $file = "./numbersList.txt"; if (!file_exists($file)){ echo ("error. the file does not exist."); exit(); }else{ $fp = fopen($file, "r"); } for ($y = 0; $y < 512; $y++) { for ($x = 0; $x < 512; $x++) { $current_line = (int) fgets ($fp); if ($current_line % 2 == 0){ imagesetpixel($im, $x, $y, $white); } } } imagepng($im); imagedestroy($im);
It represents the winning numbers, which are stored in the file numbersList.txt, graphically. If the winning number is even, a white pixel is printed. Otherwise, the pixel remains black.
NIST
NIST [5] is a statistical test suite for random and pseudorandom number generators for cryptographic applications. It provides a comprehensive set of statistical tests for randomness as the following picture shows.
Figure 8 – NIST
A brief description of these tests is shown below:
Test | Defect Detected |
Frequency (monobit) | Too many zeroes or ones |
Frequency (block) | Too many zeroes or ones |
Runs test | Oscillation of zeroes and ones too fast or too slow |
Longest run of ones in a block | Oscillation of zeroes and ones too fast or too slow |
Binary matrix rank | Deviation from expected rank distribution |
Discrete fourier transform (spectral) | Repetitive patterns |
Non-overlapping template matching | Irregular occurrences of a prespecified template |
Overlapping template matching | Irregular occurrences of a prespecified template |
Maurer’s universal statistical Test | Sequence is compressible |
Linear complexity | Linear feedback shift register (LFSR) too short |
Serial | Non-uniformity in the joint distribution for m-length sequences |
Approximate entropy | Non-uniformity in the joint distribution for m-length sequences |
Cumulative sums (cusum) | Too many zeroes or ones at either an early or late stage in the sequence |
Random excursions | Deviation from the distribution of the number of visits of a random walk to a certain state |
Random excursions variants | Deviation from the distribution of the number of visits (across many random walks) to a certain state |
The input file which NIST is provided with should contain binary sequences stored as ASCII characters consisting of zeroes and ones. This means that the collected winning numbers should be turned into 0s and 1s previously. For example, even numbers can be 0s and odds numbers can be 1s.
It should be taken into consideration that minimum quantity of data varies depending on test requirements. For example, the Frequency (Monobit) test requires a minimum of 100 numbers. The following table shows the recommendations from NIST for each test.
Test | Input Size (n) |
Frequency (monobit) | 100 |
Frequency (block) | 2000 |
Runs test | 100 |
Longest run of ones in a block | 6272 |
Binary matrix rank | 38912 |
Discrete fourier transform (spectral) | 1024 |
Non-overlapping template matching | 1048576 |
Overlapping template matching | 998976 |
Maurer’s universal statistical Test | 387840 |
Linear complexity | 1000000 |
Serial | 500 |
Approximate entropy | 500 |
Cumulative sums (cusum) | 100 |
Random excursions | 1000000 |
Random excursions variants | 1000000 |
Once the tests have finished the results are displayed in the same way as in the following figure.
Figure 9 – Tests result
Those tests that have failed (the p-value is lower than the 0.01 significance level or the proportion of sequences passing the test is lower than 0.9) are marked with stars.
The results of statistical testing must be interpreted with some care and caution to avoid incorrect conclusions. More information about NIST can be found in [5] and [6].
This is probably the most complicated roulette to analyse, as it is necessary to take some measurements (such as winning positions, roulette speed, ball speed, etc.) which requires writing a specific script. This is a time-consuming task and therefore, it might be impossible to perform an appropriate assessment of roulette of this type during a usual security assessment because of time limits. Perhaps a more suitable option would be to carry out a research project on this kind of roulette applications.
A roulette of this kind cannot be biased due to a manufacturing defect (for obvious reasons). However, the software might be not well-designed causing the ball to be more likely to land on some specific pockets. Therefore, it is well worth checking that there are no significant statistical deviations in terms of winning positions frequencies.
Regarding live automatic roulette, the winning positions should be analysed but not the winning numbers unlike it is done in the case of virtual roulette. For example, the following figures show the initial (the ball has not been spun yet) and final positions (the ball was spun, landed in one of the pockets and the roulette wheel stopped) of a roulette. The winning position would be 23 as the winning number (21) was initially in this position.
Figure 10 – Live Roulette initial position
Figure 11 – Live Roulette final position
It is recommended that this kind of roulette implements one of the following security measures in order to prevent malicious players from predicting the winner number.
Such coefficients as air pressure, air friction or ball mass should vary slightly for every spin. The goal is to change the behaviour of the ball making it difficult to predict even the area where the ball is going to land, not to mention the winning number. An example of this can be observed in the following figure that describes the movement of two balls spun at different moments and having the same initial conditions (speed and acceleration). Both balls had the same position within first ten seconds. Then their behaviour changes and they land in different moments and different pockets.
Figure 12 – Balls’ positions
The wheel speed is almost constant due to its very low friction and therefore, it is relatively easy to predict the position of every pocket at a particular moment. In order to avoid this, once the betting session is closed (few second after the ball being spun) the wheel speed should be modified to make it impossible to predict the pockets’ position. An example of this security measure can be seen in the following figure. This describes the speed of a wheel before and after the betting session is closed.
Figure 13 – Wheel speed
It should be checked that the speed change is sufficient so that it would not be possible to predict the position of the pockets with a mistake lower than 36 positions at the time the ball leaves the rim (the outer track).
For example, if the maximum speed of the wheel is 11.5 pockets/second and the minimum speed is 10 pockets/second then the maximum error of a prediction will be 1.5 pockets/second (11.5 – 10).
Hence, after, for instance, 13 seconds the maximum error would be 19.5 pockets.
(MaxSpeed - MinSpeed) * time = (11.5 - 10) * 13 = 19.5 pockets
As the error is lower than 36 positions, a malicious player who can predict where and when the ball is going to land, could bet on 20 potential winning numbers and win good money at the rate of 85 cents per every euro bet.
Correlation is any statistical relationship, whether causal or not, between two random variables or bivariate data. It is interesting to check if there is any correlation between the following speeds as if this is the case then an attacker could predict the position of the wheel at any moment:
An example of a real correlation analysis will be given to demonstrate how important this test could be.
The following picture shows the different speeds described above:
Figure 14 – Wheel speeds
A correlation coefficient is a numerical measure of the degree of association between two continuous variables. The correlation coefficients of all the speeds (correlation matrix) can be calculated by using, for example, Microsoft Excel [7].
The closer the correlation coefficient is to either 1 or -1, the stronger the relationship between the two variables is. On the contrary, if the correlation coefficient is close to 0 then there is no a linear relationship between the variables.
The correlation matrix is showed below:
Figure 15 – Correlation between wheel speeds
As it can be observed in the correlation matrix, there is a correlation between the speed1 and speed2 as the correlation coefficient is close to one (0.817512961).
If the speed1 and speed2 of multiple spins are represented in a graph then the evident linear correlation can be noticed:
Figure 16 – Speed1 and Speed2 graph
Below a scatter plot graph [8] was used represent the same data:
Figure 17 – Speed1 vs Speed2 scatter plot graph
As can be seen, there are well-defined speed limits. Specifically, the speed2 is always lower than the speed1 plus one and higher than speed1 minus 0.8.
Taking this into consideration, the maximum error of the speed2 prediction after 13 seconds would be 23.4 pockets.
MAXspeed2 = speed1 + 1 MINspeed2 = speed1 – 0.8 MaxError = (MAXspeed2 – MINspeed2) * time = ((speed1 + 1) – (speed1 – 0.8)) * time = 1.8 * 13 = 23.4 pockets
As explained before a malicious player who can predict where and when the ball is going to land, could potentially bet on the range of potential winning numbers (24 numbers in this case).
For example, just a second before the session betting is closed a malicious player calculates that taking into account the position and speed of the ball and roulette wheel in that moment, the ball will land on the pocket number 30 in 13 seconds. Then, in order to ‘assure a victory’, they would have to bet on the following range of numbers:
[21,2,25,35,24,6,27,13,36,11,30,8,23,10,5,24,16,33,1,20,14,31,9,22]
Figure 18 – Range of potential winning numbers
In addition, it is worth mentioning that even if the malicious player makes a mistake and the ball lands on the 36th pocket or on the first pocket then this betting strategy would continue being profitable in the long run as there is margin for error.
[1] Roulette – Type of bets: https://en.wikipedia.org/wiki/Roulette#Types_of_bets
[2] Martingale: https://en.wikipedia.org/wiki/Martingale_(probability_theory)
[3] PNGR: https://en.wikipedia.org/wiki/Pseudorandom_number_generator
[4] Predicting the outcome of roulette: https://arxiv.org/pdf/1204.6412
[6] Random Number Generators: An Evaluation and Comparison of Random.org and Some Commonly Used Generators: https://www.random.org/analysis/Analysis2005.pdf
[7] Correlation in Excel: coefficient, matrix and graph: https://www.ablebits.com/office-addins-blog/2019/01/23/correlation-excel-coefficient-matrix-graph/
[8] Scatter plot: https://en.wikipedia.org/wiki/Scatter_plot