首页
社区
课程
招聘
[原创]CVE-2017-0101-Win32k提权分析笔记
发表于: 2019-12-28 12:29 11737

[原创]CVE-2017-0101-Win32k提权分析笔记

2019-12-28 12:29
11737

    学习内核提权相关知识,拿CVE-2017-0101这个漏洞练习,于是有了这篇笔记。CVE-2017-0101是位于Win32K中的一个整形溢出漏洞,通过利用可实现内核提权。学习过程中,查阅了很多资料,尤其是xiaodao师傅的博客文章,给予了莫大的帮助。阅读xiaodao师傅博客,深感到师傅内功的深厚,无比佩服。本篇笔记以初学者的视角,尽量避开太多的Windows内核GDI内部实现逻辑相关知识,对该漏洞进行分析,调试,最终POC内核提权。         

    原xiaodao师傅最终pool fengshui 最终使用Bitmap+Palette实现,关键内存布局为0xDF8-0x1F0-0x18。我对其进行修改,使用Btimap+Bitmap实现,关键布局方式为0xD88-0x260-0x18。

    环境: x86  win7 sp1 

    定位漏洞代码位置可选择找到对应补丁修复后,使用diff工具对比其函数差异定位到对应函数代码片段,阅读相关公开信息,我们可知漏洞发生在Win32k-EngRealizeBrush函数内,此处直接贴出该部分代码,后续所有讨论都基于该函数代码。

    通过分析Win32k-EngRealizeBrush可知,函数内由于存在一处整形溢出。该函数内在对要使用的ENGBRUSH对象进行内存申请时(即下图中PALLOCMEM申请pool tag为”Gebr”的对象内存,该对象类型即为ENGBRUSH),由于其使用到的申请大小相关变量v12可溢出、可控,从而导致我们可以利用该溢出点,通过构造小于其标准对象大小的ENGBRUSH对象,进而在其后续对象成员初始化时,可越界操作到其对象内存以外区域,进一步有机会通过布局内存,达到利用目的。

    

    

    通过下图(只会注释,不会画图)注释图可理解,如果想要精确的攻击到指定的内存布局对象,首先需要搞清楚的问题是漏洞处代码在ENGBRUSH对象申请内存前的大小计算过程,该过程可通过动态调试结合静态分析来完成。

    

    首先我们静态分析漏洞函数处相关代码可知,ENGBRUSH对象申请的大小的决定因素为v12变量,v12最终的计算过程为通过下述代码获得(另外此处可看到有gpCachedEngbrush缓存策略,调试时有如有合适缓存大小,将不申请内存直接使用缓存,故代码测试调试前可注销系统等操作避开此处判断):

    

    接下来分析v12不包括自身初始值的影响部分((v15 >> 3) * v14),从后向前经历以下流程:

    首先其影响来自v15,v14:

    v12 += (v15 >> 3) * v14;

    v15影响来自v13或v15为定值0x20:

    v15 = (v13 + 0x3F) & 0xFFFFFFE0

    v13影响来自v61:

    v13 = *((_DWORD *)v61 + 8);

    V14影响来自v61:

    v14 = *((_DWORD *)v61 + 9);

    递进整理下前后关系流程:

(v15 >> 3) * v14;

     v15=(v13 + 0x3F) & 0xFFFFFFE0

          V13=*((_DWORD *)v61 + 8);

     v14=*((_DWORD *)v61 + 9);

          V14=*((_DWORD *)v61 + 9);

    分析到此处可知,影响该部分计算结果的成员为*((_DWORD *)v61 + 8),*((_DWORD *)v61 + 9)处数值。接下来继续分析v61的由来,回溯到函数头部,可看出,v61来自v7,而v7通过SURFOBJ_TO_SURFACE(a4)得来,a4为EngRealizeBrush函数形参,一个SURFOBJ对象。

    查看SURFOBJ_TO_SURFACE()函数代码可知其本质即为指向参数SURFOBJ(a1)-0x10处,而查看SURFACE对象结构也可进一步确认,SURFOBJ就是SURFACE对象其0x10处的一个成员。回到关注点,此时我们关心v61 + 8/9处的值,即指向v61偏移0x20和0x24,也就是指向SURFACE(v7)对象偏移0x20和0x24处,SURFOBJ(a4)对象0x10和0x14处,查看结构相应说明即为SURFOBJ->sizlBitmap成员,该成员保存了一个Bitmap图像的像素宽高。    

    最后梳理上述分析到的部分公式:

    (v15 >> 3) * v14;

    =((v13 + 0x3F) & 0xFFFFFFE0或 0x20)>>3*v14

    =(((v13 + 0x3F) & 0xFFFFFFE0) 或 0x20)>>3*v14

    =((((*((_DWORD *)v61 + 8)) + 0x3F) & 0xFFFFFFE0) 或 0x20)>>3*(*((_DWORD *)v61 + 9))

    =(((a4对象的像素宽+ 0x3F) & 0xFFFFFFE0)或 0x20)>>3* a4对象的像素高

    接下来继续分析v12值的初始化部分过程,过程同上述分析流程得到以下递进关系:

