PlaidCTF 2014 :: hudak (reverse 250)
Пожалуй начну с самого простого (исходя из количества решивших).
Сэмпл представляет собой ELF 32 бита размером 9640 байт, что относительно не много.
Задание звучало так: «Can you reverse this program and find out what The Plague was working on?», что ни о чем не говорит кроме как о том, что предстоит реверсить.
Общая схема приложения следующая:
1) запрашивается ввод пароля
2) получается пользовательский ввод;Code:.text:080484DB mov dword ptr [esp+4], offset rqs ; "Enter the password.\n" .text:080484E3 mov dword ptr [esp], 1 .text:080484EA call ___printf_chk
3) проверяется длина (30 символов);Code:.text:080484EF mov dword ptr [esp+0Ch], 0Ah .text:080484F7 mov dword ptr [esp+8], 50h .text:080484FF mov [esp+4], ebx .text:08048503 mov dword ptr [esp], 0 .text:0804850A call read_entry
4) отдается на обработкуCode:.text:08048541 cmp edx, 1Eh .text:08048544 jz short len_ok
5) проверяется результат обработки и чеканьяCode:.text:08048566 mov dword ptr [esp], offset FUN_1 .text:0804856D call XX_constructor .text:08048572 mov dword ptr [esp], offset FUN_1_1 .text:08048579 mov esi, eax .text:0804857B call XX_constructor .text:08048580 mov [eax+XX.buffer_ptr], ebx .text:08048583 mov [eax+XX.busy_fl], 1 .text:08048589 mov [esp], eax .text:0804858C call XX_get_mem4 .text:08048591 mov [esi+XX.link_YY_ZZ], eax .text:08048594 mov [esp], esi .text:08048597 call [esi+XX.fun_ptr]
Сама проверка, дающая значение eax без затейCode:.text:0804859A test eax, eax .text:0804859C jz short msg_wrong
Схема стандартная и в реалии лечится патчем перехода, но нам нужно другое, а именно значение пароля который пройдет преобразование и проверку и будет в итоге игровым флагом. Кода в обработке не так много, но код «мутный». Используется по ходу работы три структуры вида,Code:.text:08048A3E mov dword ptr [esp+8], 1Fh ; n .text:08048A46 mov dword ptr [esp], offset MASTER ; s1 .text:08048A4D mov [esp+4], eax ; s2 .text:08048A51 call _memcmp .text:08048A56 test eax, eax .text:08048A58 setz al
под каждый экземпляр которых алочится память, сами структуры завязываются в длинные списки, хранят указатели на функции этапов обработки, указатели на буферы и их части… и вся работа идет на основе этих списков. Желающие могут посмотреть приложенную базу от IDA (не знаю у всех ли откроется).Code:00000000 XX struc ; (sizeof=0x10) 00000000 busy_fl dd ? 00000004 buffer_ptr dd ? ; offset 00000008 fun_ptr dd ? ; offset 0000000C link_YY_ZZ dd ? ; offset 00000010 XX ends 00000010 00000000 ; --------------------------------------------------------------------------- 00000000 00000000 YY struc ; (sizeof=0x8) 00000000 link_XX1 dd ? ; offset 00000004 link_XX2 dd ? ; offset 00000008 YY ends 00000008 00000000 ; --------------------------------------------------------------------------- 00000000 00000000 ZZ struc ; (sizeof=0xC) 00000000 link_XX1 dd ? ; offset 00000004 link_XX2 dd ? ; offset 00000008 link_XX3 dd ? ; offset 0000000C ZZ ends
В процессе обработки строка, введенного пароля, размножается в необходимом количестве с циклическим сдвигом ее содержимого влево. Затем массив этих строк сортируется от меньшего значения к большему. И в порядке отсортированных строк из них в результирующую строку выбираются последние символы. И перед проверкой эта строка по всей длине ксорится значением 0х37. Вроде не сложно, но посмотрим на практике.
Код на python выполняющий аналогичные действия не впечатляет
Результаты преобразования уже напрягают (не согласным с этим предлагаю не читать дальше, а самим обратить алго).Code:#! /usr/bin/python # -*- coding: utf-8 -*- pass_ = 'it_might_be_your_password' array_str = [] for i in range(len(pass_)): array_str.append(pass_[i:]+pass_[:i]) array_str.sort() _out_hex = '' for i in range(len(pass_)): c = ord(array_str[i][-1]) ^ 0x37 _out_hex += '%02X '%c print _out_hex
Массив сдвинутых строк.
Он же отсортированныйCode:it_might_be_your_password t_might_be_your_passwordi _might_be_your_passwordit might_be_your_passwordit_ ight_be_your_passwordit_m ght_be_your_passwordit_mi ht_be_your_passwordit_mig t_be_your_passwordit_migh _be_your_passwordit_might be_your_passwordit_might_ e_your_passwordit_might_b _your_passwordit_might_be your_passwordit_might_be_ our_passwordit_might_be_y ur_passwordit_might_be_yo r_passwordit_might_be_you _passwordit_might_be_your passwordit_might_be_your_ asswordit_might_be_your_p sswordit_might_be_your_pa swordit_might_be_your_pas wordit_might_be_your_pass ordit_might_be_your_passw rdit_might_be_your_passwo dit_might_be_your_passwor
Строка, сформированная из последних символов ttrep_rbigmd_wy_uoashios_.Code:_be_your_passwordit_might _might_be_your_passwordit _passwordit_might_be_your _your_passwordit_might_be asswordit_might_be_your_p be_your_passwordit_might_ dit_might_be_your_passwor e_your_passwordit_might_b ght_be_your_passwordit_mi ht_be_your_passwordit_mig ight_be_your_passwordit_m it_might_be_your_password might_be_your_passwordit_ ordit_might_be_your_passw our_passwordit_might_be_y passwordit_might_be_your_ r_passwordit_might_be_you rdit_might_be_your_passwo sswordit_might_be_your_pa swordit_might_be_your_pas t_be_your_passwordit_migh t_might_be_your_passwordi ur_passwordit_might_be_yo wordit_might_be_your_pass your_passwordit_might_be_
И результирующая строка CCERGhEU^PZSh@NhBXVD_^XDh.
Начнем отматывать действия назад и посмотрим на нашу MASTER строку без заключительного расксоривания. Короткий IDA скрипт нам в этом поможет.
Теперь можем увидеть из чего должен состоять наш пароль 3.._tvl3\xFFstttwrp__1mea4as4i1_.. Сразу бросается в глаза присутствие в строке символа 0xFF. Во-первых, кто мог бы предполагать о его наличии, во-вторых, как его ввести с клавиатуры в консоли и, в-третьих, вся работа алгоритма (этого нет в повествовании выше) строится на наличии этого символа во входной строке и его местоположении. На этом завязано количество генерируемых структур, количество размножаемых строк, количество сдвигов и т.д. Понятно поначалу в моей строке тоже такого символа не было, и он был найден очень далеко в памяти от начала строки, что вовлекло всю эту память в процесс обработки, увеличило объем трассировки и не добавило понимания процессу. Поначалу стало грустно, алгоритм казался необратимым и была попытка строить пароль из представленных символов работая анаграмными методами, что ближе к криптографии. Ничего в итоге в голову не пришло учитывая написание на leet и я вернулся к обращению алгоритма. Повторное прокручивание короткой выходной строки (без повторяющихся символов) тем же алгоритмом с ручным допиливанием дало возможность получить исходную.Code:auto ea, i, b; ea = ScreenEA(); for(i=0; i<30; i=i+1) PatchByte(ea+i, Byte(ea+i)^0x37);
Проделаем аналогичные действия с нашей выходной строкой:Code:Исходная строка: azbycxd Сдвиг исходной строки: azbycxd zbycxda bycxdaz ycxdazb cxdazby xdazbyc dazbycx Сортировка строк: azbycxd bycxdaz cxdazby dazbycx xdazbyc ycxdazb zbycxda Выходная строка: dzyxcba Сдвиг выходной строки: dzyxcba zyxcbad yxcbadz xcbadzy cbadzyx badzyxc adzyxcb Сортировка строк (справа пара символов – 1й из выходной строки, 2й из сортированного массива): adzyxcb - da badzyxc - zb cbadzyx - yc dzyxcba - xd xcbadzy - cx yxcbadz - by zyxcbad - az Итого (az)-(zb)-(by)-(yc)-(cx)-(xd)-(da) => azbycxd
Ввиду того, что есть повторяющиеся символы, то это будет не также просто.Code:.._tvl3\xffstttwrp__1mea4as4i1_.3 - 3. .3.._tvl3\xffstttwrp__1mea4as4i1_ - .. ._tvl3\xffstttwrp__1mea4as4i1_.3. - .. 1_.3.._tvl3\xffstttwrp__1mea4as4i - _1 1mea4as4i1_.3.._tvl3\xffstttwrp__ - t1 3.._tvl3\xffstttwrp__1mea4as4i1_. - v3 3\xffstttwrp__1mea4as4i1_.3.._tvl - l3 4as4i1_.3.._tvl3\xffstttwrp__1mea - 34 4i1_.3.._tvl3\xffstttwrp__1mea4as - \xff4 _.3.._tvl3\xffstttwrp__1mea4as4i1 - s_ _1mea4as4i1_.3.._tvl3\xffstttwrp_ - t_ __1mea4as4i1_.3.._tvl3\xffstttwrp - t_ _tvl3\xffstttwrp__1mea4as4i1_.3.. - t_ a4as4i1_.3.._tvl3\xffstttwrp__1me - wa as4i1_.3.._tvl3\xffstttwrp__1mea4 - ra ea4as4i1_.3.._tvl3\xffstttwrp__1m - pe i1_.3.._tvl3\xffstttwrp__1mea4as4 - _i l3\xffstttwrp__1mea4as4i1_.3.._tv - _l mea4as4i1_.3.._tvl3\xffstttwrp__1 - 1m p__1mea4as4i1_.3.._tvl3\xffstttwr - mp rp__1mea4as4i1_.3.._tvl3\xffstttw - er s4i1_.3.._tvl3\xffstttwrp__1mea4a - as stttwrp__1mea4as4i1_.3.._tvl3\xff - 4s tttwrp__1mea4as4i1_.3.._tvl3\xffs - at ttwrp__1mea4as4i1_.3.._tvl3\xffst - st tvl3\xffstttwrp__1mea4as4i1_.3.._ - 4t twrp__1mea4as4i1_.3.._tvl3\xffstt - it vl3\xffstttwrp__1mea4as4i1_.3.._t - 1v wrp__1mea4as4i1_.3.._tvl3\xffsttt - _w \xffstttwrp__1mea4as4i1_.3.._tvl3 - .\xff
Посчитаем символы. ('e','i','l','m','p','r','v','w','\xff') – по одному; ('1','3','4','a','s') – по два; ('.') – три; ('_','t') – четыре.
Одиночные символы дают нам однозначную последовательность в парах 'er','it','l3', 'mp', 'pe', 'ra','v3','wa', '\xff4', и уже длинный кусок кода 'mpera'. Для остальных можно присовокупить предыдущий символ '_it','_l3','1v3','_wa','.\xff4','1mpera' одновременно удалив строки со всеми рассмотренными комбинациями из списка. Ну и так далее, что в итоге нам даст строку 4t_l34st_it_was_1mperat1v3...\xff , которая и есть флаг за исключением '\xff'.
Содержание архива hudak.RAR:
hudak – сэмпл из задания
hudak.idb – база для IDA Pro v5.7
hudak_direct.py – реализация алгоритма модификации входной строки
hudak.py – построение выходной таблицы для анализа
That's all Folks!



Reply With Quote
Thanks
