This is one of the hardest CTF I’ve been in. My team did really well to qualify for the finals in Sweden, and I wish them all the best as I will not be joining them.
The Gambler
This was a DotNet binary, we so could use DNSpy to inspect the full source code

In the main file, we had to key in a value, which was used as a seed for creating a random number generator.
This random number generator is then used to generate a whole bunch of different numbers, specifically int a
, int b
, and int c
.
The generator is then passed into Program.play()
, which looks like this

Play generates random numbers, and takes one bit from a location specified by mask, which is a list of 208 integers, and adds it to num

Looking at the main function, num must be equals to 95.
The program then validates a, b and c in this function

We can derive c and b pretty easily, and a looks pretty crazy to solve. Fortunately, my team mate knew of an approach called Z3 https://github.com/Z3Prover/z3 which essentially takes in a bunch of constrains, and gives the value that meets those constrains.
With that, a = 793646200, b = 254483726 and c = 973340155
Using these values, we brute force the seed value. Crazy huh, but possible. It gives us the seed value of 866551920
We now enter the seed to the program, and the flag value is actually in the Program.play() function. Instead of adding the 1s and 0s to num, we extract those values out, and convert them from binary to ASCII
We edit the method to look something like this, and run it with 866551920 as the input


Convert the binary to ASCII to get the flag!

Leave a Reply