Roulette

June 23, 2025

السلام عليكم ورحمة الله وبركاته

Today we are going to face an nice challenge called roulette here the challenge gives us a roulette that we need to get 13 37 times but how can we do this let’s examine the source code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from hashlib import sha256
import secrets


def get_color(number):
    """Determine roulette color for a given number"""
    if number == 0:
        return 'green'
    return 'red' if number % 2 == 1 else 'black'


def main():
    print("Welcome to Provably Fair Roulette!")

    with open('flag', 'rt') as f:
        FLAG = f.read()

    streak_13 = 0
    while True:
        # Generate server seed and its hash
        server_seed = sha256(bytes(secrets.randbits(17))).hexdigest()
        server_seed_hash = sha256(server_seed.encode()).hexdigest()

        print(f"Server seed hash (verify later): {server_seed_hash}")

        # Get client seed
        print("Enter your client seed (press enter to generate): ", end="")
        client_seed = input().strip()
        if not client_seed:
            client_seed = secrets.token_bytes(8).hex()
            print(f"Generated client seed: {client_seed}")

        # Generate game hash
        combined = f"{server_seed}:{client_seed}"
        game_hash = sha256(combined.encode()).hexdigest()
        hash_int = int(game_hash, 16)

        # Calculate roulette result
        roulette_number = hash_int % 37  # 0-36
        roulette_color = get_color(roulette_number)

        # Get user's bet
        while True:
            print("Place your bet (number 0-36 or color red/black/green): ", end="")
            bet = input().strip().lower()
            if bet in ['green', 'red', 'black']:
                break
            try:
                num = int(bet)
                if 0 <= num <= 36:
                    bet = str(num)  # Standardize to string for comparison
                    break
                print("Number must be between 0-36")
            except ValueError:
                print("Invalid bet. Enter number (0-36) or color (red/black/green)")

        # Determine result
        result_str = f"{roulette_number} ({roulette_color})"
        print(f"\nThe wheel lands on: {result_str}")

        # Check win conditions
        win = False
        if bet.isdigit():
            win = int(bet) == roulette_number
        else:
            win = bet == roulette_color

        if win:
            print("Congratulations! You win! ")
            if roulette_number == 13:
                print("...and you got 13, double congratulations!")
                streak_13 += 1
            else:
                print("But it's not 13, no streak for you")
                streak_13 = 0
        else:
            print("Sorry, you lose!")
            streak_13 = 0

        # Verification information
        print()
        print("Verification Details:")
        print(f"Server seed: {server_seed}")
        print(f"Client seed: {client_seed}")
        print(f"Combined string: {combined}")
        print(f"Game hash: {game_hash}")
        print(f"Calculated number: {roulette_number}")
        print(f"Resulting color: {roulette_color}")

        if streak_13 == 37:
            print("How? How is it possible? What was the chance?! "
                  f"Anyway, here's your flag, congratulations... {FLAG}")
            exit()


if __name__ == "__main__":
    main()

here if you notice the code used “randbits(17)” here lies the weakness that a random 17 bits is 131072 possible numbers and this is easy to brute force so we can after receiving the server hash we can calculate the the server hash here we can advance to next step.

Here how is the roulette number generated ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
print("Enter your client seed (press enter to generate): ", end="")
client_seed = input().strip()
if not client_seed:
    client_seed = secrets.token_bytes(8).hex()
    print(f"Generated client seed: {client_seed}")

# Generate game hash
combined = f"{server_seed}:{client_seed}"
game_hash = sha256(combined.encode()).hexdigest()
hash_int = int(game_hash, 16)

# Calculate roulette result
roulette_number = hash_int % 37  # 0-36
roulette_color = get_color(roulette_number)

So here either the client seed is an input from the user or generated from the server so here to get 13 we need to calculate a client seed.

Here is a code snippet the calculates the server seed and client seed

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def compute_server_seed(server_seed_hash):
    for i in range(131072):
        candidate = b'\x00' * i
        server_seed = hashlib.sha256(candidate).hexdigest()
        hashed = hashlib.sha256(server_seed.encode()).hexdigest()
        if hashed == server_seed_hash:
            return server_seed
    raise Exception("Server seed not found")

def find_client_seed_for_13(server_seed):
    i = 0
    while True:
        client_seed = f"{i:08x}"
        combined = f"{server_seed}:{client_seed}"
        hash_int = int(hashlib.sha256(combined.encode()).hexdigest(), 16)
        if hash_int % 37 == 13:
            return client_seed
        i += 1

and this is how you solve the challenge.

Hope you enjoyed the write-up.

Categories: