CVE-2014-4113本地提权漏洞分析

by Netfairy - 2016-02-24
CVE-2014-4113本地提权漏洞分析
By Netfairy
前言

2014年10月14日, Crowdstrike和FireEye发表了一篇文章, 描述了一个新的针对Windows的提权漏洞. Crowdstrike的文章表明:这一新漏洞是在追踪一个高度先进攻击团队飓风熊猫(HURRICANE PANDA)发现的, 这之前, 飓风熊猫利用此漏洞进行攻击已至少五个月了.原文http://www.crowdstrike.com/blog/crowdstrike-discovers-use-64-bit-zero-day-privilege-escalation-exploit-cve-2014-4113-hurricane-panda/. 通用漏洞发布平台随后披露了这个漏洞的部分细节, 该漏洞的CVE编号为CVE-2014-4113. 报告指出:Microsoft Windows下的 win32k.sys是Windows子系统的内核部分,是一个内核模式设备驱动程序,它包含有窗口管理器、后者控制窗口显示和管理屏幕输出等。如果Windows内核模式驱动程序不正确地处理内存中的对象,则存在一个特权提升漏洞。成功利用此漏洞的攻击者可以运行内核模式中的任意代码。攻击者随后可安装程序;查看、更改或删除数据;或者创建拥有完全管理权限的新帐户。


环境
操作系统:windows XP SP3
漏洞文件:Win32k.sys 5.1.2600.6514
调试器:Windbg 6.11.1.404 && Olldbg &&IDA6.6
漏洞验证
我在网上拿到了POC, 这个POC包含了两个文件:Win32.exe和Win64.exe
 image1.png
先验证漏洞是否存在, 打开控制台, 直接执行calc.exe
 image2.png
Calc.exe进程属于用户netfairy,然后在转到Win32.exe所在目录,,执行Win32.exe calc.exe
 image3.png

再看此时的calc.exe进程,是以SYSTEM权限运行的,所以这个win32.exe在XP SP3 下提权成功.


漏洞分析
粗略分析Win32.exe执行流
在调试机器用OD载入win32.exe并添加参数calc.exe. 程序进入主函数后调用的第一个函数是
 image4.png
OD对符号的识别不太好,用IDA反汇编这个函数
image5.png 
蓝色部分是程序的执行流程。程序先调用
GetVersionExA
获取系统版本信息,下面就cmp     [ebp+VersionInformation.dwMajorVersion], 5 等等进行一系列的比较。所以可以看看出这个函数的作用就是获取目标系统版本并比较决定是否进行下一步的攻击。接着main函数调用的第二个函数是
 image6.png
这个函数有点大,就不全贴出来了。总之这个函数首先调用LoadLibraryA加载ntdll.dll,接着调用GetProcAddress得到QuerySystemInformation地址函数的地址,接着调用LoadLibraryA得到ntkrnlpa.exe的地址,接着调用GetProcAddress得到LookupProcessByProcessId的地址,最后调用GetCurrentProcessId获得当前进程的PID。
继续执行,main函数调用的第三个函数是
 image7.png
一开始我就猜想这个函数创建的线程应该就是触发漏洞提权的用的。因为Main在这之后就是一个CreateProcessA调用,显然是执行shellcode的。跟进CreateThread看看,这个线程调用函数次序如下:
 image8.png
现在我们还不清楚漏洞是怎么触发的,继续往下执行,main函数调用CreateProcessA
 image9.png
通过传入的命令行参数创建一个新的进程,该进程和win32.exe有相同的权限。如果前面一步提权成功,那么新创建的进程将拥有system权限。总结一下main函数执行流程以及每个函数的作用如下
 image10.png

要分析这个漏洞,只需要分析call    ds:CreateThread ; //触发漏洞,提权 就好。


IDA&&Windbg漏洞详细分析
静态分析
先设置加载win32.exe时调试器中断下来
 image11.png
然后bp $exentry断在模块入口点,如图
 image12.png
但这里不是main函数入口点,继续单步
 image13.png
这个就是main函数了。然后跟进main函数。下面不一一说。直接来到关键函数
 image14.png
 image15.png
继续往下执行,TrackPopupMenu执行前
 image16.png
TrackPopupMenu执行后
 image17.png
可见是由TrackPopupMenu最终触发了漏洞,这是从用户层看到的,下面跟踪TrackPopupMenu对应的内核函数win32k!xxxTrackPopupMenuEx
 image18.png
1. 在win32k!xxxTrackPopupMenuEx中进行一些列处理后,最终会调用win32k! xxxMNLoop
 image19.png
2. 跟进win32k! xxxMNLoop,函数又会调用win32k!xxxHandleMenuMessages
 image20.png
