­

TG:HACK 2020 Writeup - A game of keys

Download this file and get the flag. You will also need this wordlist.

The provided files are a python compiled file (keygame.pyc) and a txt file with a list of gibberish words (wordlist.txt). The wordlist didn't seem very interesting, so I proceeded to decompile the python file

$ uncompyle6 keygame.pyc > keygame.py

And had a look at the source code

import base64
from itertools import cycle

class myGame:

    def __init__(self, xdim=4, ydim=4):
        self.x = xdim
        self.y = ydim
        self.matrix = []
        for i in range(self.x):
            row = []
            for j in range(self.y):
                row.append(0)

            self.matrix.append(row)

    def make_keys(self, *args, **kwargs):
        words = []
        with open('wordlist.txt') as (f):
            for line in f:
                words.append(line.strip())

            for i in range(self.x):
                for j in range(self.y):
                    self.matrix[j][i] = words[(i + j)]

        keyArray = []
        keyArray.append(self.matrix[args[0]][args[1]])
        keyArray.append(self.matrix[args[2]][args[3]])
        key = ''
        for k in keyArray:
            key = key.strip() + str(k).strip()

        print(key)
        return key

    def checkdata(self, key):
        f = base64.b64decode('NSYDUhoVWQ8SQVcOAAYRFQkORA4FQVMDQQ5fQhUEWUYMDl4MHA==')
        data = f.decode('ascii')
        c = ''.join((chr(ord(c) ^ ord(k)) for c, k in zip(data, cycle(key))))
        print('%s ^ %s = %s' % (data, key, c))


if __name__ == '__main__':
    mgame = myGame(25, 25)
    x = input('input a number: ')
    y = input('input a number: ')
    x1 = input('input a number: ')
    y1 = input('input a number: ')
    data = mgame.make_keys(int(x), int(y), int(x1), int(y1))
    mgame.checkdata(data)

My take from it was that the programs takes four inputs and uses them for creating a key based on a matrix and on the wordlist. If the resulting key is the correct one, the checkdata function is going to print out the flag.

Running the program with some random inputs confirmed my theory.

Now, how to figure out what are the correct inputs? The sources shows that the game is created with a 25x25 matrix (mgame = myGame(25, 25)) and since the inputs are used as indexes for the matrix, any input greater than 24 should be invalid.

A quick run of the program confirmed this theory as well

$ python keygame.py
input a number: 25
input a number: 25
input a number: 25
input a number: 25
Traceback (most recent call last):
  File "keygame.py", line 56, in <module>
    data = mgame.make_keys(int(x), int(y), int(x1), int(y1))
  File "keygame.py", line 34, in make_keys
    keyArray.append(self.matrix[args[0]][args[1]])
IndexError: list index out of range

So, I needed a way to test all the possible combinations of four inputs ranging from 0 to 24 and it turned out that python has a built-in way for doing just that. Enter itertools.products

import itertools
from keygame import myGame

inputs = [range(0, 25), range(0, 25), range(0, 25), range(0, 25)]
possibilities = [p for p in itertools.product(*inputs)]

mgame = myGame(25, 25)

for p in possibilities:
    data = mgame.make_keys(*p)
    result = mgame.checkdata(data)
    if result.startswith('TG20{'):
        print(result, p)

I commented out the print statements from keygame.py to reduce the noise, and then waited for my script to spit out the flag

'TG20{this flag should be on teh moon}'
Go Top