首页
社区
课程
招聘
[原创]cve-2018-8453分析及利用EXP编写
发表于: 2019-1-18 15:34 14419

[原创]cve-2018-8453分析及利用EXP编写

2019-1-18 15:34
14419

在2018年11月份的时候,请论坛里各位请教了这个漏洞

下载该漏洞的单独补丁用bindiff与历史补丁进行查看,可发现主要变化如下(新旧变化主要用上一个补丁日的win32k.sys来比较,下同):


在底部,看到颜色差异比较大的就是一个叫NtUserSetWindowFNID的函数,比较一下:



可见判断流程中,多了一个IsWindowBeingDestroyed函数调用:


也就是说,主要在设置改变窗口的一个成员时,多了一个检查。那么就意味着,本漏洞的原因是设置成员时,没有判断某成员造成,从名字上看,这个成员为FNID。

那么,问题在于,FNID这个成员没有检查又会造成什么影响呢?我们查看一下这个成员的作用,在win2000的部分源代码中,我们可以搜索FNID来探明FNID是什么意义。


这个FNID成员是用来标识本窗口是一个什么样的窗口,比如是一个按钮还是一个编辑框,这一点从文章里也可以印证。而从补丁修改后新加的函数名IsWindowBeingDestroyed来看,这里是要判断本窗口是否已经准备删除了。从文章中说的查看ReactOS代码,可知道准备删除标记就是添加上FNID_FREE(0x8000)的标记。关键在于,不检查FREE之后的窗口是如何触发漏洞呢?

通过卡巴的文章,我们整理出来大致利用思路:代码先HOOK KernelCallbackTable->产生一个主窗口->在USER32!__fnINLPCREATESTRUCT回调中去查找并取消掉sysShadow窗口->以主窗口作为父窗口产生一个滚动条窗口SrollBar->发送WM_LBUTTONDOWN消息->系统处理消息时会发生USER32!__fnDWORD回调,在USER32!__fnDWORD回调中销毁主窗口->这将导致主窗口销毁从而产生USER32!__fnNCDESTROY回调->USER32!__fnNCDESTROY回调中调用NtUserSetWindowFNID更改掉FNID->至此文章中开始语焉不详,文章中说重用了sysShadow,但我们根本理解不了如何发生得重用。所以需要我们自己来动手实现。

首先我们来实现漏洞函数得调用,仔细观察:


可以看到,要想成功更改FNID,需要满足几个条件,我们不可能只设置为0x4000(这个只是打个标记,不产生实际作用)。至于新FNID得值,我们可以按照文章中说的直接设置为0x2a1即可。而对于后面的条件,要求我们要设置的窗口原来不能有FNID(除0x4000和0x8000外,但这两个标记我们打了没用)。这里经过多次测试,发现三种情况会时FNID为空:一种是在任意类型窗口刚建立时,这时系统在用户态主动调用NtUserSetWindowFNID来设置FNID(user32.dll中自动实现),而此时,如果没有设置完FNID,则窗口还没有设置消息处理函数,也就没有处理消息的能力。而文章中提到了WM_LBUTTONDOWN消息,则可以肯定是在Scrollbar窗口完全创建之后。故此种情况不行。二种是用户注册的窗口类所产生的窗口,此窗口一直到销毁,都没有设置FNID。第三种就是文章中所说的sysShadow窗口,此窗口的作用只是产生阴影效果,但是确实FNID为空。也正是由于这个特性,本人被文章误导很长时间。后来请教leeqwind才知道,根本不是重用的sysShadow,而是SBTrack结构。另外也可以看文章截图:


由于本人注意力全放在了文章触发中,还未关注利用,没注意后面的内容,其实这里已经泄露了真相(深刻检讨反思!)。从截图的红框中可看到,标记是Usst,分配者又是win32k!xxxSBTrackInit。所以很明显可知要被重用的是SBTrack。

文章中说明了需要在FNID设置为0x8000之后,再调用漏洞函数更改FNID。我们知道,一个窗口销毁的用户态接受到的最后消息是WM_NCDESTORY,在win32k中,这是在xxxFreeWindow函数中发送给窗口的:


