首页
论坛
课程
招聘
[原创]CVE-2019-1458分析
2020-6-23 17:22 3904

[原创]CVE-2019-1458分析

2020-6-23 17:22
3904

目录

CVE-2019-1458分析笔记

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

 

但是为了方便大家理解,我准备以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

    printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n");
    HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hself, NULL);

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

 

EXP调试分析笔记

第一次调用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的字段,因此要对被破坏的字段进行修复

 

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

移植到windows 2016R2的讨论

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呢?


安卓应用层抓包通杀脚本发布!《高研班》2021年6月班开始招生!

收藏
点赞1
打赏
分享
最新回复 (4)
雪    币: 1396
活跃值: 活跃值 (314)
能力值: (RANK:860 )
在线值:
发帖
回帖
粉丝
仙果 活跃值 19 2020-6-24 16:13
2
0
楼主努力下,你这篇分享没有写完,继续补充下吧
雪    币: 4616
活跃值: 活跃值 (1601)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
与时尽现。 活跃值 6 2020-6-24 16:26
3
0
仙果 楼主努力下,你这篇分享没有写完,继续补充下吧
已经分析完了,只不过后边跟了一个希望能移植到2016R2的讨论,前边是分析和利用,后边是想等等看雪的大佬,看看是否能做到这样。没人发过2016R2的利用,不确定2016R2是否能利用
雪    币: 2697
活跃值: 活跃值 (627)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 5 2020-6-24 21:23
4
0
不错
雪    币: 4616
活跃值: 活跃值 (1601)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
与时尽现。 活跃值 6 2020-6-24 21:28
5
0
王cb 不错
师傅好
游客
登录 | 注册 方可回帖
返回