Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
12
Добавлен:
20.04.2024
Размер:
19.47 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

ВЗЛОМm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

АрсенийРеутов(http://raz0r.name,areutov@ptsecurity.ru),to

 

 

 

 

 

 

 

 

 

 

m

ТимурЮнусов(tyunusov@ptsecurity.ru),ДмитрийНагибин(dnagibin@ptsecurity.ru)PositiveTechnologiesw Click

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

НОВЫЕСПОСОБЫ АТАКНАГЕНЕРАТОРЫ ПСЕВДОСЛУЧАЙНЫХ ЧИСЕЛВPHP

ПРЕДСКАЗАНИЯ

СБЫВАЮТСЯ

Доклад

2012

WARNING

Всяинформация предоставлена исключительно вознакомительных целях.

Ниредакция,ниавторненесутответственностизалюбой возможныйвред, причиненныйматериаламиданной статьи.

Если говорить о языках с небезопасным подходом к генерации псевдослучайных значений, то в первую очередь на ум приходит PHP. Первый дубль эпопеи PHP с рандомными числами имел место более пяти лет назад, однако с тех пор мало что изменилось.

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

ВВЕДЕНИЕ

Проблемы веб-приложений на PHP, связанные с генерацией псевдослучайных чисел, были известны достаточно давно. Еще в 2008 году Стефан Эссер (Stefan Esser) указал на недостатки ручной инициализации генератора случайных чисел и описал общий алгоритм атак через keep-alive HTTP-запросы. Если на тот момент все уязвимости, связанные с предугадыванием различного рода

токенов, в том числе для восстановления пароля, можно было списать на сами веб-приложения (из-за неправильного использования возможностей PHP и утечек состояния ГПСЧ), то со временем стали выявляться недостатки самого интерпретатора.

В 2010 году Милен Рангелов (Milen Rangelov) представил PoC для создания rainbow-таблиц, позволяющих проводить поиск сида по полному диапазону из всех (2^32) возможных значений. Иначе говоря, имея код, который, например, генерирует случайным образом пароль, стало возможным заранее сгенерировать таблицы и по ним достаточно быстро искать сид под конкретное веб-приложение на PHP. Спустя полгода на конференции Black Hat Сэми Камкар (Samy Kamkar) впервые указал на проблемы PHP с генерацией идентификаторов сессий. Летом этого года на той же конференции греческие эксперты в области криптографии Джордж Аргирос (George Argyros) и Агелос Кияиас (Aggelos Kiayias) выступили с работой, в которой была еще глубже проанализирована вся подноготная генерации псевдослучайных чисел в PHP и представлены абсолютно новые методы и техники атак на веб-приложения. В частности, шла речь о брутфорсе PHPSESSID с целью получения информации о состояниях источников энтропии ГПСЧ в PHP, однако практическая реализация отсутствовала. Мы же решили проверить всю теорию, провести собственные исследования и создать необходимые инструменты. Новый взгляд на старые вещи позволил выявить уязвимости в таких продуктах, как OpenCart, DataLife Engine, UMI.CMS последних версий.

СОЗДАНИЕ НОВЫХ ПРОЦЕССОВ

Одна из новых техник такова: атакующий создает свежие процессы с вновь инициализированным состоянием ГПСЧ, что позволяет эффективно искать сиды. Прежде чем перейти к разбору нового способа, необходимо разобраться с особенностями взаимодействия PHP и Apache.

Веб-сервер может использовать один из модулей параллельной обработки (multi-processing module, MPM): обычно это либо mpmprefork, либо mpm-worker. Что касается prefork-модуля, смысл его работы в том, что заранее создается некоторое количество процессов веб-сервера и каждое соединение с веб-сервером обрабатывается одним из этих процессов. В режиме mpm-worker

070

ХАКЕР 01 /168/ 2013

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Предсказания сбываютсяw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

БрутфорсPHPSESSIDнаGPUAmazon’а

Apache обрабатывает запросы не в отдельных процессах, а в потоках (threads) внутри одного процесса. Забегая вперед, необходимо сказать: в *nix-системах идентификатор потока может иметь 2^32 значений, что делает перебор PHPSESSID невозможным. Однако в большинстве случаев атакующий имеет дело с классической связкой Apache mpm-prefork + mod_php. При такой конфигурации keep-alive запросы будут обрабатываться одним и тем же процессом, то есть с общим состоянием ГПСЧ. В режиме PHP-CGI для каждого запроса создается новый процесс интерпретатора с вновь инициализированными состояниями генераторов.

В упомянутой работе Стефана Эссера для получения новых процессов со свежими сидами предлагался радикальный метод, а именно крэшить веб-сервер с помощью многочисленных вложенных GET, POST, Cookie-параметров. Джордж Аргирос и Агелос Кияиас предложили более гуманный способ. Суть его в том, что атакующий создает большое количество keep-alive соединений, пытаясь загрузить работой все запущенные процессы веб-сервера. Задача атакующего — отправить целевой запрос, после того как у Apache закончатся свободные процессы и он начнет создавать новые.

СИНХРОНИЗАЦИЯ ВРЕМЕНИ

Одним из источников энтропии для генерации PHPSESSID является значение микросекунд. Как известно, веб-сервер перед отправкой ответа добавляет заголовок Date, через который можно узнать время завершения выполнения запроса вплоть до секунд. Несмотря на то что микросекунды неизвестны атакующему, следующая техника может помочь снизить диапазон их возможных значений:

1.Ждемобнулениямикросекунднаклиенте(msec=0),послечего делаемзадержкуdelta(вначалеdelta=0).

2.Отправляемпервыйзапросиждемответа,фиксируемвремя

насервереспомощьюзаголовкаDate(T1)имикросекундынаклиенте(msec=m1).

3.Сразужеотправляемвторойзапросиждемответа,фиксируем времянасервере(T2)имикросекундынаклиенте(msec=m2).

УдачнаяатаканаDataLife

БрутфорссидовLCGпосидуMersenneTwister

4.Еслисекундынесменились(T2-T1=0),тодобавляемкdeltaзна- чение<(m2-m1)/2(чемменьшешагdelta,темлучше)ивозвраща- емсякшагу1.

5.Еслисекундыприодинаковыхdeltaстабильноменяются(T2-T1= 1),томыдобилисьситуации,когдамеждузапросамимикросекундыобнуляются.

По описанному выше алгоритму микросекунды второго запроса находятся в интервале [0; (m2 - m1)/2].

Так как веб-сервер добавляет заголовок Date непосредственно после обработки запроса, задача атакующего — максимально снизить время обработки первого запроса. Для этого запрашивается несуществующая страница, в результате чего время начала обработки запроса практически совпадает со временем в заголовке Date. Второй же запрос является целевым — в нем и должна создаваться сессия в свежем процессе.

Очевидно, что чем больше времени проходит с момента отправки запроса до момента получения заголовков ответа, тем больше интервал микросекунд.

ПАРНЫЕ ЗАПРОСЫ

Суть техники заключается в последовательной отправке двух запросов с максимально низкой задержкой между ними. Предполагается, что у атакующего есть возможность узнать микросекунды из первого запроса (например, с помощью восстановления пароля аккаунта атакующего в целевой системе). Делая первый запрос и определяя значение микросекунд в момент его обработки, мы можем уменьшить интервал значений микросекунд второго запроса.

Владимир Воронцов (@d0znpp) предложил отправлять три запроса, где микросекунды первого и последнего известны атакующему. В этом случае диапазон микросекунд второго запроса будет ограничен известными значениями.

БРУТФОРС PHPSESSID

В своей работе Сэми Камкар рассматривал саму возможность брутфорса PHPSESSID. Исследование греков показало, что процесс брутфорса можно оптимизировать, а полученную информацию использовать для предугадывания сидов ГПСЧ в PHP.

Обратимся к коду генерации PHPSESSID:

spprintf(&buf, 0, "%.15s%ld%ld%0.8F", remote_addr ? remote_addr : "", tv.tv_sec, (long int)tv.tv_usec, php_combined_lcg(TSRMLS_C) * 10);

Пример исходной строки: 127.0.0.11351346648192088.00206033. Если разобрать ее на составные части, можно выделить следующие:

127.0.0.1—IPклиента;

135134664—timestamp;

819208—микросекунды(обозначимкакm1);

8.00206033—выводгенератораLCG(LinearCongruential Generator,линейныйконгруэнтныйгенератор).

ХАКЕР 01 /168/ 2013

071

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

ВЗЛОМm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

При вызове php_combined_lcg в свежем процессе PHP производит инициализацию генератора LCG:

LCG(s1) = tv.tv_sec ^ (tv.tv_usec<<11);

LCG(s2) = (long) getpid();

/* Add entropy to s2 by calling gettimeofday() again */ LCG(s2) ^= (tv.tv_usec<<11);

При генерации сидов s1 и s2 участвует тот же timestamp, идентификатор текущего процесса (2^15 возможных значений), а также два новых замера микросекунд (обозначим как m2 и m3).

IP и timestamp известны атакующему, таким образом, остаются следующие значения:

микросекундыm1(10^6значений);

разницамеждувторымипервымзамерамивремени(m2-m1), причемнабольшинствесистемонанепревышаетчетырехмикросекунд;

разницамеждутретьимивторымзамерамивремени(m3-m2), обычнонепревышаеттрехмикросекунд;

идентификаторпроцесса(32768значений).

PHPSESSID может представлять собой MD5либо SHA-1-хеш, но в большинстве случаев это первый вариант. Также представление хеша может зависеть от директивы конфигурации PHP session. hash_bits_per_character, которая особым образом преобразует идентификатор. Однако восстановить оригинальный хеш не составляет труда, так как все операции обратимы.

Необходимо заметить, что в PHP 5.4+ при генерации сессий по умолчанию используются внешние источники энтропии, в частности /dev/urandom. Но к счастью, живые веб-серверы на данный момент довольно редко используют новую ветку PHP.

Существуют способы, которые могут помочь при бруте PHPSESSID. Например, если на целевом веб-сервере установлен mod_status, то при обращении по /server-status можно получить идентификаторы запущенных процессов Apache. А если атакующему удалось обнаружить phpinfo, то из переменной окружения UNIQUE_ID, которую устанавливает модуль Apache mod_unique_id для идентификации запроса, можно извлечь не только PID,

но и значение микросекунд. Владимир Воронцов создал онлайндекодер UNIQUE_ID, доступный по адресу: https://dev.onsec.ru/ rands.

Очевидно, что перебор PHPSESSID требует специального инструмента, так как стандартные средства в данном случае не помогут. Поэтому мы разработали собственное решение под

названием PHPSESSID Bruteforcer, которое показало на практике впечатляющие результаты.

Главное достоинство инструмента — высокая скорость, которая достигается благодаря переносу расчетов на GPU. На одном GPUинстансе с CUDA сервиса Amazon нам удалось достичь скорости

в 1,2 миллиарда хешей в секунду, что позволяет перебрать весь диапазон значений за 7,5 минуты. Помимо этого, в программе присутствует поддержка распределенных вычислений с умным балансировщиком нагрузки. Объединив несколько машин с GPU, можно добиться невероятно высокой скорости. Более подробное описание инструмента ищи в рубрике X-Tools, а сам дистрибутив — на DVD.

В результате успешного брутфорса PHPSESSID атакующий получает значения, позволяющие выяснить сиды s1 и s2 генератора LCG, таким образом у него появляется возможность предугадывать все последующие значения. Но самое главное — это то, что становятся известными все данные для вычисления сида, использующегося для инициализации Mersenne Twister:

#ifdef PHP_WIN32

#define GENERATE_SEED() (((long) (time(0) *

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

БрутфорсPHPSESSIDнаCPU

GetCurrentProcessId())) ^ ((long) (1000000.0 *

php_combined_lcg(TSRMLS_C))))

