R0 CREW

PlaidCTF 2014 :: hudak (reverse 250)

PlaidCTF 2014 :: hudak (reverse 250)

Пожалуй начну с самого простого (исходя из количества решивших).

Сэмпл представляет собой ELF 32 бита размером 9640 байт, что относительно не много.

Задание звучало так: «Can you reverse this program and find out what The Plague was working on?», что ни о чем не говорит кроме как о том, что предстоит реверсить.

Общая схема приложения следующая:

  1. запрашивается ввод пароля
.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
  1. получается пользовательский ввод;
.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
  1. проверяется длина (30 символов);
.text:08048541                 cmp     edx, 1Eh
.text:08048544                 jz      short len_ok
  1. отдается на обработку
.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]
  1. проверяется результат обработки и чеканья
.text:0804859A                 test    eax, eax
.text:0804859C                 jz      short msg_wrong

Сама проверка, дающая значение eax без затей

.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

Схема стандартная и в реалии лечится патчем перехода, но нам нужно другое, а именно значение пароля который пройдет преобразование и проверку и будет в итоге игровым флагом. Кода в обработке не так много, но код «мутный». Используется по ходу работы три структуры вида,

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

под каждый экземпляр которых алочится память, сами структуры завязываются в длинные списки, хранят указатели на функции этапов обработки, указатели на буферы и их части… и вся работа идет на основе этих списков. Желающие могут посмотреть приложенную базу от IDA (не знаю у всех ли откроется).

В процессе обработки строка, введенного пароля, размножается в необходимом количестве с циклическим сдвигом ее содержимого влево. Затем массив этих строк сортируется от меньшего значения к большему. И в порядке отсортированных строк из них в результирующую строку выбираются последние символы. И перед проверкой эта строка по всей длине ксорится значением 0х37. Вроде не сложно, но посмотрим на практике.

Код на python выполняющий аналогичные действия не впечатляет

#! /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

Результаты преобразования уже напрягают (не согласным с этим предлагаю не читать дальше, а самим обратить алго).

Массив сдвинутых строк.

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

Он же отсортированный

_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_ 

Строка, сформированная из последних символов ttrep_rbigmd_wy_uoashios_.

И результирующая строка CCERGhEU^PZSh@NhBXVD_^XDh.

Начнем отматывать действия назад и посмотрим на нашу MASTER строку без заключительного расксоривания. Короткий IDA скрипт нам в этом поможет.

auto ea, i, b;
ea = ScreenEA();
for(i=0; i<30; i=i+1)
    PatchByte(ea+i, Byte(ea+i)^0x37);

Теперь можем увидеть из чего должен состоять наш пароль 3…tvl3\xFFstttwrp__1mea4as4i1.. Сразу бросается в глаза присутствие в строке символа 0xFF. Во-первых, кто мог бы предполагать о его наличии, во-вторых, как его ввести с клавиатуры в консоли и, в-третьих, вся работа алгоритма (этого нет в повествовании выше) строится на наличии этого символа во входной строке и его местоположении. На этом завязано количество генерируемых структур, количество размножаемых строк, количество сдвигов и т.д. Понятно поначалу в моей строке тоже такого символа не было, и он был найден очень далеко в памяти от начала строки, что вовлекло всю эту память в процесс обработки, увеличило объем трассировки и не добавило понимания процессу. Поначалу стало грустно, алгоритм казался необратимым и была попытка строить пароль из представленных символов работая анаграмными методами, что ближе к криптографии. Ничего в итоге в голову не пришло учитывая написание на leet и я вернулся к обращению алгоритма. Повторное прокручивание короткой выходной строки (без повторяющихся символов) тем же алгоритмом с ручным допиливанием дало возможность получить исходную.

Исходная строка: 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й из сортированного массива):
[B]a[/B]dzyxcb - da
[B]b[/B]adzyxc - zb
[B]c[/B]badzyx - yc
[B]d[/B]zyxcba - xd
[B]x[/B]cbadzy - cx
[B]y[/B]xcbadz - by
[B]z[/B]yxcbad - az

Итого (az)-(zb)-(by)-(yc)-(cx)-(xd)-(da) => azbycxd

Проделаем аналогичные действия с нашей выходной строкой:

.._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 (38.5 KB):
hudak – сэмпл из задания
hudak.idb – база для IDA Pro v5.7
hudak_direct.py – реализация алгоритма модификации входной строки
hudak.py – построение выходной таблицы для анализа

That’s all Folks!

Вопрос от windows-guy - чем ты трейсил? Gdb или чем-нибудь более удобным?

Насколько я знаю использовалась IDA, но не факт.

IDA (windows host) + linux_server (KALI on vmware) с пропуском ввода и пропатчиванием значений прямо в буфер.

У меня юзание трейспоинтов через иду в такой связке крашило иду :confused:
upd: пресвятые угодники! Наконец-то получилось сделать так, чтобы ида показывала графы в дебаг моде. Удобно то как (и таск этот раскрутил быстро!)

Почитал райтап к этому таску от Dragon Sector. Оказалось что в таске было использовано преобразование Барроуза — Уилера используемое в техниках сжатия для преобразования исходных данных. И понятно, оно обратимо, без допиливания напильником.

Обратное преобразование из увиденного в вики на питоне будет выглядеть так:

#! /usr/bin/python
# -*- coding: utf-8 -*-

pass_ = '3.._tvl3\xFFstttwrp__1mea4as4i1_.'

array_str = []
for i in range(len(pass_)):
   array_str.append(pass_[i])
print array_str

for j in range(len(pass_)-1):
  sorted_array_str = []
  for i in range(len(pass_)):
     sorted_array_str.append(array_str[i])
  sorted_array_str.sort()

  for i in range(len(pass_)):
     array_str[i] += sorted_array_str[i][j]

print array_str[len(pass_)-1][2:]+array_str[len(pass_)-1][0]

Как говорится “Учите матчасть” и не изобретайте велосипед.