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 timeimport randomimport math
flag = "FAKE_FLAG"
m=math.ceil(time.time()*1000000)a = 2**10b = 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 mathimport timeimport randomfrom 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: DoneSEED: 1751669984568044NEXT 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