Ghost in the Shellcode CTF 2014 (Radioactive)

Решения

https://systemoverlord.com/blog/2014/01/19/ghost-in-the-shellcode-2014-radioactive/ (System Overlord)
http://tasteless.se/2014/01/gits-2014-radioactive-crypto-250/ (ccmndhd)
https://github.com/ctfs/write-ups/issues/9 (mathiasbynens)
Назад к списку заданий

Задание (crypto)

Question 6 - Radioactive
Points: 250
Find the key! File running at radioactive.2014.ghostintheshellcode.com:4324.

Подробное описание

"Radioactive" - это криптографический сервис (его исходный код доступен для скачивания), позволяющий выполнить любой код на Питоне (в исходнике обозначен через переменную command, далее именуемый как "команда") . Но только в том случае если код для выполнения имеет правильную "подпись" (в исходнике – переменная tag). Вот обработчик:
class RadioactiveHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        key = open("secret", "rb").read()
        #drop_privs()
        cipher = AES.new(key, AES.MODE_ECB)
 
        self.request.send("Waiting for command:\n")
        tag, command = self.request.recv(1024).strip().split(':')
        command = binascii.a2b_base64(command)
        pad = "\x00" * (16 - (len(command) % 16))
        command += pad
 
        blocks = [command[x:x+16] for x in xrange(0, len(command), 16)]
        cts = [str_to_bytes(cipher.encrypt(block)) for block in blocks]
 
        command = command[:-len(pad)]
 
        t = reduce(lambda x, y: [xx^yy for xx, yy in zip(x, y)], cts)
        t = ''.join([chr(x) for x in t]).encode('hex')
 
        match = True
        for i, j in zip(tag, t):
            if i != j:
                match = False
 
        del key
        del cipher
 
        if match:
            eval(compile(command, "script", "exec"))
        else:
            self.request.send("Checks failed!\n")
 
        return

Таким образом, мы передаем сервису параметр "подпись:команда в base64" (разделитель – символ двоеточия). Команда - строка на языке Питон, которая компилируется compile(command, "script", "exec") и выполняется через eval().Чтобы получить ответ от сервиса, нам надо оформить нашу команду в виде "self.request.send(здесь код, выполняемый на стороне сервиса)".
А как рассчитывается "подпись"? Команда разбивается на 16-байтовые блоки. Затем их шифруем AES (ключ мы не знаем) в режиме ECB (из двух одинаковых открытых текстов получим два одинаковых криптотекста). Затем зашифрованные блоки XOR’им вместе, тем самым, получая "подпись".

В файле, доступном для скачивания, помимо самого сервиса, есть примеры команд для calc, echo, ls, stat. Приведем код команды "ls":

import os
# this script gets a directory listing of the current directory!
files = os.listdir(".")
 
for f in files:
    st = os.stat(f)
    m = bin(st.st_mode)[-9:]
 
    mode =  "r" if m[0] == '1' else '-'
    mode += "w" if m[1] == '1' else '-'
    mode += "x" if m[2] == '1' else '-'
 
    mode += "r" if m[3] == '1' else '-'
    mode += "w" if m[4] == '1' else '-'
    mode += "x" if m[5] == '1' else '-'
 
    mode += "r" if m[6] == '1' else '-'
    mode += "w" if m[7] == '1' else '-'
    mode += "x" if m[8] == '1' else '-'
 
    output = "%s\t%s\t%s" % (mode, st.st_size, f, )
    self.request.send(output + "\n")

Ошибка в сервисе кроится в проверке условия, а именно в особенности работы Питон-функции zip().

match = True
    for i, j in zip(tag, t):
        if i != j:
            match = False

В этом условии сравнивается полученная и рассчитанная "подпись". Функция zip() берет два итерационных объекта и возвращает пары, состоящие из элементов этих объектов. Нюанс в том, что, если один из этих объектов не имеет элементов, то работа функции завершается. В нашем случае, передав пустую "подпись", цикл for i, j in zip(tag, t) завершится, даже не начавшись и выражение match = False точно никогда не выполнится.

Решение: передать параметр вида ":команда в base64" (без "подписи").
Кстати, наш флаг находится в файле "key". Посылаем команду "прочитать файл".

$ base64 < < < "self.request.send(open('key').read())"
c2VsZi5yZXF1ZXN0LnNlbmQob3Blbigna2V5JykucmVhZCgpKQo=

$ echo ':c2VsZi5yZXF1ZXN0LnNlbmQob3Blbigna2V5JykucmVhZCgpKQo=' | nc radioactive.2014.ghostintheshellcode.com 4324

Waiting for command:
Welcom3ToTheNewAgeItsARevolutionISuppose

Пока не указано иное, содержимое этой страницы распространяется по лицензии Creative Commons Attribution-ShareAlike 3.0 License