#else

#define GENERATE_SEED() (((long) (time(0) *

getpid())) ^ ((long) (1000000.0 *

php_combined_lcg(TSRMLS_C))))

#endif

Кроме того, становятся предсказуемыми выводы таких функций, как rand(), shuffle(), array_rand() и многих других.

ЛОМАЕМ UMI.CMS

Прекрасная площадка для проведения атаки на PHPSESSID — UMI. CMS версии 2.8.5.3 (на данный момент уязвимость устранена).

За генерацию токена для сброса пароля отвечает следующая функция:

function getRandomPassword ($length = 12) {

$avLetters = "$#@^&!1234567890qwertyuio";

$size = strlen($avLetters);

$npass = "";

for($i = 0; $i < $length; $i++) {

$c = rand(0, $size - 1);

$npass .= $avLetters[$c];

}

return $npass;

}

Сброс пароля можно вызвать сразу после генерации новой сессии, отправив запрос:

POST http://host/umi/users/forget_do/

...

choose_forget=on&forget_login=admin

Для этого необходимо знать лишь логин администратора. Получив PHPSESSID в свежем процессе, выясняем сиды LCG s1

и s2, а также идентификатор процесса. В случае успешного перебора воспроизводим операции, совершенные на сервере при генерации токена сброса пароля:

