0x00 前言
上一节研究了内核栈溢出相关问题,事实上利用已经成功了, 但是源码在vs环境下编译的驱动文件和利用程序,在最后返回时的堆栈平衡出现了问题,由于时间有限,暂时搁置,后面有时间将继续研究。
这一节学习释放后引用漏洞,即UAF。
实验环境:Win10专业版+VMware Workstation 15 Pro+Win7 x86 sp1
实验工具:VS2015+Windbg+KmdManager+DbgViewer
0x01 漏洞原理
UAF
Use After Free 就是其字面所表达的意思,当一个内存块被释放之后再次被使用。但是其实这里有以下几种情况(引用Thunder J师傅的总结,到位):
①内存块被释放后,其对应的指针被设置为 NULL , 然后再次使用,自然程序会崩溃。
②内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
③内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。
一般Use After Free 漏洞主要是后两种。
Demo
1 |
|
buf2 “占坑”了buf1 的内存位置,经过UAF后,buf2被成功篡改了。
程序分配和buf1大小相同的堆块buf2实现占坑,buf2分配到已经释放的buf1内存位置,但由于buf1指针依然有效,并且指向的内存数据是不可预测的,可能被堆管理器回收,也可能被其他数据占用填充,buf1指针称为悬挂指针,借助悬挂指针buf1将内存赋值为hack,导致buf2也被篡改为hack。
如果原有的漏洞程序引用到悬挂指针指向的数据用于执行指令,就会导致任意代码执行。
在通常的浏览器UAF漏洞中,都是某个C++对象被释放后重引用,假设程序存在UAF的漏洞,有个悬挂指针指向test对象,要实现漏洞利用,通过占坑方式覆盖test对象的虚表指针,虚表指针指向虚函数存放地址,现在让其指向恶意构造的shellcode,当程序再次引用到test对象就会导致任意代码执行。
分析
UAF漏洞中涉及许多驱动程序功能,依次查看,以提供适当的详细信息。
AllocateUaFObject
1 | typedef void(*FunctionPointer)(); |
该函数分配一个非分页的池块,用‘A’填充它,预先设置一个回调指针并添加一个空终止符。 IDA中的流程几乎相同,如下所示。 请注意,对象大小为0x58字节,池标记为“Hack”(小端对齐)。
FreeUaFObject
1 | NTSTATUS |
相当直接,这可以通过引用标记值来释放池块。在安全版本下,这是安全的,而在不安全的版本中这是包含漏洞的函数,因为在释放对象后“g_UseAfterFreeObject”未设置为null,因此保留了“过时”的对象指针。
UseUaFObject
1 | NTSTATUS UseUaFObjectNonPagedPool(VOID){ |
此函数由于UAF存在,则读入“g_UseAfterFreeObject”值并执行对象回调。
AllocateFakeObject
最终,我们找到这样一个驱动函数:它允许我们在非分页内存池上分配一个伪造的对象;该函数允许我们把对象分配到原本的UAF对象所在的位置上。
1 | NTSTATUS |
整理一下,整个流程:
- 我们分配一个UAF对象。
- 我们释放掉UAF对象。
- 我们使用伪造的对象占坑释放掉的UAF对象内存。
- 我们用野指针调用UAF对象的callback函数,此时callback函数指针已经由伪造的对象来决定了。
0x02 漏洞利用
在Hacks Team提供的利用程序源码中,可以看到,其利用流程与我们上面总结的利用流程无二。
当申请的堆大小和UAF中堆的大小相同,极有可能申请到我们的这块内存;而且提前构造好了这块内存中的数据,释放时指向payload,从而达到提权的效果。
只构造一块伪造堆,显然是不可取的,“堆海”才有可能实现,即就是Feng shui技术。
利用代码,可以参考这里,下面只展示出核心部分:
1 | //申请fake UAF对象 |
最终看到。提权成功,提权过程参考我的前一篇。
0x03 漏洞防范
很明显在安全版本中,已给出防范方法,即释放后指控,防止悬挂指针的出现。
1 | #ifdef SECURE |
0x04 链接
[1]fuzzsecurity:https://www.fuzzysecurity.com/tutorials/expDev/19.html
[2]Thunder J https://bbs.pediy.com/thread-252310.htm