转载

Windows内核漏洞MS15-010/CVE-2015-0057分析及利用(含EXP)

简介

早在2015年初Udi Yavo[1]发现一枚影响Windows XP到Windows 10(预览版)的Windows内核漏洞,以下为两篇有关CVE-2015-0057的分析文章,大家可以参考一二:

1.FireEye described some technical details about the Dyre malware that exploited this vulnerability [2]. 2.NCC Group explained technical solutions to achieve reliable exploitation on Windows 8.1; however they did not release any code [3].

概述

这是一个use-after-free内核漏洞,它能获取一个专属的write primitive操作,之后侵染临近的一个对象。这个yields语句可以在内核空间或者用户空间随意写入。

更准确的说,这个use-after-free触发器利用scrollbar对象,然后通过一个新的proplist对象(同样大小)替换freed对象。正是因为use-after-free,新对象的header会被一个OR primitive修改。

win32k !xxxEnableWndSBArrows+0xaa: fffff960`0036cdfe 0903      or      dword ptr [rbx],eax

该漏洞导致对象属性的最大值增加,这样你就可以将数据push到堆中。通过SetProp(HWND hWnd, LPCTSTR lpString, HANDLE hData)增加一个新属性,可以覆盖下一个对象。

在下文中,我们将使用"SetProp(hWnd, V1, V2)"来代替表示"SetProp(HWND hWnd, LPCTSTR lpString, HANDLE hData)"

在Windows 8.1 (64-bit)上利用

根据FireEye的报告,有恶意软件使用了上面我们讨论的技术侵染一个临近的菜单对象。事实上,其中有一张截图就显示了tagMENU.rgItems和tagMENU.cItems的值被覆盖。

typedef struct _MENU { …   DWORD         cItems  // number of items contained by the items array …   PITEM         rgItems // pointer to the items array … }

这在Windows XP下提供了一个write primitive操作,覆盖nt!HalDispatchTable+0×4。接下来我们根据FireEye以及NCC Group所提供的技术细节,在Windows 8.1 64-bit下尝试进行漏洞复现。

我们可能会遇到以下障碍:

1.攻击者不能完全控制每个属性中的内容;虽然V2的8个字节是被完全控制的,但是V1只能使用2个字节。 2.上面提到的_MENU结构有变化,并且由于V1的内容缺乏控制,cItems和rgItems无法完全控制 3.需要绕过ASLR和SMEP 4.需要绕过heap entry编码机制 5.最后覆盖的对象还要记得恢复,以避免当内核释放时造成崩溃

获取任意写入操作

为了替代直接覆盖rgItems和cItems,NCC Group发现并提供的一个解决方案涉及对堆的header操作和两个覆盖,以便最终控制一个窗口结构,提供一个读写primitive操作。在此我建议大家可以看看他们提供的解决方案[3],其中的技巧已经写的非常详细了。

我选择控制一个菜单对象,因为它在各种操作系统(从Windows XP到8.1)下提供了一个常见的write primitive;在这种情况下,read primitive的不足对于我们来说也就不存在问题了。

虽然Dyre恶意软件损坏cItems和rgItems,然而我们要做的仅仅只是损坏rgItems字段:

a.创建了一个item的菜单,使用CreateMenu()以及InsertMenuItem() b.使用NCC Group提供的覆盖控制方法损坏rgItems(item数组的地址) c.数组的第一个item指向rgItems包含的一个wID字段(我们使用SetMenuItemInfo()进行修改的字段),这也导致我们获得任意write primitive操作

接下来的这一步骤十分经典:nt!HalDispatchTable+8中的一个函数指针被覆盖,然后会运行的payload包括两个阶段:

1.首先恢复桌面堆(desktop heap)以及原始的函数指针 2.第二阶段是一个用于改变当前系统进程token的Shellcode

绕过heap header编码

在该利用例子中,第一个覆盖的heap header是必须要修改的,之后我们必须在第二个覆盖内容中建立一个假的heap header。这里由于Windows 8.1的heap header是由一个cookie进行编码,为进一步的了解细节,有必要去看看Chris Valasek以及Tarjei Mandt所提供的参考[9][10]

NCC Group提供的解决方案依赖于桌面堆(desktop heap)映射的只读用户区域,暴露cookie的值。

首先我们必须找到用于编码heap header的cookie,进而确认桌面堆(desktop heap)的heap base。然后我们可以获取到存储在0×80下的cookie:

nt!_HEAP    +0x000 Entry            : _HEAP_ENTRY    +0x010 SegmentSignature       : Uint4B    […]    +0x07c EncodeFlagMask        : Uint4B    +0x080 Encoding           : _HEAP_ENTRY    […]

现在就可以通过对编码字段进行xor操作解码heap header

接着解码并修改Entry后,我们只需计算新的SmallTagIndex的校验和:

Entry.SmallTagIndex = Entry.SizeLow ^ Entry.SizeHigh ^ Entry.Flags;

在中等完整性等级下绕过ASLR

在中等完整性权限下ASLR对我们来说没有啥问题:

a.使用NtQuerySystemInformation()获取ntoskrnl以及hal.dll的基地址 b.动态获取ROP小工具偏移量,之后恢复后的hal!HaliQuerySystemInformation()会分别读取ntoskrnl.exe和hal.dll

在低等完整性等级下绕过ASLR

NtQuerySystemInformation()是不能从一个低等完整性等级下进行调用的。尽管在exploit源代码中没有实现,但是Alex Ionescu [5]提出了一种使用SIDT作为内核信息泄漏的方法,该方法是不会理会完整性等级的。

绕过SMEP

早在2011年5月,为了使用户模式页面从内核中执行,Dan Rosenberg就提出了一种十分新颖的方式绕过Linux下的SMEP[6]。j00ru以及Gynvael Coldwind将这一技术进一步应用到到Windows[7]。对于该exploit,我使用了siberas在2014 pwn2own所展示的方法[8]

防止post-exploitation崩溃

为了防止post-exploitation崩溃,记得一定要清理桌面堆(desktop heap)。事实上,已损坏的对象以及hal!HaliQuerySystemInformation()的原始指针都是需要进行恢复的。

我们成功了

Windows内核漏洞MS15-010/CVE-2015-0057分析及利用(含EXP)

正文到此结束
Loading...