Another sunny day in the cyber security world, welcome back security folks. Today, in the fourth and for now last part of this blog series, we will focus on how to build our own backend for the slot machine. The goal is to try a technique where we can control the behavior of the slot machine. Start the coffee maker, mute all notifications and start your VSC, and we’re off.
warning note
I would like to point out that I do not support gambling. It can very quickly become a very dangerous thing where you can lose everything. Casinos are neither your friends nor charities, in the end they want your money and they get it, ALWAYS. I will not mention any casinos by name because I do not want to endorse them. If you already have symptoms of addiction, so the following blog may trigger you, simply do not read it. There are many places that can help you, such as “responsiblegambling“. Take care of yourselves!
Introduction
With that clarified, a quick recap. In the last two blogs “Jackpoting Online Slots – part two” and “Jackpoting Online Slots – part one” we looked at the techniques of data analysis and payload injection. There were some fun results, but we didn’t get to our goal. Now we are looking to bring the backend under our flag. We want to make the slot communicate with a game engine that we are developing, so that we have full power over the slot. Therefore, we will deal with the bold marked technique.
- Winning indicator
- Recurring Events Analysis
- Request Payload Attack
- Response Interception Attack
- Backend Forgery Attack
Execution
We already know, when we call the site, that the slot basically communicates with two hosts. One is the CDN, which is responsible for sending the configs, images, music and all that stuff. The second host is the actual game engine that the slot communicates with to set spins, collect winnings, etc. This is where we want to start, what if we could provide this game engine ourselves. Then we could control the balance, control exactly when a profit should come or even how high a profit should be. We already have all the basic knowledge from the last blogs. We know what a request looks like and how the game engine responds to it. Here are the URLs again.
The “alvcw” is the CDN host, we don’t really need to manipulate it, but it would be interesting. The “alvflyp” is our target for today. Now we have two ways to tell the slot to communicate with our super duper backend instead of the original boring one. Either we find in some config or script the URL that the slot should use and adapt it to ours, that would be variant one, or variant two we fake the DNS response so that our client connects to our spoofed IP. In the first case we don’t have to worry about certificates (https), but in the second case this is a problem we have to solve. So let’s start with the first one. If it works, we will skip the second one.
After a quick analysis in the devtools, it turns out that the “alvcw” endpoint “/Casino/GameLoader” provide a JavaScript that has all the necessary information to start the game. And there are two hardcoded URLs in it.
this.configuration.server = "https://alvflyp.playngonetwork.com";
LoginRequest("https://alvflyp.playngonetwork.com", pid, "", "", ........
Let’s try to adjust them on the fly with Burp Suit so that they are replaced by our super nice URL “https://collfuse.com/jackpoting”. Of course, we could do this manually, but we’re too busy for that, and we might not catch all the entries. So we just write a rule in Burp that replaces the URL in all response bodies for us, oh man, we are good.
This is already working quite well. When we restart the game, we see that the initial POST request (remember the three initial requests from the second blog) goes directly to us. But now the game won’t start because it doesn’t get a correct answer from us. So let’s write the code to include the initial requests. We do this simply in PHP and here is the created code.
<?php
# create session
if(empty(session_id())) session_start();
# define variables
$data = file_get_contents('php://input');
if(isset($_SESSION["balance"])) { $balance = $_SESSION["balance"]; } else { $balance = "100000000"; }
$sessionid = "NCFam45z9hf";
# game setup
if ((str_contains($data, 'd=1')) && (str_contains($data, '103'))) {
echo 'd=103 ' .$sessionid;
exit;
}
if ((str_contains($data, 'd=2')) && (str_contains($data, '101'))) {
echo "d=103 \"$sessionid!5803535\"\r\n101 5803535 \"USD\" \"\" \"CW\" \"EYMnHXuvfCzfporB8_1_USD\" \"JWT TOKEN\"\r\n127 \"2023-05-15T12:54:23Z\"";
exit;
}
if ((str_contains($data, 'd=3')) && (str_contains($data, '104'))) {
echo "d=104 1\r\n54 11 1 2 3 4 5 10 20 50 100 150 200 1" . '57 \"<custom><RTP Value=\"96\" /></custom>\"' . "60 96 0 0\r\n52 $balance 0 0\r\n83 0\r\n91 193047046 \"JWT TOKEN\"\r\n109";
exit;
}
?>
With this code and the Burp rule, the game starts cleanly. It is also visible in the dev tools that the slot now connect to our fake backend, magnificent.
But so far we can’t do much, at the moment we can’t even spin because we haven’t implemented that case yet and the game freezes immediately after pressing start. We already know that for a win it is important that there are two POST requests. One that does the spin itself and one that collects the win. So let’s start right away and map both requests by extending our code as follows. We add the following at the end.
# spin response
# win $200
if ((str_contains($data, '1 1 10 200 1'))) {
$_SESSION["balance"] = $balance + 20000;
echo "d=1 1 10 200 9 3 2 9 4 5 4 7 1 0 2 8 2 0 3 0 2 1 9 2 2 10 7 7 3 2 40 1 1 3 3 3 3 6 1 4 6\r\n3 1 50 20000 0 1";
exit;
}
# win collect
if ((str_contains($data, '4 0')) && (str_contains($data, $sessionid))) {
echo "d=5 5\r\n6 20000\r\n52 $balance 0 0";
exit;
}
?>
World class, now we have a win of $200 on every spin, whoop whoop! But the pride goes before the fall, as with the other methods, our funds are not transferred to the casino. It feels like a rabbit hole, we were so **** close. Sadly, even this method does not bring the desired success. I will now analyze the scripts in detail and if I find a solution to the balance synchronization problem, I will create a new blog about it. But first, I need to take a break from all those numbers, winning lines, flashing lights, and shrill sounds and focus on other security topics.
Until then, stay tuned and see you soon!
** midjourney string “casino player sitting at a slot machine sad he lost everything with head down with tears, cinematic lighting –ar 1:2 –q 2“