首页
社区
课程
招聘
[翻译]Windows exploit开发系列教程第十八部分:内核利用程序之RS2 Bitmap巫术
发表于: 2018-3-19 19:27 15447

[翻译]Windows exploit开发系列教程第十八部分:内核利用程序之RS2 Bitmap巫术

2018-3-19 19:27
15447

点击查看原文

欢迎来到另一个Windows内核exp开发系列!自上次一别,已是三秋。我已然在我的 PSKernel-Primitives repo上开源了一些精炼的代码,所以如果你对PowerShell内核pwn感兴趣的话就点个star。

今天我们挖出最喜欢的Windows10 RS2上的Bitmap内核溯源(kernel primitive)。我们跳过RS1,此前我已经发过博文给@mwrlabs,其中详述了如何在周年纪念版本中去绕过新的缓解机制,文章在这里here

我们将使用两种不同的技术来从桌面堆(desktop heap)中泄露Windows对象,此后将使用那些对象,来泄露Bitmap。我强烈推荐你先看看下面的这些资料以了解背景知识。罢了,不跟你多BB,开始吧!

Win32k Dark Composition: Attacking the Shadow part of Graphic subsystem (Peng Qui & SheFang Zhong => 360Vulcan) - here

LPE vulnerabilities exploitation on Windows 10 Anniversary Update (Drozdov Yurii & Drozdova Liudmila) - here

Morten Schenk's tweet revealing the first technique :D (@Blomster81) - here

Abusing GDI for ring0 exploit primitives: reloaded (@NicoEconomou & Diego Juarez) - here

A Tale Of Bitmaps: Leaking GDI Objects Post Windows 10 Anniversary Edition (@FuzzySec) - here

PSKernel-Primitives (@FuzzySec) - here

Windows RS2 HmValidateHandle Write-What-Where (@FuzzySec) - here

我们简单的列出微软用于防止Bitmap泄露而实现的各种缓解机制,同时会说明它们分别该如何绕过。主版本在下面给出作为参考。

Windows 10 v1511

Windows 10 RS1 v1607

Windows 10 RS2 v1703

本文中我们将讨论如何利用用户映射的桌面堆来泄露Windows对象,和此前做法相似,执行一个UAF风格的攻击来重新溯源Bitmap(primitive)!

我们想做的第一件事就是去泄露tagWND和tagCLS Window结构体的内核地址。我们将以Morten的泄露手法开始,它提供了更好的背景去理解第二部分泄露。

下面的推文给予了我们所有的细节来从用户映射的桌面堆中泄露出地址,同时也告诉了我们如何去t通过用户模式版本计算出内核模式版本(ulClientDelta)的偏移量。

看起来指向用户模式版本的指针被存在TEB.Win32ClientInfo+0x28的位置,而指向内核模式版本的指针存在该地址偏移0x28处的更远的地方。Client delta即是内核地址减去用户地址。我们可以简单的把脚本编在一起来获取这个数据。

运行POC,给出如下输出。

我们可以简单的在KD中证实。

对桌面堆的分析超出了本文的范围,更多信息可以参考here

很好,下一步就是创建一个Window对象并扫描桌面堆来找到它。微软在Windows 8之后不再公开tagWND&tagCLS的符号了,所以我们只好在Windows 7上看看 。

如我们所见,tagWND的2第一个IntPtr值是Window句柄(通过CreateWindow/Ex返回)。同时也注意到tagWND->THRDESKHEAD->pSelf是一个指向内核中tagWND的指针,我们实际上可以通过内核tagWND的地址减去用户tagWND的地址来计算出ulClientDelta。最后一点需要注意的是tagWND&tagCLS结构体在Windows 10 RS2中有一些变化。通过简单的逆向我们找到了一些变更的相对偏移。

x64 Pre RS2 (15063)

Window handle => 0x0

pSelf => 0x20

pcls => 0x98

lpszMenuNameOffset => pcls + 0x88

x64 Post RS2 (15063)

在桌面堆上找到一个Window相当的直接,从桌面堆的基地址开始我们可以读出IntPtr尺寸的值并将其与特定的Window句柄进行比较。一旦我们匹配上了句柄,就掌握了到tagWND结构体起始位置的偏移。更新POC并运行。

这种读操作没什么问题,我们立即得到了下面的反馈结果。注意到PowerShell提示符没有返回,这是因为我们需要在script退出前断下来。

在KD中用dq/db来看看我们成功计算出的所有相对偏移。

HmValidateHandle第一次被使用是在2011年 @kernelpool攥写的论文 Kernel Attacks through User-Mode Callbacks中提出的,随后不久这一技术就被用在了各种exp中,包括CVE-2016-7255 as exploited by Fancy Bear

HmValidateHandle是一个很有意思的函数,我们可以提供一个Window句柄给它,它会返回桌面堆上用户映射的tagWND对象,这简直太好用了!通过这种方式我们可以获取完整的TEB解析和爆破。唯一的问题在于HmValidateHandle并未被user32导出,所以我们需要一些技巧来获取它的地址。

从一大堆公开的解释中我们发现HmValidateHandle与导出的User32::IsMenu函数最近,让我在KD中看看。

确实不痛不痒!我们需要做的就是获取User32::IsMenu运行时地址,寻找第一个0xE8字节(call xxx)并攫取出HmValidateHandle的指针。我们可以利用下面的一段代码实现。

运行POC,本质上它给了我们和第一种泄露同样的结果,但它更为简洁。

我猜想读者的问题依然是为什么要去关心这些Window对象?我的Bitmap在哪儿?其实,Window菜单名称(lpszMenuName)和bitmap一样,都分配在同一个内核池中。我们可以分配一个较大的Window菜单名称并释放它,此后分配我们的Bitmap就可以重用这段内存了(挖坑到占坑)。这看起来有点巧妙,但是如果我们可以让菜单名称超过4kb的话,他就会用尽低熵大内存池来保证我们的UAF泄露百分百可靠。这一过程和在RS1中使用Accelerator Tables绕过是一致的。

下面的图片阐释了这一过程。

该PowerShell函数写在下方。更多的细节请参考我的 PSKernel-Primitives repo。

快速运行一下。

SURFOBJ结构体是如此的明显,我们甚至无需给出任何符号就可以说明这一次的泄露是成功的。

非常漂亮!我们挚爱的Bitmap溯源在两轮微软的缓解措施下顽强的活了下来。Bitmap提供了一个真实有效(且方便)的读写本源,它在相当宽泛的内核利用原理中都可以被用到。不可避免的,微软会继续下绊子,让我们的策略失效,但是谁又能保证在第三轮实锤面前我们不能再次绕过呢?

 
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2018-3-20 19:59 被玉涵编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (1)
雪    币: 183
活跃值: (6634)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
2
虽然没懂,但是很厉害的样子
2022-8-25 20:15
0
游客
登录 | 注册 方可回帖
返回
//