CVE-2018-8897 调试分析

1526884676b73bad.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一步算完了.

样本是Win7x64位的,接下来搭建一个Win7x64的VMWar环境,运行poc.exe尝试触发一次,POC显示失败了.

POC失败问题先暂停.关于WinDBG的内核调试,WinDBG与VMWare双机内核调试工具的配置请使用VirtualKD.如何安装使用请自行搜索.

1526885152b72a5a.jpg

修复样本

回看一下上面的问题,样本为什么会失败?看下前面错误提示之前的代码,这段代码与KeBugCheckEx与PsGetCurrentProcess函数有关.

在IDA中打开目标系统的ntoskrnl.exe文件,看KeBugCheckEx的汇编代码,代码应该搜寻0x53E0这个偏移,gs:20h是一_KPRCB结构的变量_KPCR→CurrentPrcb,这些结构一般是公开的,利用搜索引擎很容易获得.0x53EO偏移处保存的一个CONTEXT结构,也就是Prcb.Context.

1526885180c96c08.png

PsGetCurrentProcess的汇编代码,代码应该是搜寻0x70值.GS寄存器等于32位系统FS寄存器,其实这里指向内核结构_KPCR结构.0x188的偏移位置是_KTHREAD结构,0x70的偏移位置是_KTHREAD结构,也就是KThread.ApcStateFill.Process

15268851879425be.png

1526885207437d46.png

PsGetCurrentProcess函数的有关实现.

1526885224edfbe1.png
现在说下修复方法,找到自己系统上这两个函数数据结构的偏移,如果发现没有这两个数据结构的偏移,那么请找一个函数修复这个偏移.测试系统上Prcb.Context对应不上,需要修复偏移.
8.png

打开WinDBG进行双机内核调试,输入命令dt _KPRCB查找_CONTEXT的偏移地址,测试系统偏移为0x4bd8.

1526885236243fac.png

修改POC里面的偏移地址,然后运行POC测试

1526885243749cf0.png

在vmware里面运行到漏洞触发那里还是崩溃了?为什么呢?原因是读取GS的基址失败了.原因很有可能就是vmware不支持rdgsbase之类的指令,尝试了二进制翻译功能,仍然提示错误.怎么解决呢?请看下面.

如果本机打了补丁的请卸载名字为KB4103712 与KB4103718的补丁,卸载补丁后再次运行.运行结果是直接重启.WHAT?说明已经触发了漏洞,但是例如ROP与Shellcode之类的可能还需要修复.先看一下攻击过程.

攻击过程

源码中首先检测内核页表隔离(KPTI,kernel page table isolation, Windows内核中又叫KvaShadow)是否被启用,这好像与一个INTEL漏洞相关.

1526885267037a59.png

使用VirtualLock函数把两个函数的代码锁定在物理页面,供内核使用.

1526885274197f76.png

得到内核基址并加载(LoadLibrary)内核ntoskrnl.exe到自身进程.

15268852939a0ee0.png

分配4个内核对象(_KPCR,_KTHREAD,_KPROCESS,_KPRCB)供内核使用.

15268853201e0fee.png

内核结构_KPCR的偏移0x180处是_KPRCB结构.

15268853313941c4.png

解析内核模块,查找一些用于ROP技术的小组件.

1526885337f40ee6.png

找到内核结构_KPRCB成员Context的偏移地址,找到进程_EPROCESS的偏移地址.前面讲过,不重复讲.

填充_KPCR,_KPRCB,_KTHREAD结构体,相关成员的偏移地址在不同的系统可能会一样,请自己修复.

15268853521ff1de.png

读取堆栈段寄存器保存到变量SavedSS.

1526885360a440a3.png

创建一个线程,获得填充_KPRCB对象的成员_CONTEXT的偏移地址,并等待RtlCaptureContext函数调用获得泄漏的RSP,漏洞触发前会一直等待,实际上异常才会进入这个函数.等待第二次RtlCaptureContext被调是计算两次RSP的差异值来预测下一次RSP的值,并预测返回指针的位置,并建立RtlCaptureContext需要的上下文.第一条ROP的RETN位置放在XMM13寄存器.

设置辅助线程的优先级为最高并分离当前线程与创建的辅助线程,让它们在不同的CPU上执行,接着获取了一些内核函数的地址.

1526885441041574.png

给线程设置两个异常,一个在SS段寄存器为读写异常,另一个写异常位于结构_KPRCB→ProcessorState(_KPROCESSOR_STATE)→SpecialRegisters(_KSPECIAL_REGISTERS)→Cr8.

WinDBG输入u KeBugCheckEx,红色部分,如果偏移不一样则需要修复这个结构.

开始填充rop到XMM寄存器.

最后读取gs段寄存器基址,重设基址指向自己填充的_KPCR结构,然后触发漏洞,恢复GS段寄存器,并使用SS段寄存器去触发漏洞.

先使用mov ss指令切换堆栈,并且会产生前面的ss调试异常,但mov ss切换后不会立即发送异常要等下一条指令执行完成后才发送;紧接着是一个int 3指令,由于前面会改变了gs段寄存器的基址,进入内核之后要使用SWAPGS指令交换gs段寄存器.

可能需要修复的一些数据偏移,根据不同的系统偏移不同.

小结

由于无法使用WinDBG对本机进行内核调试验证,修复POC里面的数据偏移,如果数据偏移没有修改正确就测试POC,可能会造成系统重启.快点自己动手试下吧.

标签: 漏洞分析

作者:pig 来源:黑客视界 浏览量:1385 时间: 2018-05-21

加入墨者学院可立即获得20墨币

可免费开启靶场环境

点击注册领取

已有账号,立即登录