v12 = v60 * v68 + 0x44;

    v60 = (unsigned int)(v11 * v8) >> 3;

         v11取决于局部变量a3(switch)//注意此a3不是函数传参对象a3

         a3 = (struct _SURFOBJ *)*((_DWORD *)v58 + 0xF)

              v58 = SURFOBJ_TO_SURFACE(a2)

         v8 = *((_DWORD *)v6 + 8);

              v6 = SURFOBJ_TO_SURFACE(a3)

    v68 = *((_DWORD *)v6 + 9);

         v6 = SURFOBJ_TO_SURFACE(a3);

    梳理流程可得以下公式:

    v12 = v60 * v68 + 0x44;

    v12 = v60 * (*((_DWORD *)v6 + 9)) + 0x44;

    v12 = v60 * (*((_DWORD *)SURFOBJ_TO_SURFACE(a3) + 9)) + 0x44;

    v12 = v60 * (a3对象的像素高) + 0x44;

    v12 = ((v11 * v8) >> 3) * (a3对象的像素高) + 0x44;

    v12 = ((v11 * (a3对象的像素宽)) >> 3) * (a3对象的像素高) + 0x44;

v12 = ((取决于 a2.iBitmapFormat -switch数值* (a3对象的像素宽)) >> 3) * (a3对象的像素高) + 0x44;

    v12+部分:

    (((a4对象的像素宽+ 0x3F) & 0xFFFFFFE0)或 0x20)>>3* a4对象的像素高

    v12 初始部分:

    v12 = ((取决于 a2.iBitmapFormat * (a3对象的像素宽)) >> 3) * (a3对象的像素高) + 0x44;

    总结静态分析结论可知影响最终的v12可控溢出因素均来自EngRealizeBrush函数参数 a2,a3,a4 ,类型为SURFOBJ,具体影响来自以下成员

    1.a4(SURFOBJ)对象的像素宽和高,offset:0x10

    2.a2(SURFOBJ)对象的iBitmapFormat 成员,offset:0x2c

    3.a3(SURFOBJ)对象的像素宽和高,offset:0x10

    通过静态分析,我们已经大概的了解到影响溢出的关键公式流程,接下来还要通过动态分析,来进一步确认静态分析的结论,同时对静态分析过程中的未确认部分进行进一步探索(例如a2.iBitmapFormat最终switch后的分支到达结果)。

    首先在漏洞代码处关键位置下断点,通过栈回溯找到可以触发漏洞的3环代码路径(此处注意,实际测试下断点后操作系统内窗口短时间并未断下,可以尝试通过打开浏览器,注销,锁定机器等拥有较多复杂UI操作过程的动作,快速让系统断到我们希望调试的代码处)。通过观察断下的堆栈情况,我们可知通过使用gdi32!PolyPatBlt可触发到达EngRealizeBrush过程ENGBRUSH对象初始化处。

    接下来我们还需要了解如何编写3环代码使用PolyPatBlt,来进行下一步的验证调试工作。由于该函数未文档化,我尝试通过在reactos 系统源代码中寻找一些答案(xiaodao师傅已经直接给了使用方法,但还是要了解过程和方法),下图为系统源码中其正确的调用方式和所需参数的定义,通过参考系统代码的方法,可较快速分析,掌握该api相关用法。


    经过查看reactos,结合xiaodao师傅给出的答案,了解3环测试代码写法后,接下来直接使用下述代码调试。

    通过在EngRealizeBrush头下断,调试观察我们关心的EngRealizeBrush参数a2,a3,a4,断下后首先观察堆栈中的函数参数(下图红框从左到右分别为EngRealizeBrush参数a1-a4)


    经过反复调试,可以发现a4始终为空,经过上述静态分析可知,当a4为空时,v12+部分不参与运算。也就是说上述的关键两部分计算,由于a4对象未使用,现在只需要关心v12初始计算过程:

    v12 初始计算过程关键取决于a2.iBitmapFormat(offset:0x2c)和a3对象的像素宽高(offset:0x10),经过多次调试,我们可知a2.iBitmapFormat始终为0x6,而a3对象的像素宽高即为我们测试代码中指定与Brush绑定的bitmap宽高。


    接下来,需要关心的是,当a2.iBitmapFormat值为6时,最终执行的switch将影响的公式中v11关键数值,还有当前3环代码最终影响0环生成ENGBRUSH对象的大小。调试可知,当a2.iBitmapFormat为6时,switch最终影响v11值为32(0x20)。



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

上传的附件:
收藏
免费 3
支持
分享
最新回复 (4)
雪    币: 620
活跃值: (372)
能力值: (RANK:150 )
在线值:
发帖
回帖
粉丝
2
2019-12-28 17:55
0
雪    币: 1297
活跃值: (821)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
wjllz
你超赞
2019-12-28 20:35
0
雪    币:
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
大佬,我想问下用windbg查看EngRealizeBrush函数引用是该怎么做(双机调试下断点还是用户态attach一个进程或是什么其他方法)。希望大佬给个解答。还有一个问题就是编译了大佬的POC在虚拟机上跑(试了家庭版x86 win7 sp1和旗舰版x86 win sp1),并没有弹出计算器,想知道这是为什么?或者大佬能给个你实验环境的说明吗
2020-4-22 11:35
0
雪    币:
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
5
想请问大佬,windbg调试的时候:.process 87df5030是什么意思。如果我想用windbg验证这个POC的流程,应该如何下断点,能给个比较详细一点的建议吗?(菜鸟一只)
2020-4-24 13:22
0
游客
登录 | 注册 方可回帖
返回
//