CVE-2019-1215分析笔记
2020-02-05 18:58:00 Author: mp.weixin.qq.com(查看原文) 阅读量:87 收藏

本文为看雪论坛优秀文章

看雪论坛作者ID:我是哥布林

简介
CVE-2019-1215是ws2ifsl.sys中的一处UAF漏洞,原POC中,主要通过提前释放+堆喷的方式,修改函数执行流程,造成任意内存写的作用。
本文主要基于原POC介绍(https://www.anquanke.com/post/id/196893),进行一些深入的分析。
测试环境
Windows 10 18362.30 x64
背景知识

ws2ifsl.sys中SocketFile和ProcessFile对象的分配

在驱动的DispatchCreate函数中,驱动根据上层传入的字符串,判断初始化的对象类型:
(1)NifsSct初始化SocketFile对象对应的FSContext为sockedData
(2)NifsPvd初始化ProcessFile对象对应的FSContext为procData

驱动通过FileObject的FSContext对象,区分对象是SocketFile还是ProcessFile
SocketFile: FSContext前4个字节为硬编码0x6B636F53对应字符串 'kcoS'
ProcessFile: FSContext前4个字节为硬编码0x636F7250对应字符串 'corP'
(1)CreateProcessFile函数中的 ProcessFile初始化

(2)CreateSocketFile函数中的 SocketFile初始化:


ws2ifsl.sys中SocketFile和ProcessFile对象的释放

释放的位置在DispatchClose函数中,驱动根据FSContext头4个字节的硬编码内容,分别进行释放操作:

procData中的KAPC初始化

在调用CreateProcessFile初始化ProcessFile时,内部会调用InitializeRequestQueue初始化一个APC队列。位置为procData+0x30处:

因此procData的主要结构如下,我们的目标,便是在提前释放后利用堆喷,修改KernelRoutine指向我们希望执行的函数(这个函数最好能够进行内存读写的功能)达到利用的目的。

procData中的APC分发

每次在向SocketFile下发读写操作时,驱动会调用DoSocketReadWrite函数,DoSocketReadWrite会先通过socketData获取procData,然后调用QueueRequest,最终调用SignalRequest利用procData中的KAPC分发APC。

漏洞根本原因
提前关闭了ProcessFile句柄,会导致procData提前释放。但是在SocketFile如果之前有下发读写操作时,procData中的KAPC结构依然在使用,这样在下一次APC调度的时候,造成UAF。
利用步骤
(1)分配好ProcessFile和SocketFile。
(2)提前释放ProcessFile,并往SocketFile中写数据。
(3)利用堆喷,占坑PorcessFile释放的位置,其中的关键逻辑是伪造KAPC结构,避免系统检查异常导致蓝屏。
(4)调用NtTestAlert强制分发APC,触发任意写,破坏token,之后注入winlogon完成提权。
利用过程
1. 分配好ProcessFile和SocketFile,没什么可说的。

在驱动调用CreateProcessHandle初始化完后,procData的结构如下,大小为0x120。

注意procData+0x30处的KAPC结构,初始化完毕以后如下。
其中的RequestRundownRoutine函数是用来在APC调度完毕后做一些清理操作的函数,如释放DoSocketReadWrite分配的MDL内存,完成R3的IRP请求操作等。
QueuedKernelRoutine则是接下来APC分发要调用的函数。

2. 关闭ProcessFile句柄,并往SocketFile中写数据,之后关闭SocketFile句柄,触发提前释放操作。
从堆栈可以看出,其实是因为关闭了socketHandle,间接减少了proHandle的引用计数,触发了内存释放的操作。


释放以后的内存状态如下:

3. 利用堆喷,占坑ProcessFile释放的位置,其中的关键逻辑是伪造KAPC结构,同时要避免系统检查异常导致蓝屏。
这里利用的手法是:利用命名管道的创建和读写进行堆的占坑的操作。
注意在WIN10 18362版本上,DATAENTRY的结构头占0x30字节(参考Npfs!NpAddDataQueueEntry函数)
所以前0x30字节的DATAENTRY头我们是不能进行改写的,但是procData的KAPC恰好位于0x30字节开始处(运气好)

同时占坑的内容为我们精心构造的KAPC结构,我们构造的目的主要有两个:
(1)在APC调度的时候执行我们的代码,我们只需要将KAPC中的KernelRoutine替换成我们的函数即可,这里我们使用的SeSetAccessStateGenericMapping,原因是他可以向第二个参数的位置 写入第一个参数指定内容,写入的大小为16个字节(xmm0)。前八字节可控(rdx):

我们在构造的时候,只需要将这两个参数,构造成我们想写的地址,和写的内容即可。
注意第一个参数对应KAPC的NormalContext,第二个参数对应KAPC的SystemArgument2参数:

这里我们构造的写入地址为当前进程的Token位,目的是启用Privileges的Present和Enabled位(原POC中使用0xffffffffffffffff覆盖)


(2)绕过内核的安全检查
为了避免蓝屏,我们需要保证ApcListEntry字段的正确性。这里我们直接使用当前线程的KTHREAD结构里面的ApcState的链表进行替换即可。


堆喷成功以后,对应的APC结构如下图(PS.原POC将NornalRoutine指定为xHalTimerWatchdogStop,其只有一条ret指令,目的是为了不进行任何处理操作)

4. 调用NtTestAlert强制分发APC,触发任意写,破坏token,之后注入winlogon完成提权。
NtTestAlert能够强制当前线程进行APC的分发操作,继而触发SeSetAccessStateGenericMapping的函数调用,向指定的位置写数据。

触发以后的效果,可以看到成功的向Present和Enabled位写入了0xffffffffffffffff,并且可以注入winlogon进程,达到提权的效果。

系统补丁
参考第二步的利用堆栈。因为根本原因是因为ProcessFile对象提前释放导致的,所以,微软的修改方法就是在初始化ProcessFile时,在procData增加一个内部引用计数,初始化为1。
在每次有APC分发时(SignalRequest函数),增加引用计数,APC分发完毕后的清理函数中(RequestRundownRoutine函数),减少引用计数。DispatchClose被触发时,同样减少引用计数,检查引用计数是否为0,如果为0,就直接释放。
非0说明此时还有APC在分发,先不进行释放,等到APC分发完毕之后的清理函数中(RequestRundownRoutine)再进行释放。避免了提前释放的情况发生。


参考链接
https://www.anquanke.com/post/id/196893
https://labs.bluefrostsecurity.de/blog/2020/01/07/cve-2019-1215-analysis-of-a-use-after-free-in-ws2ifsl/
https://github.com/bluefrostsecurity/CVE-2019-1215
http://www.alex-ionescu.com/?p=231
- End -

看雪ID:我是哥布林

https://bbs.pediy.com/user-700462.htm 

*本文由看雪论坛 我是哥布林 原创,转载请注明来自看雪社区。

推荐文章++++

CVE-2017-11882理论以及实战样本分析

恶意代码分析之 RC4 算法学习

CVE-2017-0101-Win32k提权分析笔记

ROPEmporium全解

实战栈溢出漏洞

好书推荐


公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]
“阅读原文”一起来充电吧!

文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458303178&idx=1&sn=13af1f1faf6a0090826e922d8977e31e&chksm=b1818a4086f60356c4a66302afefc51764ce8a5a44e14037e0994fa8be5c7785e252bfb7608d#rd
如有侵权请联系:admin#unsafe.sh