инициализируемLCGсидамиs1иs2;

выполняемнескольковызововLCG(количествоможетзависеть отверсииинтерпретатора,ночащевсегоэточислоравнотрем);

производимвызовGENERATE_SEED,указываяизвестныйатакующемуtimestamp,идентификаторпроцессаичетвертыйвызов LCG,инициализируемMersenneTwisterполученнымсидом;

вызываемфункциюgetRandomPassword(),котораявернетнам

072

ХАКЕР 01 /168/ 2013

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

UMI.CMSсдалсябезбоя

токен,ипереходимпоадресуhttp://host/umi/users/restore/ md5(token).

Если все этапы алгоритма сработали правильно, то для учетной записи администратора будет установлен новый, известный нам пароль.

АТАКУЕМ OPENCART

Примечательной особенностью механизма инициализации генераторов псевдослучайных чисел для функций rand() и mt_rand() в PHP является то, что отвечающий за данную операцию макрос

GENERATE_SEED использует в качестве источника энтропии вывод значения генератора LCG.

Можно ли считать использование LCG в данном случае безопасным? Чтобы ответить на этот вопрос, представим веб-приложение, использующее сразу два ГПСЧ: LCG и Mersenne Twister. Если атакующему удастся получить сид хотя бы одного из генераторов, то ему не составит труда подобрать сид другого. Примером такого вебприложения может служить движок интернет-магазина OpenCart версии 1.5.4.1 (последняя на момент написания статьи). В нем присутствует следующий код, задача которого — сгенерировать безопасный токен для восстановления пароля администратора:

$code = sha1(uniqid(mt_rand(), true));

А в предыдущих версиях использовался совсем смешной способ:

$code = md5(mt_rand());

Итак, в данном случае мы имеем три источника энтропии:

mt_rand—числос2^32возможнымизначениями;

uniqid—нечтоиное,какизвестныйатакующемуtimestampчерез заголовокDateиmicrotime(10^6возможныхзначений),представ- ленныевhex-формате;

lcg_value—выводLCGпривызовеuniqidсовторымаргументом.

Витоге получается строка следующего вида: 924968175087b4 c6968487.41222311. Казалось бы, SHA-1-хеш от такой строки точно не сбрутить, однако OpenCart делает непозволительный подарок — в CSRF-токене происходит утечка состояния Mersenne Twister:

$this->session->data['token'] = md5(mt_rand());

Очевидно, что сбрутить MD5 от числа 2^32 можно довольно быстро. Получив число, мы можем вычислить сид, вернее сиды, так как присутствуют коллизии. Для получения сидов на данный момент существуют следующие утилиты:

php_mt_seedотSolarDesigner—используетCPU,ноприпомощи SSE-инструкцийпокрываетвесьдиапазонменеечемзаодну минуту(goo.gl/PtkFG);

pyphp_rand_oclотGifts—поддерживаеткакCPU,такиGPU, справляетсяза~70и~20секундсоответственно(goo.gl/rfbMJ);

mt_randбрутеротont—используетCUDA,помимопрочего,поз- воляетнаходитьсидпринеполномвыводеслучайныхзначений (goo.gl/UETm6);

SnowflakeотДжорджаАргироса—представляетсобойцелый фреймворкдлясозданияэксплойтов,реализующихатакинаслучайныечисла(goo.gl/XuVB2).

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Предсказания сбываютсяw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Итак, алгоритм атаки включает следующие шаги:

1.Атакующий,отправляябольшоеколичествоkeep-aliveзапро- сов,заставляетвеб-серверсоздатьновыепроцессысосвежими сидами.

2.Отправляютсятриkeep-aliveзапросаводномсоединении:пер- вый—дляполученияMD5-токена,второй—длясбросапароля атакующегои,наконец,третий—длясбросапароляадминистра- тора.

3.Расшифровываетсятокен,почислупроизводитсяпоисксида.

4.ИмеясидMersenneTwisterинекотороеколичествоколлизий, атакующийбрутитдвасидаLCG—дляэтоготребуетсяосуще- ствитьперебордиапазонаидентификаторовпроцесса(1024– 32768),microtime(10^6значений),атакжедельтымеждупервым ивторымзамеромвремени.Какужебылосказаноранее,вбольшинствеслучаевразницамеждузамераминепревышаеттрех микросекунд,поэтомусмыслаотподобногодействияпрактическинет.

