Link Search Menu Expand Document

minigen

A stream cipher in only 122 bytes!

Note: This has been tested on python versions 3.8 and 3.9

Challenge

TL;DR: Figure out the value of next(g) (key) used to encrypt its corresponding plaintext’s byte. Then, xor next(g) with the ciphertext to get the flag.

We are given the following ciphertext:

281 547 54 380 392 98 158 440 724 218 406 672 193 457 694 208 455 745 196 450 724

And the source code:

exec('def f(x):'+'yield((x:=-~x)*x+-~-x)%727;'*100)
g=f(id(f));print(*map(lambda c:ord(c)^next(g),list(open('f').read())))

The recurrence relation to generate each of the key is key[i] = key[i - 1] + (key[i - 1] - key[i - 2] + 2)

From the above recurrence relation, it is clear that we need to know at least the first two keys used to encrypt the plaintext. Since the flag starts with rarctf, we can obtain the first two keys:

>>> ord('r') ^ 281
363
>>> ord('a') ^ 547
578

Once we have obtained the keys used to encrypt the plaintext, we xor the key with its corresponding ciphertext to get the flag.

Working POC:

arr = [281, 547, 54, 380, 392, 98, 158, 440, 724, 218, 406, 672, 193, 457, 694, 208, 455, 745, 196, 450, 724]

g = [363, 578]

def crack_gen():
    for i in range(1, 20):
        diff = g[i] - g[i - 1]
        next_gen = (g[i] + diff + 2) % 727 
        g.append(next_gen)

def get_flag():
    for i in range(len(arr)):
        print(chr(arr[i] ^ g[i]), end="")

crack_gen()
print("Flag: ", end="")
get_flag()

Output of the script:

$ python3 crypto.py
Flag: rarctf{pyg01f_1s_fun}

Flag: rarctf{pyg01f_1s_fun}