crypto/dream

104 solves / 448 points

Writeup by Aryan

NOTE: This writeup is a valid solution to both dream and dream-revenge. There was a cheese in dream which I did not notice, leading me to take the long road and find the intended solution.

We are given a script which encrypts a flag with AES. Before encrypting the flag, the program takes in a list of 8 indexes as inputs. It then randomly generates 624 numbers and gives us the numbers at the indexes we requested, as shown below:

# taken from challenge source
from ast import literal_eval
idxs = literal_eval(input(">>> "))
if len(idxs) > 8:
    print("Ha thats funny")
    exit()
for idx in range(624):
    rand_out = random.getrandbits(32)
    if idx in idxs:
        print(rand_out)

The idea is to somehow crack the random seed with access to only 8 generated numbers. This task is trivial if we had access to all 624 generated numbers, thanks to randcrack. However, having 8 numbers makes it a lot harder.

Here's where the cheese comes in. The server code reads the seed from a text file; see below:

from os import urandom
# check if seed.txt exists
try:
    seed = open("seed.txt", "rb").read()
except:
    seed = urandom(8)
    open("seed.txt", "wb").write(seed)

If the seed is the same every time, we can just enter indices 0, 1, ... 7 on our first run, then 8, 9, ... 15 on our second run, and so on, where we enter indices 8n, 8n+1, ... 8n+7 on the nth run. We do this until we get all 624 numbers, at which point we use randcrack.

However, I didn't see this, and found the actual solution.

Doing a bit of searching leads us to an interesting blog on breaking Python's PRNG. The bottom of this blog contains a link to a GitHub repository with example seed recovery code.

Using the seed recovery code provided and then decrypting the AES, we are able to get our flag.

Flag: vsctf{dream_luck???_5e3ec2f2d338fc9f}

Last updated