5.ПолучивнекотороеколичествовозможныхсидовLCG(обычно небольше100),атакующийпроводитещеодинбрутфорс—наэтот разSHA-1-токенадлявосстановлениясобственногопароля.Про- блемсбрутомздесьнет,даженесмотрянато,чтоизвестнылишь первыедесятьсимволовхеша,—длятакихслучаевестьпро- граммаPasswordsPro,котораясправляетсядажеснеполнымихе- шами.Цельданногоперебора—получитьзначениемикросекунд, атакжеузнатьистинныесидыMTиLCG.

6.Таккакзапросыбылиотправленыводномсоединенииодин задругим,разницавмикросекундахмеждузапросомнавосста-

новлениепароляатакующегоиадминистраторабудетминимальной.Остаетсялишьподобратьискомоезначениеmicrotime,имея точныесидыMTиLCG.

При атаке на реальных системах может возникнуть несколько проблем: трудности с созданием новых процессов для получения свежего сида MT, большая задержка между обработкой запросов на восстановление пароля, а также смещение LCG на разных версиях PHP. Что касается последнего — суть в том, что PHP делает вызовы php_combined_lcg() для своих внутренних нужд, например для генерации того же PHPSESSID, поэтому перед осуществлением атаки желательно узнать версию PHP и локально определить, какой вызов LCG используется для генерации кода для восстановления пароля атакующего и какой — администратора. Например, для PHP 5.3.17 это пятый и восьмой вызовы соответственно.

Для реализации атаки был создан брутфорсер сидов LCG на CUDA, который позволяет осуществить перебор полного диапазона значений меньше чем за полминуты. Программа есть на DVD.

ЗАКЛЮЧЕНИЕ

В свете новых техник атак на ГПСЧ в PHP снова и снова настораживает реакция разработчиков интерпретатора. В результате нашего продолжительного общения с ними все, чего нам удалось добиться, — это обещание добавить в документацию предупреждения о небезопасности использования функции mt_rand() для криптографических целей. Однако спустя несколько месяцев в документации так ничего и не появилось. Остается лишь порекомендовать разработчикам веб-приложений на PHP не полагаться на документацию, а использовать правильные методы, например функцию от греческих экспертов (goo.gl/omJvn). Надежной энтропии! z

WWW

РаботаСтефанаЭссера2008года:goo.gl/wkdxA;

rainbow-таблицыдлябрутасидовнаPHP:goo.gl/zZwN5;

исследованиеСэмиКамкара:goo.gl/0Gzfx;

анализгенераторовпсевдослучайныхчиселотДжорджаАргиросаиАгелоса Кияиса:goo.gl/QQhFK.

ХАКЕР 01 /168/ 2013

073

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

ВЗЛОМm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

SanjarSatsura(satsura@r00tw0rm.com)w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Изучаемвозможности фреймворкаMetasm

Метаморфозы

Манипуляция с машинным кодом дает большие возможности как для программистов, так и для хакеров. Единственное, что является

камнем преткновения для тех и других, — это динамический разбор структур, анализ и систематизация кода для эффективной работы с ним. Автоматизацию этой рутинной работы мы сегодня и рассмотрим.

WARNING

Всяинформация предоставленаисключительно вознакомительныхцелях. Ниредакция,ниавторненесут ответственностизалюбой

возможныйвред,причиненный материаламиданнойстатьи.

074

ХАКЕР 01 /168/ 2013

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

ВВЕДЕНИЕ В ДИНАМИЧЕСКУЮ РЕКОМПИЛЯЦИЮ

Что же такое динамическая рекомпиляция и зачем она нужна? С точки зрения науки реверс-инжиниринга вопрос непростой, потому что включает в себя разбор больших структур бинарного кода с последующим анализом (отделением мух от котлет) и определенными действиями уже над подготовленным кодом. Компиляция — процесс однонаправленный, и в отличие от интерпретаторов и виртуальных машин компиляторы имеют на борту оптимизирующие бэкенды, которые наглухо уничтожают всю «полезную» информацию для полноценного восстановления исходного кода скомпилированной программы. Конечно же, есть возможность компиляции с драгоценной с точки зрения реверсера отладочной информацией (например, «gcc -g ./prog1.c -o /prog»), но на практике в release-версиях (то есть конечных вариантах) это маловероятно. Ты должен понимать, что отладочная информация — это палка о двух концах, которая также может послужить кратчайшим путем для крекинга проприетарного ПО.

Суть динамической рекомпиляции, которую также часто называют двоичной трансляцией (binary translation), заключается в эмуляции одного набора инструкций на другом за счет трансляции машинного кода. Последовательности инструкций переводятся из исходного набора (source) в целевой (target) набор инструкций. Двоичная трансляция позволяет выполнять приложения одной архитектуры при работе на второй, причем для оптимизирующих двоичных компиляторов скорость выполнения кода зачастую получается выше оригинала. Динамические рекомпиляторы бинарного кода могут быть реализованы как программно, так и аппаратно, причем программно они реализуются в двух вариантах:

a)вкачестветрансляторафронтенд-кодавпользовательском уровне(ring3);

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

Здесь нельзя не привести в пример разработанный для Apple корпорацией Transitive программный пакет Rosetta для динамической трансляции между платформами на основе архитектур SPARC, PowerPC, MIPS, Itanium и x86. Уровень трансляции Rosetta был включен в выпуски Mac OS 10.4 для Intel-ориентированных Mac — это было необходимо для упрощения перехода от PPC к x86.

