CTF Writeups
WebsiteDiscordSocials..
  • CryptoCat's CTF writeups
  • 2025
    • Tsuku
      • Web
        • len_len
        • Flash
        • YAMLwaf
    • CTF@CIT
      • Web
        • Breaking Authentication
        • Commit & Order: Version Control Unit
        • How I Parsed your JSON
        • Mr. Chatbot
        • Keeping Up with the Credentials
  • 2024
    • CryptoCat
      • Summar-AI-ze
    • Intigriti
      • Warmup
        • BabyFlow
        • In Plain Sight
        • IrrORversible
        • Layers
        • Rigged Slot Machine 1
      • Game
        • Bug Squash 1
        • Bug Squash 2
      • Rev
        • Secure Bank
      • Web
        • Biocorp
        • Cat Club
        • Pizza Paradise
        • SafeNotes 2.0
      • Misc
        • Quick Recovery
        • Triage Bot 2
      • Pwn
        • Floormat Sale
        • Retro2Win
        • Rigged Slot Machine 2
        • UAP
      • Crypto
        • Schrodinger's Pad
      • Mobile
        • Cold Storage
      • OSINT
        • No Comment
        • Trackdown
        • Trackdown 2
      • Forensics
        • CTF Mind Tricks
        • Hoarded Flag
        • Password Management
    • CSAW
      • Web
        • Playing on the Backcourts
        • Log Me In
        • Lost Pyramid
        • BucketWars
    • CyberSpace
      • Web
        • Feature Unlocked
    • UIU
      • Web
        • Fare Evasion
        • Log Action
    • Wani
      • Web
        • Bad Worker
        • PoW
        • One Day One Letter
    • Akasec
      • Web
    • HTB Cyber Apocalypse
      • Web
        • Flag Command
        • TimeKORP
        • KORP Terminal
        • Labyrinth Linguist
        • Locktalk
        • SerialFlow
        • Testimonial
  • 2023
    • Intigriti
      • Gamepwn
        • Dark Secrets
      • Misc
        • Triage Bot
      • OSINT
        • Photographs
      • Pwn
        • Floormat Store
      • Web
        • Bug Report Repo
        • My Music
    • Imaginary
      • Web
        • Blank
        • IDORiot
        • Inspection
        • Login
        • Perfect Picture
        • Roks
    • Google
      • Pwn
        • Write-Flag-Where
    • Cyber Apocalypse
      • AI
        • Last Hope
        • Mysterious Learning
      • Crypto
        • Perfect Synchronization
      • Pwn
        • Getting Started
        • Labyrinth
        • Pandora's Box
        • Void
      • Rev
        • Cave System
        • Hunting License
        • Needle in a Haystack
        • Shattered Tablet
        • She Sells Sea Shells
    • Sekai
      • Rev
        • Azusawa's Gacha World
    • Amateurs
      • Web
        • Sanity
        • Waiting an Eternity
    • NahamCon
      • Web
        • Hidden Figures
        • Marmalade 5
        • Obligatory
        • Star Wars
        • Stickers
    • Angstrom
      • Pwn
        • Leek
  • 2022
    • Imaginary (iCTF)
      • Crypto
        • ASE
      • Pwn
        • Links 1
        • Links 2
        • Links 3
        • Open Doors
    • SEETF
      • Pwn
        • 4mats
        • Easy Overflow
      • Rev
        • BabyReeee
      • Web
        • Super-Secure-Requests-Forwarder
    • HTB Cyber Apocalypse
      • Pwn
        • Hellbound
    • Angstrom
      • Pwn
        • Really Obnoxious Problem
        • Wah
        • Whats My Name
        • Where Am I
      • Web
        • Crumbs
        • Xtra Salty Sardines
    • NahamCon
      • Pwn
        • Baby Steps
      • Web
        • Flaskmetal Alchemist
        • Hacker Ts
        • Two for One
    • Pico
      • Forensics
        • Side Channel
      • Pwn
        • Buffer Overflow 1
        • Buffer Overflow 2
        • Buffer Overflow 3
        • Flag Leak
        • Function Overwrite
        • ROPfu
        • RPS
        • Stack Cache
        • Wine
        • X-Sixty-What
      • Rev
        • Wizardlike
      • Web
        • Noted
    • Space Heroes
      • Pwn
        • Vader
      • Web
        • Flag in Space
    • Intigriti
      • Pwn
        • Bird
        • Cake
        • Easy Register
        • Search Engine
    • Dice
      • Pwn
        • Interview Opportunity
  • 2021
    • Pico
      • Pwn
        • Unsubscriptions Are Free
    • Crusaders of Rust (COR)
      • Crypto
        • Fibinary
      • Pwn
        • Chainblock
    • HTB Cyber Santa
      • Crypto
        • Meet Me Halfway
        • Xmas Spirit
      • Pwn
        • Minimelfistic
        • Mr. Snowy
        • Naughty List
        • Sleigh
      • Rev
        • Infiltration
        • Intercept
    • K3rn3l
      • Crypto
        • Badseed
        • Twizzty Buzzinezz
    • HTB x Synack RedTeamFive
      • Misc
        • Context
        • Hotel
      • Pwn
        • Air Supplies
        • Injection Shot
        • Library
        • Recruitment
      • Rev
        • Knock Knock
        • Split
    • KillerQueen
      • Pwn
        • A Kind of Magic
        • Tweety Birb
        • Zoom2Win
    • HacktivityCon
      • Pwn
        • Retcheck
        • The Library
        • Yabo
      • Web
        • Availability
    • CSAW
      • Pwn
        • Alien Math
        • Password Checker
      • Rev
        • Checker
    • HackyHolidays
      • Crypto
        • Cute Invoice
        • Mineslazer
      • Forensics
        • Injection Traffic
        • Power Snacks
      • Pwn
        • Deleted Flag
        • Engine Control
      • Web
        • Skylark
    • HTB Cyber Apocalypse
      • Crypto
        • Phasestream
      • Misc
        • Alien Camp
        • Build Yourself In
      • Pwn
        • Controller
        • System Drop
      • Web
        • Blitzprop
        • E-Tree
        • Wild Goose Hunt
    • Angstrom
      • Pwn
        • Sanity Checks
        • Secure Login
        • Sticky Stacks
        • Tranquil
      • Rev
        • Free Flags
        • Jailbreak
      • Web
        • Jar