可以看到在106行发送了0x82(WM_NCDESTORY)号消息,所以我们需要在106行之后想办法回到用户态。但同时有另外一个问题,就是注意第134行,这行把FNID打上0x4000的标记,而文章中完全是0x8000直接变成了0x82a1,没有0x4000的标记,所以我们如果再WM_NCDESTORY消息中去更改FNID,那么确实可以马上更改掉FNID,但是这时窗口还并没有打上0x8000的标记(到136行中才被标记),这与文章明显不符。所以文中所说的在USER32!__fnNCDESTROY中去调用NtUserSetWindowFNID更改FNID的做法为故意错误。


经过本人用pykd动态测试发现,窗口在426行的调用后,窗口句柄将不存在,NtUserSetWindowFNID函数的ValidateHwnd函数将返回0从而直接跳过FNID设置。也就是我们想要实现文章中直接将0x8000设置成0x82a1的效果,需要我们在134行到426行之间,找到一个回到用户态的调用。

这里插播一点题外话。本人一开始是在WINDOWS7的win32k.sys做分析,结果搜索很长时间未能成功找到,直到某天twitter上有人提到该漏洞在win8.1之后可利用,在看了win10的win32kfull.sys后恍然大悟,教训深刻!

回到正题,之后我们可以看到这里:


上面这张截图为win10下的win32kfull.sys的IDA分析结果。我们可以看到,在256行改为了0x8000后,在266行有一个xxxClientFreeWindowClassExtraBytes函数调用:


该函数中很直接的调用了KeUserModeCallback!毫无条件的直接回到了用户态。所以我们只要符合进入到xxxClientFreeWindowClassExtraBytes函数的条件即可。仔细查看代码,发现258行的判断,主要是判断窗口是否具有扩展字节(正如xxxClientFreeWindowClassExtraBytes函数名字所暗示的那样),有的话则调用xxxClientFreeWindowClassExtraBytes函数释放掉,由于扩展字节是分配在用户空间中的,所以该函数返回到用户态让用户态代码去释放掉(至少要通知)。所以只要我们在注册窗口类的时候,cbWndExtra成员不为0即可。在窗口销毁时,就会在设置了0x8000之后,又回到了用户态。当窗口以0x8000回到用户态后,我们更改FNID为0x82a1,返回到内核态后,xxxFreeWindow继续往后执行。

回到xxxFreeWindow函数:


其中这里,可以看到代码判断了FNID的值,从而决定要不要调用USER32!__fnDWORD。我们知道,当一个窗口被多个其他窗口、结构引用时,即时这个窗口已经被用户调用DestoryWindow销毁掉了,窗口对象也要在内存中继续存在,以等待所有引用它的地方不再引用它才真正释放本对象内存。那么,如果我们在销毁了一个窗口后,它的最后一个引用也释放的时候,调用xxxFreeWindow时,我们就可以用FNID来控制流程是否要回到用户态的USER32!__fnDWORD调用。所以攻击链也就此完整。

结合上面提到的,文章中提到了使用xxxSBTrackInit。该函数主要用来实现滚动条按钮的跟随鼠标滚动,当用户在一个滚动条上按下左键,表示用户想要拖动滚动条,此时需要开始处理鼠标的移动,让滚动条也跟着相应动起来,在系统中,产生SBTrack结构来标记用户鼠标的当前位置,最后当用户放开鼠标左键时,表示用户已经拖动完成,需要释放相应SBTrack结构。

在windows 2000的源代码中,xxxSBTrackInit部分代码如下:


大致流程就是在调用UserAllocPoolWithQuota申请了内存后,初始化SBtrack,会将滚动条窗口以及通知窗口的指针放在本结构中,然后在2425行将当前窗口设置为捕获窗口。之后就调用xxxSBTrackLoop开始循环来处理用户的鼠标消息:


可以看到,xxxSBTrackLoop循环获取消息、判断消息、分发消息。当用户放开鼠标时,应当停止跟踪处理消息,退出xxxSBTrackLoop后回到xxxSBTrackInit之后,释放SBTrack占用的内存:


而往上两行,可以看到在释放SBTrack之前,会解除一次spwndSBNotify窗口的引用。结合上面的分析,我们可以让这次解除引用时,回到用户态。如果在用户态释放掉SBTrack,则流程再次回到内核时,紧接着后面的UserFreePool即造成重复释放的问题。