Что касается аппаратных реализаций, то они, по сути, клонируют алгоритмы трансляции кода с последнего варианта софтверной реализации. Как ты понял, возможности динамической рекомпиляции (если процесс не завершается обратной компиляцией) в целом намного шире, чем обычная двоичная трансляция с оптимизацией.

Ниже хотелось бы представить несколько важных и перспективных направлений, где может использоваться динамическая рекомпиляция:

динамическаядекомпиляцияиманипуляциякодомнауровне ассемблераконкретноймашины:морфинг,крипт,структурная обфускацияинструкций,которыетакчастоиспользуютсявирусописателями;

динамическаядекомпиляцияспереводомассемблеровских инструкцийвпсевдокодналюбомизHighLevelLanguage(далее простоHLL);

динамическаяинструментализациябинарногокодадляпоискаузкихмест(вопросыоптимизации),утечкипамяти(memory leaking);

динамическаяинструментализациябинарногокодаспоследующиманализомпокрытиякодадляпоискатипичныхошибок (bufferoverflow,stackoverflowитакдалее);

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

На ум сразу приходят динамические рекомпиляторы типа Valgrind, PIN Toolkit, DynamoRIO, которые часто используются

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Метаморфозыw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ДекомпиляциябиблиотекиlibcдизассемблернымдвижкомMetasm

Построениеграфаисполняемойпрограммы,кстати,предоставляет большевозможностей,чемкоммерческийWinGraph

как средства для анализа покрытия кода. Некоторые исследователи используют в качестве инструмента известную ныне связку реверс-инженера: IDA Pro + IDAPython. В большинстве своем первые инструменты решают очень узкие задачи, то есть об универсальном подходе речи тут и не может быть. Для второго инструмента, как минимум, придется раскошелиться на платную версию Иды, ну и, соответственно, ознакомиться с API IDAPython. Казалось бы, универсального и бесплатного инструмента для вышеперечисленных направлений не существует, но чудо, как оказалось, есть, и имя ему Metasm.

ШВЕЙЦАРСКИЙ НОЖ РЕВЕРС-ИНЖЕНЕРА

Metasm — довольно крутой фреймворк с открытым исходным кодом, позволяющий взаимодействовать с машинным кодом в различных форматах (шестнадцатеричным байт-кодом, кодом на Cи и ассемблером конкретной машины). Поддерживает подавляющее большинство архитектур процессоров, форматов исполняемых файлов и работает на практически всех известных операционных системах. Впервые данная разработка была представлена Йоанном Гийо (Yoann Guillot) из ESEC на конференции SSTIC 2007, и позднее на ней практиковались на нескольких конференциях Hack.lu. Кстати, Metasm является сердцем такого известного и любимого инструмента пентестеров и хакеров всех мастей, как Metasploit Framework.

Основная часть фреймворка, которая именуется движком, написана на Ruby. Установка довольно проста, единственное,

что для нее требуется сделать, — это прописать пути в переменные окружения пользовательского интерпретатора. Если ты так же, как и я, используешь в качестве интерпретатора bash, тебе после распаковки библиотеки нужно будет добавить следующую строку в ~/.bash_profile:

export RUBYLIB=$RUBYLIB:/<путь до библиотеки>/metasm

Для пользователей Windows вопрос решается аналогичным образом, если они работают с cygwin (эмулятор UNIX-среды). В любом другом случае идем в переменные окружения OS Windows: свойства «Моего компьютера», на вкладке «Дополнительно» находим «Пере-

ХАКЕР 01 /168/ 2013

075

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

ВЗЛОМm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

менные окружения». Если у тебя уже стоит Руби, то там должна быть переменная RUBYLIB (если ее нет, то создаем вручную), в которую нужно будет как раз таки дописать путь до самой библиотеки.

Чтобы проверить, работает ли наша библиотека, достаточно вписать в консоль следующую команду:

$ ruby -r metasm -e 'p Metasm::VERSION'

1

С установкой разобрались, теперь рассмотрим краткий перечень возможностей фреймворка:

интерактивнаяработаигибкаяманипуляцияисполняемыми файлами:PECOFF,ELF,Mach-O,RawShellcode;

компиляцияснулябезиспользованиякомпиляторов,линкеров итомуподобногоинструментария;

дизассемблированиеибазоваядекомпиляциявпсевдокодС-like HLL;

манипуляцияструктуройфайлов.

ПОСТАНОВКА ПРОСТЫХ ЗАДАЧ И БЫСТРОЕ РЕШЕНИЕ

Выше мы рассмотрели перспективные направления динамической рекомпиляции, теперь пришло время продемонстрировать возможности фреймворка на деле. Итак, есть простая задача компиляции сырого Linux-шелл-кода на asm. Ниже приведен пример сборки шелл-кода без использования компилятора.

Подключаем нашу библиотеку:

require 'metasm'

Вызываем модуль ассемблирования, тип исполняемого файла a.out, архитектура процессора IA-32 (то есть x86), выхлопной файл test1.out, в переменную RAW_SHELLCODE заносим буфер с кодом нашего шелл-кода:

Metasm::AOut.assemble(Metasm::Ia32.new, <<RAW_SHELLCODE).encode_file('test1.out')

.text

.entrypoint mov eax, 4

mov ebx, 1

.data

str db "test\\n"

strend:

.text

mov ecx, str

mov edx, strend - str

int 80h // linux sys_write

mov eax, 1

mov ebx, 42

int 80h // linux sys_exit

ret

RAW_SHELLCODE

Отлично, проверяем наш сырой шелл-код и видим, что он работает, дизассемблировав встроенным дизассемблером (который идет в папке с примерами /samples):

$ ruby ./test1.rb

$ ruby ./disassemble.rb --no-data --cpu Ia32 ./test1.out entrypoint_0:

add [eax],

al

; @0

0000

add fs:[eax], al

;

@2

