1xbet
Writeup of misc/1xbet challenge that I solved in Ingehack 2025
1xbet
Last weekend, I had the chance to participate in Ingehack CTF 2025(4th Edition) Organized by Ingeniums and I managed with my team @m1nt4l1ty to secure the 3rd place. Here is a writeup for one of the challenges I solved during this ctf.
Challenge Overview
- CTF: Ingehack 4th Edition
- Challenge: 1xbet
- Category: Misc
- Points: 430 (8 solves)
- Description: There are no losers , only quitters
- Connection info :
ncat 1xbet.ctf.ingeniums.club 1337 --ssl
- Challenge file : casino.py
Initial Thoughts
- After reading the source code and connecting to the instance we understand that we must guess the winning number each round :
1 2 3 4 5 6
if user_guess == winning_number : if self.current_round <= self.total_rounds - 1: print("Correct! Moving to the next round.\n") elif self.current_round == self.total_rounds : print("u beated the house congrats !!! ") print(f"here is your flag : {FLAG}")
- The winning number is a 32bit number generated with
random.getrandbits
. - So we must break the 32bit PRNG with only 6 values to get the 1000 winning numbers.
Searching
- The first thing that I searched for was Breaking PRNG with 6 values.
- After the search, I found this article in the head of the results, and after reading it we understand that we can recover 32bitseed with 6 values and 64bitseed with 8 values.
- I got lazy to write a script from scratch so i have directly visited thier github and i found a this repo python-random-playground
- The scripts that we need are named recover_32bitSeed.py and functions.py
Final solve script
- After having all what we need the only remaining step is to write a solve script. Beacause of my bad scripting skills, I tried many times to write a solve script and i just got many issues one of them is that mersenne implementation recovers two seeds one of them is the wrong seed. my teammate Koyphshi has solved this by testing the 6 values that we got with the indexes of what we generated. I can’t believe that I didn’t think of this :
1 2 3 4 5 6 7 8
for seed in (seed1, seed2): random.seed(seed) predicted = [random.getrandbits(32) for _ in range(1000)] if (predicted[0] == outputs[0] and predicted[227] == outputs[227] and predicted[1] == outputs[1] and predicted[228] == outputs[228] and predicted[2] == outputs[2] and predicted[229] == outputs[229]): trueseed = seed break
- Then after getting all the 1000 values we send them one by one :
1 2 3 4 5
for i in range(1000): io.sendlineafter(b"Guess the number: ",str(predicted[i]).encode()) response = io.recvline().decode().strip() log.info("Round {}: {}".format(i+1, response)) io.interactive()
- After getting all the 1000 correct guesses we get the Flag :
ingehack{PYThON_rANdOm_1SNt_tH4T_$3cuRE_AFTER_4lL_$h0uT0uT_t0_MeRs3nn3}
The full solve script can be found here
This post is licensed under CC BY 4.0 by the author.