首页
论坛
课程
招聘
[原创]CVE-2020-1054分析
2020-7-24 16:02 3312

[原创]CVE-2020-1054分析

2020-7-24 16:02
3312

目录

CVE-2020-1054研究

背景

CVE-2020-1054是存在于win32k.sys内核模块中的一个oob漏洞,成功利用可以导致权限提升。

 

原exp为rust语言所写,在分析中发现该exp并不能成功执行,在分析漏洞后感觉该漏洞利用的成功率较高,因此我决定用C语言重写该exp

 

原exp地址

https://github.com/0xeb-bp/cve-2020-1054

image-20200721102638813

漏洞分析

寻找触发点

蓝屏的POC如下非常简单

int main(int argc, char *argv[])
{
    LoadLibrary("user32.dll");
    HDC r0 = CreateCompatibleDC(0x0);
    // CPR's original crash code called CreateCompatibleBitmap as follows
    // HBITMAP r1 = CreateCompatibleBitmap(r0, 0x9f42, 0xa);
    // however all following calculations/reversing in this blog will 
    // generally use the below call, unless stated otherwise
    // this only matters if you happen to be following along with WinDbg
    HBITMAP r1 = CreateCompatibleBitmap(r0, 0x51500, 0x100);
    SelectObject(r0, r1);
    DrawIconEx(r0, 0x0, 0x0, 0x30000010003, 0x0, 0xfffffffffebffffc, 
        0x0, 0x0, 0x6);

    return 0;
}

通过栈回溯,可以找到触发崩溃的地址为vStrWrite01+0x36a

0: kd> k
 # Child-SP          RetAddr           Call Site
00 fffff880`02bc6bd0 fffff960`0011fce6 win32k!vStrWrite01+0x36a
01 fffff880`02bc6c80 fffff960`00121dd7 win32k!EngStretchBltNew+0x164a
02 fffff880`02bc7210 fffff960`0026914e win32k!EngStretchBlt+0x797
03 fffff880`02bc7340 fffff960`00266b33 win32k!EngStretchBltROP+0x5fe
04 fffff880`02bc7470 fffff960`0026613f win32k!BLTRECORD::bStretch+0x623
05 fffff880`02bc75c0 fffff960`0017d06f win32k!GreStretchBltInternal+0xa37
06 fffff880`02bc7860 fffff960`0017d485 win32k!BltIcon+0x18f
07 fffff880`02bc7910 fffff960`0015693d win32k!DrawIconEx+0x3b1
08 fffff880`02bc79f0 fffff800`03ecee53 win32k!NtUserDrawIconEx+0x14d
09 fffff880`02bc7a70 00000000`74e203da nt!KiSystemServiceCopyEnd+0x13
0a 00000000`001ddb08 00000000`74dff2e0 0x74e203da
0b 00000000`001ddb10 00000000`00000000 0x74dff2e0
0: kd> r
rax=fffff900c3c00000 rbx=0000000000000000 rcx=fffff906c3c00238
rdx=fffff900c0790430 rsi=fffff900c07903a0 rdi=fffff900c07903a8
rip=fffff9600012218a rsp=fffff88002bc6bd0 rbp=0000000000000000
 r8=0000000000000020  r9=fffff96000080000 r10=fffff88002bc6c30
r11=0000000000000000 r12=0000000000000001 r13=0000000000000001
r14=fffff906c3c00238 r15=0000000000000000
iopl=0         nv up ei ng nz na po cy
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00000287
win32k!vStrWrite01+0x36a:
fffff960`0012218a 418b36          mov     esi,dword ptr [r14] ds:002b:fffff906`c3c00238=????????

在对该函数进行逆向后,下面为该函数部分逆向截图,崩溃处有标注

 

image-20200721103105049

 

在崩溃处取值给了tmp,往下继续看,tmp是机会跟某些值进行and 或者是 or运算,且最终有机会赋值给自身,如果可以控制越界地址,则有可能实现任意内存破坏

 

image-20200721104042298