640000

adc [eax],

al

;

@5

1000

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Metasmпредоставляетвозможностьпросмотраэнтропиииудобную навигациюпосекциям,строкам,функциямфайла

...

mov eax, 1

; @36h

b801000000

mov ebx, 2ah

; @3bh

bb2a000000

int 80h

;

@40h

cd80

ret

;

@42h

c3 endsub entrypoint_0

Собственно, ты можешь сравнить этот код с дизассемблерным листингом, полученным с помощью ndisasm из пакета NASM.

$ ndisasm -b32 ./test1.out

Такой дизассемблерный дамп легко будет распарсить тем же Metasm/Ruby и в дальнейшем манипулировать этим кодом

как душе угодно — оптимизировать узкие места, транслировать в опкоды других машин, разбивать и переводить в микод (Mutable Independent Code) и так далее.

А как насчет декомпиляции и перевода в псевдокод HLL, например, реального работающего исполняемого файла библиотеки libc? После некоторой возни интерпретатора Руби и шуршания процессора на выходе мы получим:

$ ruby ./disassemble.rb --no-data --cpu Ia32 --exe ELF /lib/libc.so.6 --decompile

Качество кода не сказать что хорошее, но и не особо плохое, к примеру, для морфинга на уровне исходных кодов или в качестве

легитимного стаба для исполняемых файлов полученный код очень даже хорош. Если немного посидеть и поработать над модулем декомпиляции, естественно, можно добиться куда лучших результатов. Для более качественной декомпиляции можно изменить подход, например на ступени генерации IR (Itermediate Representation) разбивать код на специальные блоки (узлы графа), далее на основе сигнатур методом сопоставления восстанавливать структуры кода. Реализация идеи довольно проста, по сути, представляет собой специальный конструктор конечного автомата на основе множества свитчкейсов Ruby-кода: case something when «1» ... when «2»

... и так далее. Если прикрутить сюда модули генерации правдоподобных имен переменных, функций, процедур, можно получить более-менее полноценный декомпилятор. Подняв же веб-панель на Ruby on Rails, можно за короткое время организовать и неплохой сервис по декомпиляции ПО.

Текущий псевдокод больше поход на выхлоп фронтендовой части компилятора gcc, на этапе генерации синтаксического дерева. Такой код легко перегнать в PI-код (Position Independent Code, не путать с p-кодом виртуальных машин). Скрипт disassemble.

rb принимает намного больше параметров и, соответственно, предоставляет большие возможности для манипуляции процессом дизассемблирования. В папке samples ты также можешь найти сценарий disassemble-gui.rb, представляющий собой графическую прослойку над консольным дизассемблером.

076

ХАКЕР 01 /168/ 2013

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

АрхитектуравиртуальноймашиныT2Metasm

СБОРКА ИСПОЛНЯЕМЫХ ФАЙЛОВ НА HLL

Выше мы рассмотрели компиляцию сырого шелл-кода, в котором обошлись без использования компиляторов. Это уже впечатляет, а что ты скажешь, если мы решим компилировать Си-код с использованием WIN32API? Веришь или нет, но Metasm может и не такое!

Подключаем нашу библиотеку:

require 'metasm'

Дальше описываем параметры для сборки бинаря: первый параметр execlass, в который попадает тип исполняемого файла Metasm::PE, можно выбрать и другие типы ELF/MachO, с помощью второго параметра srctype_data указываем, что наш код на С (понятно, что можно указать и asm, если у нас код на ассемблере):

$opts = { :execlass => Metasm::PE, :srctype_data => 'c' }

Склеиваем исходники нашего файла и сэмпла exeencode.rb, представленного как раз для компиляции HLL-исходников:

load File.join(File.dirname(__FILE__), 'exeencode.rb')

__END__

Ниже представлен код на Си. Если внимательно рассмотреть этот код, можно заметить, что WinAPI-функции тут представлены декораторами, и их в обязательном порядке нужно вначале объявлять в виде прототипов. Ты также должен понимать, что любые другие функции из ряда CRT/RTL/LIBC и прочих библиотек также должны описываться в виде прототипов прежде, чем ты сможешь использовать их в своем коде.

__stdcall int MessageBox(int, char*, char*, int);

__stdcall void ExitProcess(int);

void main(void)

{

MessageBox(0, "Sanjar Satsura", "hello", 0);

ExitProcess(0);

}

Высокоуровневый код тут можно мешать также и с ассемблером, как отдельно в виде связки C + ASM кода, так и в виде инлайновых ассемблерных вставок прямо в Си-коде. Тот же самый код на чистом ассемблере x86 будет выглядеть так:

require 'metasm'

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Метаморфозыw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Создаем переменную pe в качестве хендла для манипуляции кодом. Указываем тип исполняемого файла, в данном случае — PE, наш код на ассемблере, поэтому применяем метод assemble, так как код мы пишем для x86, ОС указываем в качестве архитектуры Ia32. В переменную EOS методом HEREDOC помещается наш код на ассемблере:

pe = Metasm::PE.assemble Metasm::Ia32.new, <<EOS

.entrypoint push 0

push title push message push 0

call MessageBoxA

xor eax, eax ret

.data

message db 'Sanjar Satsura', 0 title db 'hello', 0

EOS

Собираем наш исполняемый файл:

pe.encode_file 'testpe.exe'

В исходниках, приложенных к данной статье, ты можешь найти демку, цель которой — продемонстрировать ход трансляции и кодогенерации рубинового компилятора Metasm. Думаю, не нужно быть гением, чтобы понять, какой профит можно сорвать на этой теме. Если посмотреть на внутренности предложений черного рынка, к примеру услуги «уникальных» крипторов/протекторов, предоставляющих доступ через веб-интерфейс, можно понять, насколько они убоги в реализациях. И неудивительно, что все сервисы подобного рода оказываются построены на PHP (вебинтерфейс, шлюз) и исполняемом бинарном движке, работающем в подавляющем большинстве (99,8%) на VPS с ОС Windows. Добавим сюда криворуких кодеров подобных веб-сервисов с конструкциями кода типа:

