Mind Reader

This is a challenge from Cryptography section of Standcon 2021. The challenge wants us to break the PRNG system.

Statement

So you think you are the best mind reader in the entire galaxy? We shall see about that…

Observation

After reading the code, we can find out that the server is using Python library random to generate the namelist.

Looking at the python documentation for random. They explicitly state that

Warning : The pseudo-random generators of this module should not be used for security purposes. For security or cryptographic uses, see the secrets module.

Can we exploit the python random library?

Solution

Yes we actually can! There’s also an online library for that!

Internally python uses mersenne twister PRNG.

I did not research on how the algorithm and the exploits work.

Basically the PRNG is not cryptographically secure.

import random
from randcrack import RandCrack
from pwn import *

def main():
    r = remote('20.198.209.142', 55003, level='debug')

    firstName = open('./files/first-names.txt', 'r').read().split('\n')
    lastName = open('./files/last-names.txt', 'r').read().split('\n')
    game = open('./files/game.txt', 'r').read().split('\n')

    dicFirst = {}
    dicLast = {}

    for i in range(len(firstName)):
        dicFirst[firstName[i]] = i

    for i in range(len(lastName)):
        dicLast[lastName[i]] = i

    arr = []
    highscore = []

    r.recvuntil('>')
    r.sendline('2')
    r.recvuntil('>')
    r.sendline('2')

    r.recvuntil('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n')

    for i in range(350):
        rank = r.recvline().split()
        fn = rank[0].decode()
        ln = rank[1].decode()
        highscore.append(int(rank[2].decode()))
        arr.append((dicLast[ln] << 16) + dicFirst[fn])

    arr += highscore
    toPredictArr = []
    rc = RandCrack()
    for i in range(730):
        if (i < 624):
            rc.submit(arr[i])
        else:
            x = rc.predict_getrandbits(32)
            toPredictArr.append(game[x & 0xFFFF])

    toPredictArr = toPredictArr[-30:]
    r.recvuntil('>')
    r.sendline('1')

    for i in range(30):
        r.recvuntil('Letter:')
        r.sendline(toPredictArr[i])
    print(r.recvuntil(b'}'))

if __name__ == '__main__':
    main()        

flag : STC{w0w_wHa7_An_UneXPec7eD_7Wi57}

Updated: