首页
社区
课程
招聘
[原创]CVE-2019-1458分析
发表于: 2020-6-23 17:22 13847

[原创]CVE-2019-1458分析

2020-6-23 17:22
13847

文章本该以倒叙的方式,从漏洞函数到调用漏洞的函数,类似于栈回溯的方式开始编写。

但是为了方便大家理解,我准备以exp的执行流程为主线开始讲解

调试过程中用到的exp地址
https://github.com/unamer/CVE-2019-1458

漏洞存在于函数为xxxPaintSwitchWindow

其交叉引用如下

xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow

要想调用xxxWrapSwitchWndProc函数,我们需要调用NtUserMessageCall

该函数原型如下

NtUserMessageCall(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType)

下面我们从NtUserMessageCall开始分析,以及怎么经过层层bypass最终调用漏洞函数

可以看到NtUserMessageCall内部会调用gapfnMessageCall表

通过控制其参数NtUserMessageCall将会调用NtUserfnINLPCREATESTRUCT,该函数内部会通过gpsi,调用gapfnMessageCall表从而调用触发漏洞的函数xxxWrapSwitchWndProc

我们可以看到该表的调用跟dwType有关系


因此可以通过控制传入dwType的值为0xE0,调用xxxWrapSwitchWndProc函数(公式:(gpsi+ 8 * ((dwType + 6) & 0x1F)+ 16))

xxxSwitchWndProc函数分析

红色部分是第一次判断,我们新创建的窗口期,fnid为0,可以顺利进入判断内部

黄色部分是第二次判断,在这里前面已经不符合要求,因此我们要进行绕过,绕过的方法为 不满足 cbwndExtra+0x128 < [gpsi+0x154]

蓝色部分是第三次判断,在这里我们可以直接将消息设置为WM_CREATE即可绕过

我们要对NtUserMessageCall进行两次调用,第一次调用目的修改wnd.fnid为2a0(过掉红色和黄色部分,蓝色部分)

黑色部分是我们最终想要得到的结果,令wnd->fnid为 0x2a0,这样我们才能走到下面的switch语句

第二次调用是目的是为了调用xxxPaintSwitchWindow函数,我们只需要让msg为0x14 或者0x3a

在对应的 Window Messages中,可以查询到

#define WM_ERASEBKGND 0x0014

xxxPaintSwitchWindow函数分析

如下图,要进行第一次bypass

我们可以通过创建一个特殊的窗口,则可以让(gpsi+0x154) = 0x130,只要事先把窗口的cbwndExtra=0x8即可bypass

走到这里,离胜利已经非常近了。我们可以看到,wndExtra的值是可以通过
SetWindowLongPtr设置的,这样我们便可以实现一个任意内存破坏的漏洞了

第一次调用NtUserMessageCall

第一次判断

[rcx+0x42]为fnid ,判断其是否为0x2a0

第二次判断 rcx为[gpsi+0x154],rax为wnd->cbwndExtra + 0x128


成功将wnd.fnid赋值为0x2a0

通过SetWindowLongPtr更改wndExtra值

红色框会设置前后,蓝色框为要设置的值

第二次调用NtUserMessageCall,在过掉几层判断之后

可以看到,实现了任意内存的破坏,成功让tagcls.cbClsExtra变成了一个比较大的数

此时我们已经通过xxxSetClassLongPtr函数进行越界写

要写的地址为pclsBase[1].pclsNext,等价于 pclsBase+sizeof_tagCLS==tagCLS+sizeof_tagCLS

该漏洞的利用方式为更改tagWND的StrName指针,搭配InternalGetWindowText 和 NtUserDefSetText即可实现任意读写,实现token的替换

因此在此处控制offset为

offset=tagWND - tagCLS - sizeof_tagCLS + off_tagWND_strName

最后的要写的地址

dwDestAddr=tagCLS+sizeof_tagCLS+offset

dwDestAddr=tagCLS+sizeof_tagCLS+tagWND - tagCLS - sizeof_tagCLS + off_tagWND_strName

dwDestAddr=tagWND + off_tagWND_strName

通过InternalGetWindowText读取

通过NtUserDefSetText实现写

exp中有封装好的函数,我们只需要根据不同的系统,构造不同的offset即可

有了任意读写,替换token已经很简单了,不再阐述

值得注意的是我们在破坏内存的时候不仅仅破坏了pcls的cbClsExtra的字段,因此要对被破坏的字段进行修复

总体来讲,该漏洞的利用还是比较简单的

exp中并没有针对2016 r2的提权。我们下面对2016 R2进行分析

左面为2016 右面 win7 的xxxSwitchWndProc 函数对比,可以看到除了偏移不一样,都可以让Fnid设置为2a0

左面为2016 右面 win7 的xxxPaintSwitchWindow函数对比

可以看到2016的会获取tagwnd 0x168偏移的位置,这里其实就是wndExtra。理论上来讲也是存在漏洞的

但是2016和win7的xxxSetWindowLongPtr函数是有区别的

2016对tagWnd的某个标志位进行了检测

只有当tagWnd的偏移0x2a的bit位为1时,才会走win7所走的流程,才会使得 tagwnd的0x168大小偏移处写上我们的数据

动态验证,将0x2a的bit位,置1 ,可以看到返回值会是tagWnd的内核地址

2016的tagWND微软已经不公开了,但是前边跟win7是一样的,可以参考如下

那么现在的关键问题就来了

是否可以在tagwnd.fnid位为0的情况下,又使bDialogWindow位为1呢?


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

收藏
免费 3
支持
分享
最新回复 (5)
雪    币: 1491
活跃值: (985)
能力值: (RANK:860 )
在线值:
发帖
回帖
粉丝
2
楼主努力下,你这篇分享没有写完,继续补充下吧
2020-6-24 16:13
0
雪    币: 2170
活跃值: (2354)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
3
仙果 楼主努力下,你这篇分享没有写完,继续补充下吧
已经分析完了,只不过后边跟了一个希望能移植到2016R2的讨论,前边是分析和利用,后边是想等等看雪的大佬,看看是否能做到这样。没人发过2016R2的利用,不确定2016R2是否能利用
2020-6-24 16:26
0
雪    币: 12846
活跃值: (7554)
能力值: ( LV13,RANK:590 )
在线值:
发帖
回帖
粉丝
4
不错
2020-6-24 21:23
0
雪    币: 2170
活跃值: (2354)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
5
王cb 不错
师傅好
2020-6-24 21:28
0
雪    币: 854
活跃值: (1253)
能力值: ( LV7,RANK:101 )
在线值:
发帖
回帖
粉丝
6
师傅,创建一个类名为#32771的窗口就会使得(gpsi+0x154) = 0x130是怎么发现的?
2021-9-13 16:40
0
游客
登录 | 注册 方可回帖
返回
//