控制oob地址

oobTarget公式计算

该地址在循环内,相对于其他漏洞略为复杂。

 

经过分析计算越界地址如下(其中pSurface为第三个参数,pStrrun为第一个参数):

 

oobTarget= pSurface->mySURFOBJ->pvScan0 + pStrrun->yPos pSurface->mySURFOBJ->lDelta + 4 (pStrrun->xrl->xPos >> 5);

 

在进入循环前下述result为固定值

 

offset = pStrrun_yPos * refpSurface->SurfObj.lDelta;

 

result = refpSurface->SurfObj.pvScan0 + offset;

 

因此可以越界地址可以描述为:

 

oobTarget= result + 4 * (pStrrun->xrl->xPos >> 5);

 

如下图椭圆圈内,在进入循环后result 每次增加refpSurface->SurfObj.lDelta大小

 

oobTarget= result + 4 (pStrrun->xrl->xPos >> 5)+refpSurface->SurfObj.lDeltaloopCount;

 

在本公式中,我们需要确定的值有 pStrrun->yPos,pStrrun->xrl->xPos,refpSurface->SurfObj.lDelta以及循环次数loopCount

 

image-20200721111211829

 

本人没有像原exp作者一样的fuzz算力,因此采用比较笨,但是比较实用的方法(栈回溯),推算yPos的计算公式

 

过程较为复杂,有兴趣的要有耐心慢慢分析(篇幅问题,在此处可能说的不详细)

 

为了方便读者理解,依旧采取函数执行流程进行讲解(算法的逆向其实是倒着来的,还是栈回溯)

 

oobTarget动态验证

 

image-20200721144844094

pStrrun->yPos

触发漏洞的顶层函数

 

DrawIconEx(exploit_dc, 0x900, 0xb, (HICON)0x40000010003, 0x0, 0xffe00000, 0x0, 0x0, 0x1);

 

yPos由arg3(0xb),以及arg6(0xffe00000)影响

 

第一次运算位置为GreStretchBltInternal+0x95e处

 

可以看到这里将arg3与arg6相加

 

yPos=arg3+arg6

 

image-20200723134739847

 

运算后在函数bStretch-->vOrderStupid中有层检验,此处算法为

 

yPos=arg3+arg6+1

 

image-20200723142432919

 

第二次计算在EngStretchBltNew-->vInitStrDDA函数中

 

yPos=P.rcx.yBottom=arg3+arg6+1+v30,v30也可用arg3和arg6表示

 

至此,只需要对下述算法进行还原,即可确定yPos的地址

 

image-20200724110855441

 

这里的逆向比较有意思,有兴趣的小伙伴可以跟一下

 

我将其还原为C代码,如下

 

可能不完全,该函数逻辑判断较多

 

image-20200724114027293

 

算法验证

 

image-20200724104423216

 

image-20200724154000080

pStrrun->xrl->xPos

pStrrun->xrl->xPos的值比较简单,直接就是arg2

refpSurface->SurfObj.lDelta

refpSurface为vStrWrite01第3个参数,其类型为SURFACE,下图为reactos项目中对该结构体的具体定义

 

image-20200721141350681

 

现在我们来回顾一下SurfObj对象 ,其结构如下

 

image-20200721141536872

 

在windbg中我们可以看到SurfObj相关字段的含义,其中我们比较关注的是cx,cy,lDelta

 

通过计算可以得到refpSurface->SurfObj.lDelta= cx / 8

 

其中cx为CreateCompatibleBitmap(exploit_dc, 0x51500, 0x100)中的第二个参数

loopCount

loopCount==pStrrun_cRep

 

在分析pStrrun->yPos时其实已经解决了这个值

 

这个值为quotient_count由agr3和arg6影响

 

quotient_count=((arg3 + 1 - (arg6 + arg3 + 1))/ 0x20);

 

这与原作者写的ptyhon脚本算法不一样,我用原作者的测试好像不是很准确,不知道是不是有什么误解

控制offset