3. 继续跟进win32k!xxxHandleMenuMessages,其会事先调win32k! xxxMNFindWindowFromPoint得到win32k! xxxSendMessage需要的菜单窗口对象指针ptagWND,之后再调用win32k! xxxSendMessage给菜单窗口发送消息。
4. 跟进wn32k! xxxMNFindWindowFromPoint,函数首先调用win32k! xxxSendMessage给菜单窗口发送0x1EB(MN_FINDWINDOWFROMPOINT)消息
 image21.png
在应用层,程序调用SetWindowsHookExA安装了一个一个钩子类型为4,也就是WH_CALLWNDPROC类型的钩子(SetWindowsHookExA与钩子类型见:https://msdn.microsoft.com/en-us/library/ms644990%28VS.85%29.aspx?f=255&MSPPError=-2147217396),应用层会拦截内核发来的0x1EB消息,钩子处理函数如下
 image22.png
跟进SetWindowLongA处理函数,函数最终会调用EndMenu销毁菜单窗口并返回0xFFFFFFFB
 image23.png
应用层代码返回0xFFFFFFFB,执行流返回到内核win32k! xxxMNFindWindowFromPoint上下文
 image24.png
此时的eax应该为0xFFFFFFFB(一会我们调试的时候会看到),函数接着往下执行,最终0xFFFFFFFB会做为win32k! xxxMNFindWindowFromPoint的返回值
5. win32k! xxxMNFindWindowFromPoint执行完返回到win32k!xxxHandleMenuMessages上下文,对于前边win32k! xxxMNFindWindowFromPoint的返回值0xFFFFFFB,程序没有严格校验它的返回值ptagWND,导致后边把0xFFFFFFFB作为ptagWND传给win32k! xxxSendMessage
 image25.png
而不幸的是win32k! xxxSendMessage又会调用ptagWND+0x60处的函数,也就是
call [0xFFFFFFB+0x60],即call [0x5B]。并且应用层会事先调用ZwAllocateVirtualMemory在0地址分配内存并在0x5B处写入shelllcode的地址。
 image26.png
最后,结合前面的分析。给出一份直观的程序流程图

image27.png 


动态调试
动态调试需要在应用程和内核层同时下断:
应用层:
004012E2 call    dword_40EBCC    ; ntdll!ZwAllocateVirtualMemory
004015FE call    ds:TrackPopupMenu
 image28.png
内核层:
BF938E11 call    xxxMNFindWindowFromPoint@12
BF9392F6 call    xxxSendMessage@16
 image29.png
其中poi(poi(poi(fs:0x124)+0x44)+0x84)代表当前执行进程的pid(win32.exe的pid为0xba4),要进行pid判断是否为win32.exe进程,不然调试器老是断在别的进程。
1. 调用ZwAllocateVirtualMemory前
 image30.png
调用后执行完
004012fb e876050000      call    image00400000+0x1876 (00401876)
 image31.png
可以看到0x5b保存了提权的shellcode的 地址
……
00401830 55              push    ebp
00401831 8bec            mov     ebp,esp
00401833 83ec08          sub     esp,8
00401836 60              pushad
……
2. 继续运行到
 image32.png
控制权转到内核
3. 转到内核层,程序断在win32k! xxxMNFindWindowFromPoint
 image33.png
4. win32k! xxxMNFindWindowFromPoint又会调用win32k! xxxSendMessage
image34.png
 
5. 最终win32k! xxxMNFindWindowFromPoint的返回值是0xFFFFFFFB
 image35.png
6. 最终程序会转到0x5B保存的0x00401830执行
 image36.png
记住,此时处于内核层,所以程序可以执行所有特权指令。当然,你完全可以直接在这里执行shellcode。Win32.exe把提权代码放到这里,可以把程序的权限提升至system,然后转到用户层
004017EE call    ds:CreateProcessA  
由CreateProcess创建的calc.exe自然也是system权限了。

 image37.png


总结

有对函数返回值进行进一步利用的,如call [retn+0xXX],一定要严格校验返回值。以免攻击者构造恶意的代码控制返回值,进行进一步的漏洞利用。


参考
Windows内核提权漏洞CVE-2014-4113分析报告:http://drops.wooyun.org/papers/3324
CVE-2014-4113漏洞利用过程分析:http://drops.wooyun.org/papers/3331

CrowdStrike Discovers Use of 64-bit Zero-Day Privilege Escalation Exploit (CVE-2014-4113):http://www.crowdstrike.com/blog/crowdstrike-discovers-use-64-bit-zero-day-privilege-escalation-exploit-cve-2014-4113-hurricane-panda/