Powered by GitBook
On this page
  • Video walkthrough
  • Challenge Description
  • Solution
  • Static Analysis
  • Solve.py
  • Performance
  1. 2024
  2. Intigriti
  3. Warmup

Rigged Slot Machine 1

Writeup for Rigged Slot Machine 1 (Warmup) - 1337UP LIVE CTF (2024) πŸ’œ

PreviousLayersNextGame

Last updated 4 months ago

Video walkthrough

Challenge Description

The casino thinks they've rigged their slots so well, they can give every player $100 free play! Of course, there's always some small print: you can't cash out unless you hit the jackpot and each player only gets 3 minutes playtime

This wasn't a challenge I intended to make, it's the result of an unintended solution in the Rigged Slot Machine pwn challenge (patched in part 2) so I figured I'd keep it in as a warmup reversing/programming exercise πŸ€·β€β™‚οΈ

Solution

checksec indicates all binary protections are enabled so we can assume it's not a pwn challenge (warmup lol). Perhaps start by testing the basic functionality of the app.

./rigged_slot1
Welcome to the Rigged Slot Machine!
You start with $100. Can you beat the odds?

Enter your bet amount (up to $100 per spin): 10
You lost $10.
Current Balance: $90

Enter your bet amount (up to $100 per spin): 10
You won $40!
Current Balance: $130

Enter your bet amount (up to $100 per spin): 10
You lost $10.
Current Balance: $120

Enter your bet amount (up to $100 per spin): 10
You lost $10.
Current Balance: $110

Enter your bet amount (up to $100 per spin): 10
You won $10!
Current Balance: $120

Enter your bet amount (up to $100 per spin): 100
You won $400!
Current Balance: $520

Enter your bet amount (up to $100 per spin): 100
You lost $100.
Current Balance: $420

Yes, I made this shortly after returning home from Vegas πŸ˜‚

Since players only receive the binary (no sauce), let's check the winning condition in ghidra.

Static Analysis

I renamed variables already to make it clearer, here's the main function.

setup_alarm(180);
balance = 100;
puts("Welcome to the Rigged Slot Machine!");
puts("You start with $100. Can you beat the odds?");
do {
  while( true ) {
    while( true ) {
      bet = 0;
      printf("\nEnter your bet amount (up to $%d per spin): ",100);
      local_10 = __isoc99_scanf(&%d,&bet);
      if (local_10 == 1) break;
      puts("Invalid input! Please enter a numeric value.");
      clear_input();
    }
    if ((bet < 1) || (100 < bet)) break;
    if ((int)balance < bet) {
      printf("You cannot bet more than your current balance of $%d!\n",(ulong)balance);
    }
    else {
      play(bet,&balance);
      if (133742 < (int)balance) {
        payout(&balance);
      }
    }
  }
  printf("Invalid bet amount! Please bet an amount between $1 and $%d.\n",100);
} while( true );

The play function will spin the slot.