那么我们在用户态如何释放SBTrack呢?分析发现,导致释放SBTrack一种是用户正常放开了鼠标左键,还有一种就是xxxEndScroll函数:



xxxEndScroll函数判断了主要根据窗口的线程信息中存放的SBTrack和pq->sqpwndCapture()。


而我们的程序是单线程,由于每个线程信息是属于线程的,所以线程创建的所有窗口也都指向同一线程信息结构。所以,即使SBTrack所属于的Scrollbar窗口已经释放了,只要还是同一线程创建的新窗口,pSBTrack也还是原来的。而qp->spwndCapture==pwnd如何绕过呢?我们如果创建新的窗口,给这个新窗口发送的消息和操作,pwnd则为新窗口,这显然不会等于在xxxSBTrackInit中设置的捕获窗口----旧窗口。

通过测试发现,这个Capture窗口的设置,只要简单的在用户态调用SetCapture API即可直接设置。所以我们只要直接调用API即可让xxxEndScroll中的判断完全通过。

在搜索之后,发现可以通过如下路径调用xxxEndScroll函数:



向一个窗口发送WM_CANCELMODE-> xxxDefWindowProc判断消息->调用xxxDWP_DoCancelMode-> xxxDWP_DoCancelMode判断当前线程信息中pSBTrack-> xxxEndScroll。而上面我们知道,所有的窗口都在同一线程中创建,所以这里的判断也可以通过!


整理一下流程:

HOOK KernelCallbackTable->注册窗口类,WNDCLASSEXW.cbWndExtra设置为4->产生主窗口->以主窗口作为父窗口产生一个滚动条窗口SrollBar->发送WM_LBUTTONDOWN消息->系统处理消息初始化SBTrack结构并开始循环->发生fnDWORD回调,fnDWORD回调中销毁主窗口->销毁主窗口,释放扩展字节xxxClientFreeWindowClassExtraBytes->xxxClientFreeWindowClassExtraBytes系统调用回调fnClientFreeWindowClassExtraBytesCallBack->fnClientFreeWindowClassExtraBytesCallBack HOOK中调用NtUserSetWindowFNID更改掉窗口FNID->创建新窗口并调用SetCapture设置新窗口为捕获窗口->xxxSBLoop返回后解除主窗口引用->由于这是主窗口唯一的一个引用,这次解除导致彻底释放主窗口对象,xxxFreeWindow函数执行->由于主窗口对象的FNID已经被更改,xxxFreeWindow函数执行过程中将再一次回到用户态->用户态向新窗口发送WM_CANCELMODE消息->系统处理WM_CANCELMODE消息,释放了SBTrack->流程返回到内核继续执行xxxSBTrackInit函数最后的释放SBTrack->重复释放SBTrack!


值得说明的一点是:在上面这个流程中,完全跟sysShadow窗口没有关系,自然也跟本不需要HOOK __fnINLPCREATESTRUCT回调。


下面看一下具体代码实现:

首先,我们设置一下回调HOOK,这里就直接用fs来获取PEB了:


创建主窗口及ScrollBar:



这将导致系统初始化SBTrack并开始循环。这导致系统回调fnDWORD:



由于在运行过程中,DWORD回调会执行很多次,所以我们加一个全局变量bMSGSENT来控制。在系统执行DestroyWindow时,由于已经预留了扩展字节,所以会回调到用户HOOK:



我们在fnClientFreeWindowClassExtraBytes回调中,直接设置FNID。由于后面还有捕获窗口的检查,所以我们一并创建窗口并且设置为捕获窗口。当流程回到系统后,发现捕获窗口已经改变,退出了xxxSBTrackLoop函数并开始释放SBTrack内存空间,在解除对主窗口的引用时,会导致调用xxxFreeWindow释放主窗口内存对象,由于我们已经改变了FNID,所以再次回到用户态。此时消息为0x70:


所以在fnDWORD中,判断消息:



WM_CANCELMODE将导致SBTrack被释放,从用户态返回后,xxxSBTrack继续释放SBTrack将导致重复释放!


      最后:非常感谢leeqwind的帮助!在分析过程中给了很大的帮助!再次感谢!极力推荐他的博客:https://xiaodaozhi.com/

在CVE-2018-8453分析得上篇中--[上篇]从补丁diff到EXP--CVE-2018-8453漏洞分析与利用,我们已经分析了漏洞成因,知道它是一个double-free类型的漏洞。本篇中,我们将利用这个漏洞获取SYSTEM最高权限。

