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