outcome = rand();
outcome = outcome % 100;
if (outcome == 0) {
  multiplier = 100;
}
else if (outcome < 10) {
  multiplier = 5;
}
else if (outcome < 15) {
  multiplier = 3;
}
else if (outcome < 20) {
  multiplier = 2;
}
else if (outcome < 30) {
  multiplier = 1;
}
else {
  multiplier = 0;
}
result = bet * multiplier - bet;

The payout function will print the flag.

if (*balance < 133743) {
  puts("You can\'t withdraw money until you win the jackpot!");
  exit(-1);
}
file = fopen("flag.txt","r");
if (file == (FILE *)0x0) {
  puts(
      "Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server."
      );
  exit(0);
}
fgets(flag,0x40,file);
printf("Congratulations! You\'ve won the jackpot! Here is your flag: %s\n",flag);
fclose(file);

So, what's the problem? Look at those odds! 1/100 spins will get a 100x multiplier πŸ‘€

If I run the program 100 times with a starting bet of $100, the chances are one of those spins will win me $10,000 πŸ€‘ That's enough capital to continue betting MAX BET (of course we'll get some 2-5x along the way too).

Solve.py

Here's my solve script. It starts with a $25 bet and then switches to $100 once the balance exceeds $10,000. You can play around with these values and see what works best, e.g. $10 starting bet, then switch to $100 when you reach $5000.

import sys
from pwn import *

# Allows switching between local/GDB/remote execution
def start(argv=[], *a, **kw):
    if args.GDB:
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:
        return process([exe] + argv, *a, **kw)

# Binary filename
exe = './rigged_slot1'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'info'

# Jackpot threshold
winning_balance = 133742
got_flag = False

while not got_flag:
    # Start the program
    io = start(level='warn')
    balance = 100
    amount = b'25'

    # Skip the initial output
    io.recvlines(3)
    io.sendline(amount)

    count = 1

    while balance > 0 and not got_flag:
        try:
            # Adjust bet amount
            if balance > 10000:
                amount = b'100'
            io.sendlineafter(b':', amount)
            io.recvuntil(b'Current Balance: ')
            balance = int(io.recvline().decode()[1:].strip())
            info(f'balance: {balance}')
            count += 1
            # Check if we've hit the jackpot (remote)
            if balance >= winning_balance:
                warn("Jackpot threshold reached. Attempting to retrieve flag.")
                flag_output = io.recvline().decode()
                info(flag_output)
                got_flag = True
        except EOFError:
            # Capture flag output in case of EOF (local)
            remaining_output = io.recvall(timeout=5).decode()
            if 'Congratulations' in remaining_output:
                warn("Flag captured via EOF handling:")
                info(remaining_output)
                got_flag = True
            break
        except Exception as e:
            warn(f'Error after {count} bets: {str(e)}')
            break

    warn(f'Total bets placed: {count}')
    io.close()

Interestingly, solving the challenge locally is slightly different to remote. Notice that for the remote exploit, we get the flag by checking the winning balance whereas the local flag prints with an EOF error. I'm not sure if this is related to pwntools or something else, but maybe it will cause some confusion in the CTF (especially for a warmup challenge) 😬

Performance

Anyway, testing this locally is fast. Obviously it varies for each run due to the random element but with 3 attempts I got an average of 4 seconds*, with the fastest being 1s.

[*] balance: 126825
[!] Flag captured via EOF handling:
[*]  You won $9900!
    Current Balance: $136725
    Congratulations! You've won the jackpot! Here is your flag: fake_flag

[!] Total bets placed: 1294

real	0m1.026s
user	0m0.580s
sys	0m0.472s

*Note that this is the time for all runs, not the winning run. For example, when you run the script it might lose 100 games in a row before it wins. Some of those games may take a few seconds, but could be longer. In other words, if time shows over 180 seconds and you still won, it's because the final game didn't exceed the limit.

Solving remotely certainly takes longer, but it's possible to solve well within the allocated 180 second time limit. In 3 attempts I got an average of 62 seconds, with the fastest being 34s.

[*] balance: 136800
[!] Jackpot threshold reached. Attempting to retrieve flag.
[*] Congratulations! You've won the jackpot! Here is your flag: INTIGRITI{ju57_l1k3_7h47_y0u_4r3_4_m1ll10n41r3!}
[!] Total bets placed: 1446

real	0m33.884s
user	0m0.866s
sys	0m0.678s

Flag: INTIGRITI{ju57_l1k3_7h47_y0u_4r3_4_m1ll10n41r3!}

Note: I have to apologise to some players here. After the event I helped debug a players script and it turns out I didn't leave enough time for those really far from the server (India, Australia etc) with poor connections. I should of tested my solution with a VPN connected in various countries before the event, it didn't occur to me but I know for next time! πŸ’œ

found a nice unintended that works on Rigged slot 1. Using careful timing, you could predict the "random" seed and therefore the outcome of each bet 🧠 Check out the 😎

Someone
writeup