我们知道,双重释放类型的漏洞,可以给我们一个多释放一次内存的机会。通过上次分析,我们发现释放的目标内存—SBTrack仅仅0x50大小,无论是利用Bitmap还是Palette对象,空间都严重不够:


这里为了方便已经在SBTrack申请的时候,直接断点并将内存地址赋值给了伪寄存器$t0(下同):

ba e 1 win32kfull!xxxSBTrackInit+0x59 ".echo SBTrack INIT;r eax;r @$t0=@eax;g";

由于空间根本不够我们去部署一个GDI对象,所以需要想办法扩展这个空间。在内存管理中,对于这种动态划分的内存块,无法预知大小,就是用户态的堆、内核态的池。所以给了我们一个可利用的机会,就是虽然申请时是0x50大小,但释放时只是按照内存管理机制来释放,没有判断内存大小释放已经改变的方法。所以我们可以在第二次释放前,可以让这块内存为任意大小,也就是在二次释放前,我们重新申请到这块内存,并且申请时是一个足以容纳Palette的大小(由于bitmap在win 10 RS2中已经更改修复,所以我们选择Palette对象)。从而实现扩展目标内存大小的目的,我们想要达到如下效果:


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

最后于 2019-1-18 19:04 被bksaro编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (14)
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
方便直接分享在论坛一份?
2019-1-18 15:38
1
雪    币: 1243
活跃值: (347)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
3
Editor 方便直接分享在论坛一份?
已复制过来了。。
最后于 2019-1-18 15:45 被bksaro编辑 ,原因:
2019-1-18 15:41
0
雪    币: 3715
活跃值: (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
666学习了。大佬
2019-1-18 15:55
0
雪    币: 6112
活跃值: (1212)
能力值: (RANK:30 )
在线值:
发帖
回帖
粉丝
5
bksaro Editor 方便直接分享在论坛一份? 已复制过来了。。
老铁,图片没过来....建议上传快捷键截图,上传本地一份
2019-1-18 16:18
0
雪    币: 1243
活跃值: (347)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
CCkicker 老铁,图片没过来....建议上传快捷键截图,上传本地一份
已更~
2019-1-18 16:29
0
雪    币: 6112
活跃值: (1212)
能力值: (RANK:30 )
在线值:
发帖
回帖
粉丝
7
bksaro 已更~
......
微信公众号的图片 是加了锁的,我们看到的都是这样的啊,直接复制是木有用的。。。

2019-1-18 16:37
0
雪    币: 5633
活跃值: (7199)
能力值: ( LV15,RANK:531 )
在线值:
发帖
回帖
粉丝
8
很强,学习了
2019-1-18 16:55
0
雪    币: 3715
活跃值: (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9

0.0

最后于 2020-7-7 16:51 被Aimeier编辑 ,原因:
2019-1-18 17:09
0
雪    币: 1243
活跃值: (347)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
10
已手工更!看看还有缺的图片么。
2019-1-18 17:57
0
雪    币: 1243
活跃值: (347)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
Aimeier 老哥,给个你测试镜像的链接啊。我的镜像测试不成功
我记得应该是这个:ed2k://|file|cn_windows_10_multiple_editions_version_1607_updated_jul_2016_x86_dvd_9060050.iso|3275581440|006F76229DB61ADA1D8E49DD4A6A0A5D|/ 
或者
ed2k://|file|en_windows_10_education_version_1511_x86_dvd_7223868.iso|2857314304|80A3D4255DEB1565FA950553F6F8646E|/
2019-1-18 18:00
0
雪    币: 292
活跃值: (810)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
12
感谢大佬分享
2019-1-18 19:05
0
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
很强 楼主的头像可以说是很皮了
2019-1-21 09:48
0
雪    币: 241
活跃值: (33)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
老哥,一个漏洞有时候可能用到好几个系统文件,那你怎么快速确定漏洞补丁相关代码在哪儿呢?因为有时候补丁代码并不是在改动很大的函数里边
2019-1-24 12:54
0
雪    币: 34
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
老哥 你有CVE-2019-0623分享一下呀
2019-4-1 05:04
0
游客
登录 | 注册 方可回帖
返回
//