404 words
2 minutes
Robust PRNG

Robust_PRNG_in_Python#

Let’s try to hack the generator of pseudo-random number (PRN) of the Python interpreter. We assume that the PRNs are generated by the getrandbits(31) function of the standard random library. You can ask for several consecutive PRNs, up to 1000 pieces. And then you have to guess the next PRN.

Server Code#

import time
import random
import math
flag = "FAKE_FLAG"
m=math.ceil(time.time()*1000000)
a = 2**10
b = 2**30
random.seed(m)
rand_numbers = []
d = """
Попробуем взломать генератор псевдослучайных чисел (ПСЧ) Python.
Считаем, что ПСЧ создаются функцией getrandbits(31) стандартной библиотеки random.
Вы можете попросить несколько последовательных ПСЧ, до 1000 штук.
А затем вы должны угадать следующее ПСЧ."""
print (f"{d}\n\n")
print ('1. Получить следующее число')
print ('2. Угадать следующее число')
ind = 0
while True:
if ind == 1000:
print ('Слишком долго думаешь. Отдохни ...')
break
inp = input('> ')
if inp == '1':
print (f"Следующее число: {random.getrandbits(31)}")
elif inp == '2':
ans = int(input('Ваше число: '))
my_ans = random.getrandbits(31)
if ans == my_ans:
print (f"\nФлаг: {flag}")
else:
print (f"\nОшибка. Мое число: {my_ans}")
break
ind += 1

Code is very similar to the LCG challenge but this time we get 1000 numbers and we have to guess what random.getrandbits(31) will output

If we can figure out what the next output is we will be able to get the flag

The easiest way to solve this is by seeing that the seed for random is generated by the current time*1000000. So all we need to do is to start with a seed of our current time. Then we keep decreasing the number until the first number we see is the same number that the server sent. Once we have the seed all we need to do is just call random.getrandbits(31) to get the next number

Solution#

import math
import time
import random
from pwn import *
def getnextguess(n):
i = math.ceil(time.time()*1000000)
random.seed(i)
while(random.getrandbits(31) != n):
i -= 1
random.seed(i)
print("SEED:",i)
return random.getrandbits(31)
io = remote('ctf.mf.grsu.by', 9045)
output = io.readuntil(b'>')
io.sendline(b"1")
o = io.recvline()
num = int(o.decode().split(': ')[1].strip(" \n'"))
print("NEXT NUM: ", getnextguess(num))
io.interactive()

Running this outputs our next num and we input that and get our flag like so

[+] Opening connection to ctf.mf.grsu.by on port 9045: Done
SEED: 1751669984568044
NEXT NUM: 22866641
[*] Switching to interactive mode
> $ 2
Ваше число: $ 22866641
Флаг: grodno{cebcc057543753644953923237233143560f6e63}

This same thing also works on the previous Python_PRNG_hack and even on the LCG one which makes it really easy

Conclusion#

Dont use pythons random functions they are not cryptographically secure