CVE-2018-8897 调试分析
原文地址:https://mozhe.cn/news/detail/285
![](https://img.haomeiwen.com/i9932774/1d40b9c7eea2eac1.png)
漏洞说明
系统内核通过Mov到SS(stack segment)或Pop到SS指令进行堆栈切换操作后处理异常情况时发现了一个缺陷.在堆栈切换操作期间,处理器没有传送中断和异常,而是在堆栈切换后面的第一条指令执行完传送.一个没有特权的系统用户可以利用这个漏洞来破坏系统内核,导致拒绝服务.实际上MOV到SS会延迟一些调试异常(例如一个硬件断点)直到下条指令完成;如果跟随MOV到SS或者POP到SS后面的指令是SYSCALL、SYSENTER、INT 3之类的指令,调试异常被捕获之后将转移到Ring0执行.
样本环境
首先从https://github.com/can1357/CVE-2018-8897下载POC源代码,用vs2015新建立一个win32控制台工程;POC里面有个64位汇编的asm文件,使用VC++自带的汇编编译器编译Native.asm为一个obj文件[ml64.exe/FlNative /c /Zi Native.asm],再把obj添加到工程里面[右击工程名字,点击属性;点击配置属性→链接器→输入→附加依赖项;输入obj名字],然后编译得到一个POC,编译POC一步算完了.
![](https://img.haomeiwen.com/i9932774/a2529815e5b08865.png)
样本是Win7x64位的,接下来搭建一个Win7x64的VMWar环境,运行poc.exe尝试触发一次,POC显示失败了.
![](https://img.haomeiwen.com/i9932774/f928e7c4f0dd3dbb.png)
POC失败问题先暂停.关于WinDBG的内核调试,WinDBG与VMWare双机内核调试工具的配置请使用VirtualKD.如何安装使用请自行搜索.
![](https://img.haomeiwen.com/i9932774/acf7ca2cd55cc45c.jpg)
修复样本
回看一下上面的问题,样本为什么会失败?看下前面错误提示之前的代码,这段代码与KeBugCheckEx与PsGetCurrentProcess函数有关.
![](https://img.haomeiwen.com/i9932774/b622327a3129ba5c.png)
在IDA中打开目标系统的ntoskrnl.exe文件,看KeBugCheckEx的汇编代码,代码应该搜寻0x53E0这个偏移,gs:20h是一_KPRCB结构的变量_KPCR→CurrentPrcb,这些结构一般是公开的,利用搜索引擎很容易获得.0x53EO偏移处保存的一个CONTEXT结构,也就是Prcb.Context.
![](https://img.haomeiwen.com/i9932774/2ee3ce1186be5d31.png)
PsGetCurrentProcess的汇编代码,代码应该是搜寻0x70值.GS寄存器等于32位系统FS寄存器,其实这里指向内核结构_KPCR结构.0x188的偏移位置是_KTHREAD结构,0x70的偏移位置是_KTHREAD结构,也就是KThread.ApcStateFill.Process
![](https://img.haomeiwen.com/i9932774/b507cb98f6b10760.png)
![](https://img.haomeiwen.com/i9932774/922b796f6d65ff57.png)
PsGetCurrentProcess函数的有关实现.
![](https://img.haomeiwen.com/i9932774/1a251eee5fd087a9.png)
现在说下修复方法,找到自己系统上这两个函数数据结构的偏移,如果发现没有这两个数据结构的偏移,那么请找一个函数修复这个偏移.测试系统上Prcb.Context对应不上,需要修复偏移.
![](https://img.haomeiwen.com/i9932774/5503363033cd8c5d.png)
打开WinDBG进行双机内核调试,输入命令dt _KPRCB查找_CONTEXT的偏移地址,测试系统偏移为0x4bd8.
![](https://img.haomeiwen.com/i9932774/27e86cb13fe95b75.png)
修改POC里面的偏移地址,然后运行POC测试
![](https://img.haomeiwen.com/i9932774/7e3f77fb15b50d39.png)
在vmware里面运行到漏洞触发那里还是崩溃了?为什么呢?原因是读取GS的基址失败了.原因很有可能就是vmware不支持rdgsbase之类的指令,尝试了二进制翻译功能,仍然提示错误.怎么解决呢?请看下面.
![](https://img.haomeiwen.com/i9932774/2e5b9dff2ebbb0f8.png)
如果本机打了补丁的请卸载名字为KB4103712 与KB4103718的补丁,卸载补丁后再次运行.运行结果是直接重启.WHAT?说明已经触发了漏洞,但是例如ROP与Shellcode之类的可能还需要修复.先看一下攻击过程./p>
攻击过程
源码中首先检测内核页表隔离(KPTI,kernel page table isolation, Windows内核中又叫KvaShadow)是否被启用,这好像与一个INTEL漏洞相关.
![](https://img.haomeiwen.com/i9932774/53de6ea7e811afc7.png)
使用VirtualLock函数把两个函数的代码锁定在物理页面,供内核使用.
![](https://img.haomeiwen.com/i9932774/c20064ea2be66c4b.png)
得到内核基址并加载(LoadLibrary)内核ntoskrnl.exe到自身进程.
![](https://img.haomeiwen.com/i9932774/74151fe8edca36ca.png)
分配4个内核对象(_KPCR,_KTHREAD,_KPROCESS,_KPRCB)供内核使用.
![](https://img.haomeiwen.com/i9932774/f55d71f1651e8188.png)
内核结构_KPCR的偏移0x180处是_KPRCB结构.
![](https://img.haomeiwen.com/i9932774/c734ed00a797754b.png)
解析内核模块,查找一些用于ROP技术的小组件.
![](https://img.haomeiwen.com/i9932774/a08e448952080c7b.png)
找到内核结构_KPRCB成员Context的偏移地址,找到进程_EPROCESS的偏移地址.前面讲过,不重复讲.
填充_KPCR,_KPRCB,_KTHREAD结构体,相关成员的偏移地址在不同的系统可能会一样,请自己修复.
![](https://img.haomeiwen.com/i9932774/0722b91dae65ea03.png)
读取堆栈段寄存器保存到变量SavedSS.
![](https://img.haomeiwen.com/i9932774/d46343d19d751b5f.png)
创建一个线程,获得填充_KPRCB对象的成员_CONTEXT的偏移地址,并等待RtlCaptureContext函数调用获得泄漏的RSP,漏洞触发前会一直等待,实际上异常才会进入这个函数.等待第二次RtlCaptureContext被调是计算两次RSP的差异值来预测下一次RSP的值,并预测返回指针的位置,并建立RtlCaptureContext需要的上下文.第一条ROP的RETN位置放在XMM13寄存器.
![](https://img.haomeiwen.com/i9932774/731d1f1b1efd4f6f.png)
设置辅助线程的优先级为最高并分离当前线程与创建的辅助线程,让它们在不同的CPU上执行,接着获取了一些内核函数的地址.
![](https://img.haomeiwen.com/i9932774/d85d119209aa6808.png)
给线程设置两个异常,一个在SS段寄存器为读写异常,另一个写异常位于结构_KPRCB→ProcessorState(_KPROCESSOR_STATE)→SpecialRegisters(_KSPECIAL_REGISTERS)→Cr8.
![](https://img.haomeiwen.com/i9932774/7edee400f8c1527d.png)
WinDBG输入u KeBugCheckEx,红色部分,如果偏移不一样则需要修复这个结构.
![](https://img.haomeiwen.com/i9932774/b909a7d204016f95.png)
开始填充rop到XMM寄存器.
![](https://img.haomeiwen.com/i9932774/d1509d057a06803b.png)
最后读取gs段寄存器基址,重设基址指向自己填充的_KPCR结构,然后触发漏洞,恢复GS段寄存器,并使用SS段寄存器去触发漏洞.
![](https://img.haomeiwen.com/i9932774/4d9ff44c56aef081.png)
先使用mov ss指令切换堆栈,并且会产生前面的ss调试异常,但mov ss切换后不会立即发送异常要等下一条指令执行完成后才发送;紧接着是一个int 3指令,由于前面会改变了gs段寄存器的基址,进入内核之后要使用SWAPGS指令交换gs段寄存器.
![](https://img.haomeiwen.com/i9932774/0d81e9a52eb2a8d2.png)
可能需要修复的一些数据偏移,根据不同的系统偏移不同.
![](https://img.haomeiwen.com/i9932774/dad988270ddcb7b7.png)
小结
由于无法使用WinDBG对本机进行内核调试验证,修复POC里面的数据偏移,如果数据偏移没有修改正确就测试POC,可能会造成系统重启.快点自己动手试下吧.