漏洞名称
: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
win32k.sys是Windows的多用户管理的sys文件
MicrosoftWindows是美国微软(Microsoft)公司发布的一系列操作系统。kernel-modedrivers是其中的一个内核驱动管理软件。Graphics是其中的一个图形驱动器组件。MicrosoftWindows内核模式驱动程序中存在特权提升漏洞,该漏洞源于程序没有正确地处理内存中的对象。本地攻击者可利用该漏洞在内核模式下运行任意代码。
Windows 7 Service Pack 1
Windows Vista SP2
Windows Server 2008 sp2
Windows Server 2008 r2 x64 sp1
http://technet.microsoft.com/security/bulletin/MS15-097
Windows:Windows 7 sp1 x86
win32k.sys:6.1.7601.17154
获取exp,编译cpp文件,获得可执行文件。在编译过程中,只有x86的编译成功了,x64的暂未成功。
之后在靶机上面执行exp
● 漏洞文件:win32k.sys
● 漏洞函数:xxxMNMouseMove
● 漏洞对象:pPopupMenu
在xxxMNMouseMove函数中,xxxSendMessage(pwnd, 0x1F0,…)发起了一次用户模式回调。在这次回调中,攻击者可以销毁Menu窗口,释放tagPOPUPMENU对象并占位重用。当回调返回内核之后,补丁前的xxxMNmouseMove并没有对已释放的pPopupMenu进行验证。之后pPopupMenu被传入xxxMNHideNextHierarchy,xxxMNHideNextHierarchy会对tagPOPUPMENU.spwndNextPopup发送消息,攻击者创建合适的对象占用被释放的tagPOPUPMENU内存,构造好tagPOPUPMENU.spwndNextPopup的数据,即可实现内核任意代码执行。
bindiff进行比较,可以发现在调用SendMessage函数之后增加了一层判断。
反汇编可以看到,第81行加了一个判断,
tagWND+0xb0处存放的是pPopupMenu的指针。这里检测回调之后tagMENUWND->pPopupMenu是否被修改,因为攻击是在回调的过程中,将shellcode写入指定的地址,回调完成后,UAF使用了这块空间,执行了shellcode。
分析补丁代码,我们可以追踪a2的值是怎样传递的,首先分析,a2的含义。
63行,v7的值赋值给a2,v7又是通过safe_cast_fnid_to_PMENUWND以v6当作参数来获取的。
再跟进看一下safe_cast_fnid_to_PMENUWND,该函数的作用是检查窗口对象是否为FNID_MENU(fnid = 0x29C),如果通过safe_cast_fnid_to_PMENUWND检查,则表明这的确是一个类为#32768的菜单窗口对象。
为什么通过safe_cast_fnid_to_PMENUWND检查,则表明这的确是一个类为#32768的菜单窗口对象?
在 Windows 内核中,菜单对象在屏幕中的显示通过窗口 tagWND 对象的特殊类型 #32768(MENUCLASS) 菜单窗口对象来实现,菜单窗口对象末尾的扩展区域中存储指向关联的弹出菜单 tagPOPUPMENU 对象的指针。
我们在往回看一下v6是在哪里赋值的,通过xxxMNFindWindowFromPoint来进行赋值,xxxMNFindWindowFromPoint函数的作用是得到菜单窗口对象指针ptagWND,所以v6的值是ptagWND。
查找资料发现:safe_cast_fnid。to_PMENUWND()的输出将会是PMENUWND结构。PMENUWND结构的定义如下:
typedef struct tagMENUWND {
WND wnd;
PPOPUPMENU ppopupmenu;
} MENUWND, *PMENUWND;
那么ptagWND+176所代表的位置存放的是其pPopupMenu的指针。因此可以推断出这几句是检查回调之后tagMENUWND->pPopupMenu是否被修改。
首先是通过xxxMNFindWindowFromPoint创建个窗口句柄v6,然后将v6赋值给v7,这样现在v7的值也为窗口句柄。
这里在51行做了一个判断,假如v7不为窗口类型的话,则退出,这样我们就不能在前面设置钩子来更改v7的值了。
然后在第59行给v9进行赋值,该值为v7+0xb0,代表的含义为pPopupMenu的指针,在4.1补丁分析的章节也已经推断出来了。
然后就到达了第68行,通过SendMessage进行一个异步消息的发送,之后程序进入到用户态 。
在这次回调的过程中,我们可以释放掉tagMENUWND的空间,从而销毁了tagPOPUPMENU的空间,之后我们再重新申请这一块空间fake。并将shellcode写入到这块空间中(这时,tagPOPUPMENU的所分配的堆空间和我们申请的fake的堆空间是用一块空间,这样,再次调用tagPOPUPMENU这块空间时,将会执行shellcode),当回调返回内核时,补丁前的xxxMNmouseMove并没有对已经释放掉的pPopupMenu进行验证,之后pPopupMenu被传入xxxMNHideNextHierarchy。
这里面v9的值,就是pPopupMenu,我们已经通过UAF将它所指向的那块空间的值的内存空间改变了。
这里将v9传入xxxMNHideNextHierarchy函数中,xxxMNHideNextHierarchy会对tagPOPUPMENU.spwndNextPopup发送消息。
这里面的tagPOPUPMENU.spwndNextPopup是a1+12,我们可以创建合适的对象占用被释放的tagPOPUPMENU内存,构造好tagPOPUPMENU.spwndNextPopup的数据,这样我们恶意构造的数据就可以通过SendMessage进行执行,这样就可以造成内核态任意代码执行。
在刚刚的静态分析也已经大致阐述了,要想达到漏洞的利用点,那么必须通过xxxMNFindWindowFromPoint得到ptagWND,并且这个ptagWND窗口对象必须是FNID_MENU(fnid = 0x29C),这样就可以走到漏洞部分。
首先在xxxMNMouseMove处下断点,然后运行exp。
这里执行过了xxxMNFindWindowFromPoint,该函数的返回值通过esi进行存取,这里esi的值为
0x9d99f580
。
接着向下走,在这里,将ebx赋值为dword ptr[esi+0xb0h],在上面静态分析也已经提到过,这个值是上面v9的值,这里的值为:
0xfe83f988
。
然后继续向下执行,这时ebx的值和dword ptr[esi+0xb0]的值是相同的。
当执行完成0x9d8b9538这条call指令时,ebx的值没有改变,但是dword ptr[esi+0xb0]所指向位置的值却为0了。
我们跟进这个xxxSendMessage中,看看里面执行了哪些操作。
之后进入xxxSendMeassageTimeout,在这里调用了hook。
进入xxxCallHook函数,在这里调用了两个函数,PhkFirstValid函数的作用是可以找到第一个钩子函数。
然后将ebx作为参数传入到xxxMNHideNextHierarchy中。
之后一路走到第二个xxxSendMessage处,跟进。
传入的值为0x5,这个值就是tagPopupMenu的值,也就是exp所占用零表空间写入的值,f构造了fake popupmenu,之后执行了shellcode。
当系统打上微软漏洞补丁之后,执行到指定位置处。
这里要分出两种情况,执行出来的结果是不一样的。
第一种
通过命令行来进行执行。
当执行到xxxMNFindWindowFromPoint函数并未执行时,eax的值为0xa1d37a94。