在漏洞触发过程中漏洞函数是被调用了两次,如下图yPos还会随之而改变,但是问题不大,每次增加pStrrun->cRep大小

 

image-20200724113808960

 

现在确定了所有的参数关系,理论上可以控制oobtarget

 

oobTarget= pSurface->mySURFOBJ->pvScan0 + pStrrun->yPos pSurface->mySURFOBJ->lDelta + 4 (pStrrun->xrl->xPos >> 5);

 

我们参考pSurface结构体,可以看到pSurface->mySURFOBJ->pvScan0=dwExpBitmapObj+0x238

 

oobTarget= dwExpBitmapObj+0x238 + pStrrun->yPos pSurface->mySURFOBJ->lDelta + 4 (pStrrun->xrl->xPos >> 5);

 

也就是说我们的oobtarget不能随意控制,但是确可以基于dwExpBitmapObj进行一个偏移

 

image-20200721142934617

 

原exp中选用的偏移为0x100000000

 

通过这个偏移我我们可以求得DrawIconEx的各个参数(理论上不唯一)

利用过程

  • 获取内核对象地址空间
  • 计算oob地址
  • 在oob地址处放一个bitmap对象
  • 触发漏洞函数,修改bitmap对象的BitmapObj->SurfObj.sizlBitmap,使其越界读写
  • 任意读写,替换token等操作

前面的文章对oob的地址计算已经叙述过了

 

通过上述文档,我们可以做到对相对于漏洞内核对象偏移0x100000000+0x358处进行内存破坏,进而在最后一次循环破坏0x100000000+0x70038处内容

 

在windows中bitamap内核对象的排布是连续的,如下图,如果在第二个红框的位置坐落的是一个Biamap对象,那么利用起来就比较简单了

 

image-20200724153355676
通过布局使得大小为0x7000的bitmap对象在这个位置 图片描述
参考上述SurfObj内核对象的结构体,我们可以知道偏移为0x38处为BitmapObj->SurfObj.sizlBitmap,如果使其值破坏变成一个较大的数,那么该对象则可以实现越界读写

 

操纵该对象读写的函数分别为GetBitmapBits,SetBitmapBits

 

其所读的最大值如下

 

BitmapObj->SurfObj.sizlBitmap.cy (((BitmapObj->SurfObj.sizlBitmap.cx v14 + 0xF) >> 3) & 0x1FFFFFFE);

 

image-20200724143259908

 

破坏前,原始值为0x6f000

 

image-20200724151832545

 

破坏后,值为0xffffff3f

 

image-20200724151935098

 

最后通过 替换tagWND.bServerSideWindowProc窗口过程函数,进行提权(方法很多,直接替换token,HookHal表都可以)

 

该漏洞利用的难点在于oobtarget在循环内部,算法过程相对复杂,需要有耐心

写在分析外

该漏洞内存破坏的时候与一张表有关系,这张表我没仔细研究,但是应该跟当前操作系统的主题等相关
如下:
代码没变,切换了系统的主题,也成功填充了,填充值为0xffffffff,通过之前提过的GetBitmapBits能读的最大值的算法计算,最大为0,所以并不能越界读写

						

[看雪官方]《安卓高级研修班》线下班,网课(12月)班开始同步招生!!

最后于 2020-7-24 16:21 被与时尽现。编辑 ,原因:
上传的附件:
收藏
点赞3
打赏
分享
最新回复 (3)
雪    币: 47
活跃值: 活跃值 (87)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
AlchemistS 活跃值 2020-7-24 16:22
2
0
学习了 学习了
雪    币: 1900
活跃值: 活跃值 (196)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 5 2020-7-27 10:44
3
0
写的不错,有需要用自动dump内存结构的工具在这里https://gitee.com/cbwang505/WindbgScript楼主用的就是这款
雪    币: 5190
活跃值: 活跃值 (6301)
能力值: (RANK:65 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-7-28 13:34
4
0
感谢分享!关注了!
游客
登录 | 注册 方可回帖
返回