книги хакеры / журнал хакер / 168_Optimized
.pdf
|
|
|
|
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 |