reverse, medium > Messaging Tool
Мы перехватили сообщение из одного мессенджера, говорят, что они зашифрованы. Попробуем получить сообщение?
Дан файл, формат .py
Огромная фрейдовская змея обвивает меч
Красиво. При гуглении “serprent PO0” находим либу, которая генерит эту красоту: https://github.com/kharland/Serpent
Закомментируем строку 182, чтобы не удалялся .pyc файл… uncompyle6 не может справиться с его декомпиляцией. По байт-коду можно восстановить самостоятельно часть кода:
import struct
from sys import argv as args
import serpent
mod_fix = 0xffffffff
def get_next_key(...):
...
def encrypt(...):
...
def decrypt(...):
...
encrypted_flag = [25 однобайтовых значений]
KEY = 'SAFE'
if type(encrypted_flag) == list:
print "Hello, you've received message, but it seems to be encrypted! Use our tool to decrypt it."
else:
print "".join(list(map( some_lambda(encrypted_flag)))
Хорошо, пора использовать энергию предков. Делаем грязный хак, чтобы получить возможность импортировать библиотеку в интерактивном режиме:
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}
>>> from ddd import *
('scriptType', None)
Hacking scriptType
Hello, you've received message, but it seems to be encrypted! Use our tool to decrypt it.
>>> globals()
{'mod_fix': 4294967295, 'get_next_key': <function get_next_key at 0x7fead2eef230>, 'struct': <module 'struct' from '/usr/lib/python2.7/struct.pyc'>, 'serpent': <module 'serpent' from 'serpent.py'>, '__builtins__': <module '__builtin__' (built-in)>, 'args': [''], 'encrypted_flag': [62, 125, 128, 30, 59, 77, 9, 125, 32, 40, 187, 190, 227, 207, 120, 195, 88, 210, 27, 20, 147, 218, 153, 83, 197], 'decrypt': <function decrypt at 0x7fead2eef320>, '__package__': None, 'sys': <module 'sys' (built-in)>, 'KEY': 'SAFE', '__name__': '__main__', 'encrypt': <function encrypt at 0x7fead2eef2a8>, '__doc__': None}
Нас интересуют названия “decrypt”, “encrypted_flag”. Заглянем в функу “decrypt”:
>>> import dis
>>> decrypt.__code__.co_argcount
2
>>> dis.dis(decrypt)
33 0 LOAD_GLOBAL 0 (struct)
3 LOAD_ATTR 1 (unpack)
6 LOAD_CONST 1 ('=L') # грустное место
9 LOAD_FAST 1 (bytes_key)
12 CALL_FUNCTION 2
15 LOAD_CONST 2 (0)
18 BINARY_SUBSCR
19 STORE_FAST 4 (key)
34 22 LOAD_GLOBAL 4 (bytearray)
25 LOAD_FAST 0 (data)
28 CALL_FUNCTION 1
31 STORE_FAST 0 (data)
35 34 SETUP_LOOP 71 (to 108)
37 LOAD_GLOBAL 6 (range)
40 LOAD_GLOBAL 7 (len)
43 LOAD_FAST 0 (data)
46 CALL_FUNCTION 1
49 CALL_FUNCTION 1
52 GET_ITER
>> 53 FOR_ITER 51 (to 107)
56 STORE_FAST 8 (i)
36 59 LOAD_FAST 0 (data)
62 LOAD_FAST 8 (i)
65 BINARY_SUBSCR
66 STORE_FAST 9 (old_byte)
37 69 LOAD_FAST 0 (data)
72 LOAD_FAST 8 (i)
75 DUP_TOPX 2
78 BINARY_SUBSCR
79 LOAD_FAST 4 (key)
82 LOAD_CONST 3 (255)
85 BINARY_AND
86 INPLACE_XOR
87 ROT_THREE
88 STORE_SUBSCR
38 89 LOAD_GLOBAL 10 (get_next_key)
92 LOAD_FAST 4 (key)
95 LOAD_FAST 9 (old_byte)
98 CALL_FUNCTION 2
101 STORE_FAST 4 (key)
104 JUMP_ABSOLUTE 53
>> 107 POP_BLOCK
40 >> 108 LOAD_GLOBAL 11 (bytes)
111 LOAD_FAST 0 (data)
114 CALL_FUNCTION 1
117 RETURN_VALUE
118 LOAD_CONST 0 (None)
121 RETURN_VALUE
Сразу отмечаем грустное место в начале. Ключ представлен четырехбайтовым значением, что заставляет нас немного погрустить.
Oh wait, у нас есть переменная KEY длиной в 4 байта. Пробуем запуститься…
>>> decrypt([62, 125, 128, 30, 59, 77, 9, 125, 32, 40, 187, 190, 227, 207, 120, 195, 88, 210, 27, 20, 147, 218, 153, 83,
197], "SAFE")
'mctf{pyc_d3c0mp1l4710n!!}'
reverse, medium > VM message
Рикардо сказал, что в нашем чатике он не может говорить про сверхсекретный правительственный проект, который он обнаружил на серверах ФБР, поэтому всю информацию он засунул в программу. Сказал разве, что там используется некая обфускация и простой шифр. Посмотрим?
64-битный эльф. Заглядываем в main:
Приятно видеть антиотладку.
Берем blob и key, засовываем в cyberchef
К слову, блоб длиной 22 символа.
Вместо того, чтобы его вытаскивать из хекс-представления или выписывать по байтам, можно воспользоваться функцией idapython - GetManyBytes:
Python>t = idc.GetManyBytes(0x202020, 22)
Python>import binascii
Python>binascii.hexlify(t)
f6bc6b117671c6713e7b4ae966022a29433aefc6b673
Но вот таск medium-уровнем назвать язык не поворачивается. Можно было поднять уровень таска, удалив имя функи или переименовав на, к слову, myowncryptofunction, чтобы немножечко поднять сложность.
reverse, easy > СейфSecurity
В этом великом PE-сейфе находится секрет SB о взломе cisco на время.
Нас встречает дельфин от Embarcadero RAD Studio весом в 12 мегабайт *ловит ностальгию по давним временам*
Вот такая гуишка встречает нас
12 секций, 12к функций. По инерции ищем пользовательские функции с конца…
Функция, которая нам нужна, называется _TMain_btn_EnterClick. Там видим веселое (напоминает стековое) заполнение трех строк.
Сила юникода
Особо глазастаые могли заметить, что при запуске с отладчиком после проверки в консоль падает дебаг-строчка
Это одна из трех строк. Так как таск простого уровня, это должно спасти человека от поиска функи.
Добрые разработчики жалеют нервы начинающим реверсерам
Длина строки подсказывает, что это md5. В коде находим подтверждение:
Радужная таблица, дай мне силу!
Флаг: mctf{21031956}
reverse, easy > Старый сидер
Странно, лет десять назад этот файл работал, пожалуй, начну с пролога...
Прекрасные 9 килобайт не менее прекрасного Mach-O под PowerPC.
Функцию заксорили на первые 12 байт (это три инструкции) её же пролога, их нужно восстановить. Звучит просто, а на деле это просто безумие и я не понимаю, как оценивалась сложность этих тасков.
Будет интересно почитать райтапы тех, кто этот таск решил.