漏洞信息
1 漏洞简介
漏洞名称:Microsoft Windows Win32k本地提权漏洞
漏洞编号:CVE-2015-2546
漏洞类型:UAF
影响范围:Windows 7 Service Pack 1
Windows Vista SP2
Windows Server 2008 sp2
Windows Server 2008 r2 x64 sp1
CVSS3.0:N/A
CVSS2.0: 6.9
2 组件概述
win32k.sys是Windows的多用户管理的sys文件
MicrosoftWindows是美国微软(Microsoft)公司发布的一系列操作系统。kernel-modedrivers是其中的一个内核驱动管理软件。Graphics是其中的一个图形驱动器组件。MicrosoftWindows内核模式驱动程序中存在特权提升漏洞,该漏洞源于程序没有正确地处理内存中的对象。本地攻击者可利用该漏洞在内核模式下运行任意代码。
3 影响版本
Windows 7 Service Pack 1
Windows Vista SP2
Windows Server 2008 sp2
Windows Server 2008 r2 x64 sp1
4 解决方案
http://technet.microsoft.com/security/bulletin/MS15-097
漏洞复现
1 环境搭建
Windows:Windows 7 sp1 x86
win32k.sys:6.1.7601.17154
2 复现过程
获取exp,编译cpp文件,获得可执行文件。在编译过程中,只有x86的编译成功了,x64的暂未成功。
之后在靶机上面执行exp
image.png
漏洞分析
1 基本信息
● 漏洞文件:win32k.sys
● 漏洞函数:xxxMNMouseMove
● 漏洞对象:pPopupMenu
2 背景知识
在xxxMNMouseMove函数中,xxxSendMessage(pwnd, 0x1F0,…)发起了一次用户模式回调。在这次回调中,攻击者可以销毁Menu窗口,释放tagPOPUPMENU对象并占位重用。当回调返回内核之后,补丁前的xxxMNmouseMove并没有对已释放的pPopupMenu进行验证。之后pPopupMenu被传入xxxMNHideNextHierarchy,xxxMNHideNextHierarchy会对tagPOPUPMENU.spwndNextPopup发送消息,攻击者创建合适的对象占用被释放的tagPOPUPMENU内存,构造好tagPOPUPMENU.spwndNextPopup的数据,即可实现内核任意代码执行。
3 补丁对比
bindiff进行比较,可以发现在调用SendMessage函数之后增加了一层判断。
image.png
下面两图时补丁前与补丁后的IDA反汇编代码。
image.png
image.png
反汇编可以看到,第81行加了一个判断,
tagWND+0xb0处存放的是pPopupMenu的指针。这里检测回调之后tagMENUWND->pPopupMenu是否被修改,因为攻击是在回调的过程中,将shellcode写入指定的地址,回调完成后,UAF使用了这块空间,执行了shellcode。
4 漏洞分析
4.1 静态分析
4.1.1 补丁分析
分析补丁代码,我们可以追踪a2的值是怎样传递的,首先分析,a2的含义。
63行,v7的值赋值给a2,v7又是通过safe_cast_fnid_to_PMENUWND以v6当作参数来获取的。
image.png
再跟进看一下safe_cast_fnid_to_PMENUWND,该函数的作用是检查窗口对象是否为FNID_MENU(fnid = 0x29C),如果通过safe_cast_fnid_to_PMENUWND检查,则表明这的确是一个类为#32768的菜单窗口对象。
image.png
为什么通过safe_cast_fnid_to_PMENUWND检查,则表明这的确是一个类为#32768的菜单窗口对象?
在 Windows 内核中,菜单对象在屏幕中的显示通过窗口 tagWND 对象的特殊类型 #32768(MENUCLASS) 菜单窗口对象来实现,菜单窗口对象末尾的扩展区域中存储指向关联的弹出菜单 tagPOPUPMENU 对象的指针。
image.png
我们在往回看一下v6是在哪里赋值的,通过xxxMNFindWindowFromPoint来进行赋值,xxxMNFindWindowFromPoint函数的作用是得到菜单窗口对象指针ptagWND,所以v6的值是ptagWND。
image.png
所以a2的值为ptagWND。
查找资料发现:safe_cast_fnid。to_PMENUWND()的输出将会是PMENUWND结构。PMENUWND结构的定义如下:
typedef struct tagMENUWND {WND wnd;
PPOPUPMENU ppopupmenu;
} MENUWND, *PMENUWND;
那么ptagWND+176所代表的位置存放的是其pPopupMenu的指针。因此可以推断出这几句是检查回调之后tagMENUWND->pPopupMenu是否被修改。
4.1.2 漏洞触发流程
首先是通过xxxMNFindWindowFromPoint创建个窗口句柄v6,然后将v6赋值给v7,这样现在v7的值也为窗口句柄。
image.png
这里在51行做了一个判断,假如v7不为窗口类型的话,则退出,这样我们就不能在前面设置钩子来更改v7的值了。
image.png
然后在第59行给v9进行赋值,该值为v7+0xb0,代表的含义为pPopupMenu的指针,在4.1补丁分析的章节也已经推断出来了。
image.png
然后就到达了第68行,通过SendMessage进行一个异步消息的发送,之后程序进入到用户态 。
image.png
在这次回调的过程中,我们可以释放掉tagMENUWND的空间,从而销毁了tagPOPUPMENU的空间,之后我们再重新申请这一块空间fake。并将shellcode写入到这块空间中(这时,tagPOPUPMENU的所分配的堆空间和我们申请的fake的堆空间是用一块空间,这样,再次调用tagPOPUPMENU这块空间时,将会执行shellcode),当回调返回内核时,补丁前的xxxMNmouseMove并没有对已经释放掉的pPopupMenu进行验证,之后pPopupMenu被传入xxxMNHideNextHierarchy。
image.png
这里面v9的值,就是pPopupMenu,我们已经通过UAF将它所指向的那块空间的值的内存空间改变了。
image.png
这里将v9传入xxxMNHideNextHierarchy函数中,xxxMNHideNextHierarchy会对tagPOPUPMENU.spwndNextPopup发送消息。
这里面的tagPOPUPMENU.spwndNextPopup是a1+12,我们可以创建合适的对象占用被释放的tagPOPUPMENU内存,构造好tagPOPUPMENU.spwndNextPopup的数据,这样我们恶意构造的数据就可以通过SendMessage进行执行,这样就可以造成内核态任意代码执行。
image.png
4.2 动态分析
4.2.1 漏洞调试
函数调用栈:
image.png
在刚刚的静态分析也已经大致阐述了,要想达到漏洞的利用点,那么必须通过xxxMNFindWindowFromPoint得到ptagWND,并且这个ptagWND窗口对象必须是FNID_MENU(fnid = 0x29C),这样就可以走到漏洞部分。
首先在xxxMNMouseMove处下断点,然后运行exp。
image.png
这里执行过了xxxMNFindWindowFromPoint,该函数的返回值通过esi进行存取,这里esi的值为0x9d99f580。
image.png
接着向下走,在这里,将ebx赋值为dword ptr[esi+0xb0h],在上面静态分析也已经提到过,这个值是上面v9的值,这里的值为:0xfe83f988。
image.png
然后继续向下执行,这时ebx的值和dword ptr[esi+0xb0]的值是相同的。
image.png
当执行完成0x9d8b9538这条call指令时,ebx的值没有改变,但是dword ptr[esi+0xb0]所指向位置的值却为0了。
image.png
我们跟进这个xxxSendMessage中,看看里面执行了哪些操作。
image.png
image.png
之后进入xxxSendMeassageTimeout,在这里调用了hook。
image.png
进入xxxCallHook函数,在这里调用了两个函数,PhkFirstValid函数的作用是可以找到第一个钩子函数。
image.png
然后将ebx作为参数传入到xxxMNHideNextHierarchy中。
image.png
之后一路走到第二个xxxSendMessage处,跟进。
image.png
传入的值为0x5,这个值就是tagPopupMenu的值,也就是exp所占用零表空间写入的值,f构造了fake popupmenu,之后执行了shellcode。
image.png
4.2.2 补丁调试
当系统打上微软漏洞补丁之后,执行到指定位置处。
这里要分出两种情况,执行出来的结果是不一样的。
第一种
通过命令行来进行执行。
image.png
当执行到xxxMNFindWindowFromPoint函数并未执行时,eax的值为0xa1d37a94。
image.png
但是之后执行完成这个call之后,函数的返回值却是0,在上面已经分析过这个函数的路程,当这个值为0的时候是不会走到漏洞函数中去的,所以这样自然不会成功,但是这就和这个漏洞无关了。具体出现这种情况的原因不明。
image.png
第二种
直接双击程序进行运行。
还是到刚刚的那一步,这时的eax值为0xa1d97a94。
image.png
之后进行下一步,这时eax的值就不为0了,也就是说过了那个不为0的验证了,也就是说现在的逻辑有可能走到我们的漏洞逻辑。
image.png
经过调试,事实上也确实进入到了我们的漏洞逻辑,并且可以发现,后面有了微软增加的判断执行逻辑。
image.png
之后继续向下调试,按理来说MS-PATCH会将漏洞拦下,但是exp把这个判断过了。
image.png
当MS-PATCH失效,exp将执行成功时,xxxMNHideNextHierarchy的逻辑里的一步有效性验证将exp拦下了。
image.png
对应的伪代码是这种形式,其中if判断将我们的exp拦下了。
image.png
这两种exp执行的方式都和微软的MS-PATCH思路相悖。
5 EXP流程
exp思路
1. 创建一个有弹出式菜单的正常主窗口
2. 在某个固定地址Addr1分配内存,并在Addr1上构造一个fake_tag WND。其中fake_tagWND->bServerSideWindowProc置为1,fake_tagWND->lpfnWndProc指向Ring0ShellCode。
3. 用Accelerator Table对象制作出内存空洞。
4. 创建类名为”#32768”的窗口MenuWindow1,并用SetWindowLong替换其WndProc。
5. 创建消息钩子,并在HookProc中处理MN_FINDWINDOWFROMPOINT消息和MN_SETTIMERTOOPENHIERARCHY消息。
6. 向主窗口发送WM_SYSCOMMAND消息或者模拟鼠标事件。
7. 系统创建的正常菜单窗口收到MN_FINDWINDOWFROMPOINT消息,返回MenuWindow1的句柄。
8. HookProc收到MN_SETTIMERTOOPENHIERARCHY消息,销毁MenuWindow1,并创建Accelerator Table对象占用tagPOPUPMENU释放的内存。
9. Fake_tagWND收到0x1E4消息,执行Ring0ShellCode。