...

system_execute('C:\\SXCryptor\engine.exe --input_file '.$_GET["file_id"].' --output_file '.rand_fl($fname).'

--dir '.$wrk_dir);

...

Все, что мы рассмотрели выше, еще цветочки по сравнению с тем, что этот волшебный фреймворк нам позволяет написать. Так вот, он позволяет создавать не только ring 3 приложения, но и мо-

дули ядра, драйверы для известных пользовательских операционных систем, работоспособность которых тут же можно проверить при помощи встроенных VM. Звучит круто? Давай попробуем создать полноценный драйвер с возможностью загрузки и выгрузки. Ниже я приведу фрагменты кода с комментариями, полный исходник драйвера ты можешь найти на нашем диске.

require 'metasm'

Обрати внимание: ниже мы не инклудим библиотеку, в данном случае слово include необходимо для перевода имени класса Metasm в глобальную область видимости. В C++/C# за это ответственна конструкция «using namespace ***». До этого мы делали так: Metasm::PE.assemble, Metasm::Ia32.new. В дальнейшем конструкцию Metasm:: можно будет не писать.

ХАКЕР 01 /168/ 2013

077

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

F

 

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

ВЗЛОМm

w Click

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

 

.c

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-xcha

n

e

 

include Metasm

#Имя нашего драйвера $drv = 'drv_test.sys'

#Размер буфера обычно используется для трейса драйвера, здесь мы его рассматривать не будем

BUF_SZ = 0

#Проверяем, существует ли файл нашего драйвера

if not File.exist? $drv

Собираем драйвер, в переменной DRV_CODE содержится код нашего 32-битного драйвера для Windows. Опция kmod в encode_file указывает, что драйвер работает на уровне ядра:

PE.assemble(Ia32.new, <<DRV_CODE).encode_file ($drv, 'kmod')

#define bufsz #{BUF_SZ}

.data

oldi1 dd 0,0

oldi15 dd 0,0

buf dd bufsz dup(?)

.text

.entrypoint

mov eax, [esp+4]

mov dword ptr [eax+0x34], unload call setup_idt

xor eax, eax

// Размер используемого буфера mov [buf], eax

ret

Для запуска и останова напишем Cи-обертку. DynLdr — специальный каркасный модуль Metasm, позволяющий интерпретатору Ruby манипулировать экспортируемыми API-функциями динамически разделяемых библиотек (*dll, *so).

DynLdr.new_api_c <<DRV_CODE

// Определяем типы и структуры данных typedef int BOOL;

typedef char CHAR;

typedef unsigned long DWORD;

...

Здесь, как и описывалось ранее, задаем прототипы WinAPIфункций:

__stdcall BOOL CloseServiceHandle (SC_HANDLE hSCObject __attribute__((in))); __stdcall SC_HANDLE CreateServiceA(SC_HANDLE hSCManager __attribute__((in)), LPCSTR lpServiceName __attribute__((in)), LPCSTR lpDisplayName __attribute__((in)),DWORD dwDesiredAccess __attribute__((in)), DWORD

...

Константы, используемые импортируемыми выше APIфункциями

#define STANDARD_RIGHTS_REQUIRED

(0x000F0000L)

#define SC_MANAGER_CONNECT

0x0001

#define SC_MANAGER_CREATE_SERVICE

0x0002

...

 

Ну и, собственно, код для загрузки/выгрузки драйвера:

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

# Создаем функцию loadmod, где переменной mod

присваивается указатель на наш драйвер def loadmod(mod=$drv)

После того как модуль DynLdr подгрузил все нужные API из прототипов, имена функций можно писать в нижнем регистре. Переменная sh является хендлом для открытия менеджера управления сервисами. Без старта OpenSCManagerA() мы не можем стартовать сервис нашего драйвера CreateServiceA().

sh = DynLdr.openscmanagera

(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)

# Выплевываем ошибку при возникновении исключения raise "cannot openscm" if (sh == 0)

Переменная rh является хендлом функции старта сервиса, функцией CreateServiceA() запускаем сервис, передав параметры для запуска, так, параметр SERVICE_KERNEL_DRIVER указывает на то, что это драйвер режима ядра.

rh = DynLdr.createservicea(sh, mod, mod,

DynLdr::SERVICE_ALL_ACCESS,

DynLdr::SERVICE_KERNEL_DRIVER,

DynLdr::SERVICE_DEMAND_START,

DynLdr::SERVICE_ERROR_NORMAL,

File.expand_path(mod), 0, 0, 0, 0, 0)

# Стартуем сервис

if (DynLdr.startservicea(rh, 0, 0) == 0)

raise "cannot start service"

end

#Закрытие хендлов в обратном порядке,

#по типу LIFO (Last Input First Out) DynLdr.CloseServiceHandle(rh) DynLdr.CloseServiceHandle(sh)

end

# Функция выгрузки драйвера def unloadmod(mod=$drv)

sh = DynLdr.openscmanagera

(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)

raise "cannot openscm" if (sh == 0)

rh = DynLdr.openservicea

(sh, mod, DynLdr::SERVICE_ALL_ACCESS)

Функция ControlService() позволяет нам управлять сервисами. В качестве параметров принимает хендл rh и параметр SERVICE_ CONTROL_STOP, который останавливает сервис.

DynLdr.controlservice

(rh, DynLdr::SERVICE_CONTROL_STOP, 0.chr*4*32)

# Удаляем сервис функцией DeleteService()

DynLdr.deleteservice(rh)

DynLdr.CloseServiceHandle(rh)

DynLdr.CloseServiceHandle(sh)

end

Наш скрипт принимает параметры (см. полный исходник на диске) для загрузки и выгрузки соответственно. Если же параметры не указываются, скрипт просто генерирует драйвер, проверяя вначале его наличие в текущей папке. Итак, теперь есть возможность динамической генерации драйверов и манипуляции ими на уровне ядра, если, конечно, у нас есть все права для его запуска.

ПОТРОШЕНИЕ ПАКЕРА

Напоследок мы займемся распаковкой известного пакера UPX. В задачу нашего скрипта будет входить загрузка упакованного исполняемого файла в память, поиск оригинальной точки

078

ХАКЕР 01 /168/ 2013

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

входа по дизассемблированному UPX стабу в памяти, установка прерываний на OEP, ну и наконец, дамп распакованного образа на жесткий диск.

def find_oep(pe)

#Дизассемблируем стаб образа UPX для поиска

#cross-section jump’ов для нахождения оригинальной точки

#входа (OEP)

dasm = pe.disassemble_fast_deep 'entrypoint'

return if not jmp = dasm.decoded.find { |addr, di|

#Проверяем каждый блок данных узлов графа next if not di.block_head?

b = di.block

next if b.to_subfuncret.to_a.length != 0 or b.to_normal.to_a.length != 1

to = b.to_normal.first

#Игнорируем прыжки в несуществующие адреса next if not s = dasm.get_section_at(to)

#Игнорируем прыжки в данной секции

next if dasm.get_section_at(di.address) == s

true

}

Теперь мы имеем нормальный jump [<адрес>, di], благодаря которому появляется возможность восстановить оригинальную точку входа:

dasm.normalize(jmp[1].block.to_normal.first)

end

В качестве прерывания будем ставить hardware breakpoint на OEP:

def debugloop

# Функция debugloop истинна, пока нет адреса точки входа

@dbg.hwbp(@oep, :x, 1, true) { breakpoint_callback }

@dbg.run_forever

puts 'done'

end

def breakpoint_callback

puts 'breakpoint hit !'

#Снимаем дамп процесса из памяти, создавая при этом

#уникальный исполняемый PE-файл

dump = LoadedPE.memdump @dbg.memory, @baseaddr, @oep,

@iat

#Загрузчик упаковщика UPX распаковывает все данные

#из секции помеченных для чтения (Read Only), сменив

#маркер RO на RW (Read Write) в заголовке PE-файла dump.sections.each { |s| s.characteristics |= ['MEM_WRITE'] }

#Пишем распакованный образ на жесткий диск dump.encode_file @dumpfile

...

В конце создаем функцию загрузки и управления ходом распаковки упакованного файла. Функция initialize является инициализирующей функцией, в задачу которой входит: поиск точки входа по дизассемблированному стабу, переход по точке входа и прокрутка (трейс) инструкций до распаковки оригинального кода файла, снятие образа распакованного файла на жесткий диск.

def initialize(file, dumpfile, iat_rva=nil) @dumpfile = dumpfile || 'upx-dumped.exe'

@iat = iat_rva

puts 'disassembling UPX loader...'

# Считываем данные запакованного файла

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Метаморфозыw Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Prologue

Prologue

RDTSC

Traitement desdonneesA

Traitement

Traitement

desdonneesB

desdonneesB'

Epilogue

Epilogue

Исследованиеструктурнойдеобфускации

WWW

Подробная информацияпо динамической рекомпиляции: bit.ly/UKqcJa;

AtomicOperation(введение ватомарные операции):bit. ly/SRtydt;

StaticBi-

naryTranslation HOWTO:bit.ly/ hynl4g;

Universityof QueenslandBinaryTranslator: bit.ly/SRtBWy;

Reverse Compilation Techniques:bit. ly/QioqTP;

страница проектаMetasm: metasm.cr0.org;

гуманное

сообщество Ruby’стов:rubylang.org.

pe = PE.decode_file(file)

#Ищем точку входа

@oep = find_oep(pe)

raise 'cant find oep...' if not @oep puts "oep found at #{Expression[@oep]}" @baseaddr = pe.optheader.image_base @iat -= @baseaddr if @iat > @baseaddr

#Запускаем отладчик для установки хардварных

#брейкпоинтов и трейса (функция debugloop)

#инструкций

@dbg = OS.current.create_process(file).debugger

puts 'running...'

debugloop

end

Единственное, что остается выполнить для восстановления полноценной работоспособности, — это восстановить таблицу импорта распакованного файла, например утилитой ImpRec.

Упаковщик UPX мы выбрали в качестве протектора не случайно. В приведенном примере выполняются практически все стандартные функции распаковки, которые могут использоваться и в других упаковщиках/крипторах. По сути, эти принципы распространяются на 95% существующих протекторов, которые как раз таки в большинстве своем черпали вдохновение именно из UPX. Вторым немаловажным моментом является мультиплатформен-

ность этого упаковщика, сжатие поддерживается как для PE COFF, так и для ELF-форматов, а вместе с тем распространяются и методы распаковки на *nix-системы. Последние 5% существующих протекторов, которые немного отличаются методами упаковки данных, — это, наверное, виртуализаторы кода и крипторы с пермутирующим движком. Для первых существуют таблицы с опкодами VM-движков, для вторых можно использовать VM нашего фреймворка.

ПРОЧИЕ ВОЗМОЖНОСТИ

Metasm предоставляет среду для воплощения большого количества идей, связанных не только со сферой инфобезопасности. Существует ряд нерешенных алгоритмических задач в теории компиляторов. Написанный на Ruby, фреймворк предоставляет возможность решения этих самых задач не самым человечным и ООП-шным образом. Дерзай! z

ХАКЕР 01 /168/ 2013

079

Соседние файлы в папке журнал хакер