Plaid CTF 2013 (hypercomputer-1)

Решения

http://f00l.de/blog/?p=1803 (Rup0rt) Назад к списку заданий

Задание (binary)

image00.jpg

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

Условия задания:
"Для тех, кто не играл в plaidCTF 2012: "supercomputer" была задачей на реверс, в которой флаг считался, используя простейшую математику (например, умножение заменяем сложением в цикле). hypercomputer легче… если вы всё делаете правильно :Р Подключение: ssh 54.224.174.166"

Инструменты: виртуальная машина с х64 Linux на борту, IDA.

Что ж авторы говорят, что будет легко. Приступим. Подключаемся по ssh и скачиваем бинарный файл hypercomputer.
Первоначальное исследование нам говорит, что…

fes@Linux64:~/Plaid13/Binary/hyper1$ file hypercomputer
hypercomputer: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x0b7c8d904831417f536c59b59fdecfc738136536, stripped

… это 64-битный исполняемый файл Linux. Поэтому просто запускаем его:

fes@Linux64:~/Plaid13/Binary/hyper1$ ./hypercomputer
…Welcome to Hypercomputer!…
…This could take a very long time…
^C

То бишь "Добро пожаловать в Hypercomputer! Это займет очень много времени". Интересный момент здесь такой. Текст появляется не сразу, а с ощутимыми задержками и по несколько символов. А после вывода всего текста, программа, кажется, зависла, пришлось сбрасывать. Можно предположить, что остальное время занимает расчет и вывод флага. В прошлом году, код задачи supercomputer надо было исследовать и оптимизировать вручную, видимо, без отладки здесь не обойтись.

Первый помощник в таком случае - это strace. Работа strace заключается в перехвате и записи системных вызовов, выполненных процессом, а также полученных им сигналов. И strace в нашем случае подскажет, что такое долгое она там выполняет:

fes@Linux64:~/Plaid13/Binary/hyper1$ strace ./hypercomputer
……
nanosleep({0, 900100000}, NULL) = 0
nanosleep({0, 555555000}, NULL) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), …}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff6ce1cf000
write(1, "…W", 4…W) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "elco", 4elco) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "me t", 4me t) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "o Hy", 4o Hy) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "perc", 4perc) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "ompu", 4ompu) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "ter!", 4ter!) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "…\n", 4…) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "…T", 4…T) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "his ", 4his ) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "coul", 4coul) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "d ta", 4d ta) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "ke a", 4ke a) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, " ver", 4 ver) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "y lo", 4y lo) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "ng t", 4ng t) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "ime.", 4ime.) = 4
nanosleep({0, 555555000}, NULL) = 0
write(1, "..\n", 3..) = 3
write(1, "\0", 1) = 1
nanosleep({90, 10000000}, ^C <unfinished …>

Как видим, очень часто исполняется функция nanosleep(), которая блокирует поток на время, не меньше заданного. В прошлом году таким же образом тормозилось выполнение программы. Поэтому наш первый шаг - удалить все вызовы nanosleep() из исполняемого файла.

Загружаем наш файл в IDA. Выясняется, что nanosleep оказался вызов usleep:

.plt: 0400770                         ; "переходник" к int usleep(__useconds_t useconds)
.plt: 0400770                         _usleep         proc near   
.plt: 0400770 FF 25 FA 68 20 00                       jmp     cs:off_607070
.plt: 0400770                         _usleep         endp

Удаление всех вызовов usleep сделать просто: надо заменить "переходник"
FF 25 FA 68 20 00
на оператор возврата ret (0xC3) и nop (0x90)
C3 90 90 90 90 90

Но не спешите выходить из IDA. Если полистать код, то заметим, что очень часто встречается такая конструкция:

mov     eax, константа 
loop:
       sub     rax, 1
       jnz     loop

Заменяем цикл 48 83 E8 01 75 FA на 90 90 90 90 90 90

И такая:

mov     eax, 0
loop:
       add     rax, 1
       cmp     rax, rdx
       jnz     loop

Заменяем цикл 48 83 C0 01 48 39 D0 75 F7 на 90 90 90 90 90 90 90 90 90

Пишим небольшой скрипт замены:

list_z = [
"\xFF\x25\xFA\x68\x20\x00", "\xC3\x90\x90\x90\x90\x90",
"\x48\x83\xE8\x01\x75\xFA", "\x90"*6,
"\x48\x83\xC0\x01\x48\x39\xD0\x75\xF7", "\x90"*9,
]
 
binary = open("hypercomputer", "rb").read()
for i in range(0,6,2):
    binary = binary.replace(list_z[i],list_z[i+1])
 
open("hypercomputer_", "wb").write(binary)

Запускаем результат на исполнение и получаем флаг:

fes@Linux64:~/Plaid13/Binary/hyper1$ ./hypercomputer_
…Welcome to Hypercomputer!…
…This could take a very long time…
Y0uKn0wH0wT0Sup3rButCanY0uHyp3r

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