【漏洞分析】MS17-017 Windows内核提权漏洞Exploit分析

https://p5.ssl.qhimg.com/t01d4ce567b0fe38e94.png

作者:playb0y23333

预估稿费:1000RMB

(本篇文章享受双倍稿费 活动链接请点击此处

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

一、简介

此次分析的内核提权漏洞为SENSEPOST的Saif El-Sherei在分析微软MS17-017补丁的时候发现的,该漏洞类型为win32k.sys驱动程序中处理GDI对象的函数EngRealizeBrush内发生的整型溢出,利用方式也比较经典,Saif El-Sherei在今年的Defcon会议上发布了一款针对Win7 Sp1 x86平台的Exploit,我就主要针对该Exploit进行了下简单的分析,如果分析过程存在问题,欢迎联系我交流指正。


二、成因分析

Saif是通过补丁比对分析的漏洞成因,为了便于分析漏洞成因,我们先把漏洞POC代码中的喷射及后续的利用代码去掉,只留能够触发蓝屏的代码(见附件中的poc.exe),运行poc程序触发蓝屏,通过分析蓝屏信息再一步步的定位漏洞触发成因(注意:poc.exe中设置了一个调试断点,在Windbg中触发之后设置了一个虚拟机快照,以后每次重新调试恢复快照重新连接Windbg即可,这样可以尽量使每次调试时的内存布局尽量稳定):

 https://p2.ssl.qhimg.com/t014c4d3ed2f4db827b.png

查看栈回溯信息也可以看到是在ExFreePoolWithTag函数释放Pool时的故障:

 https://p0.ssl.qhimg.com/t01b9336e042d3c8a47.png

查看0xfdec3168处的Pool信息:

 https://p4.ssl.qhimg.com/t01d939aa7d323fefee.png

根据Pooltag标识Gebr搜索到该Pool是在EngRealizeBrush函数中申请的:

 https://p0.ssl.qhimg.com/t014eb2dfaddb12829d.png

其实根据这里的内存申请代码已经能够看出此处存在问题,PALLOCMEM函数参数中申请的空间大小为ebx+0x40h,即下图中反编译的代码中的v12+0x40,v12本身为无符号整型,所以申请的空间大小至少为0x40:

 https://p1.ssl.qhimg.com/t0100f48e1e6a2f0386.png

但是在之前查看0xfdec3168处的Pool信息中显示的Size为0x18(包含Pool 头部8字节,实际申请空间大小应为0x10)。可以证明此处申请内存时发生了整形溢出:

 https://p0.ssl.qhimg.com/t0164273db90fe44c7b.png

下面再啰嗦下查看蓝屏的成因,在PALLOCMEM函数返回处下断查看申请的Pool内存地址:

 https://p5.ssl.qhimg.com/t012186398091af41a1.png

此处注意对比蓝屏时查看的0xfdec3168处Pool信息,可以发现蓝屏状态时0xfdec3178处显示的POOL_HEADER被破坏,在释放其链表的下一项0xfdec3160时导致其操作失败,下面再看下0xfdec3178地址处存放的数据是如何被覆盖的,触发写断点:

 https://p0.ssl.qhimg.com/t01784cfc4dba1e2491.png

0xfdec3160处Pool大小为0x18字节,除去8字节的POOL_HEADER大小,实际的缓冲区只有0xfdec3168-0xfdec3178这0x10字节,但是明显可以看到此处被越界覆写,也就验证了EngRealizeBrush函数调用PALLOCMEM申请内存时申请空间过小,导致后续操作该处内存空间时发生了越界。

下面来分析一下为什么PALLOCMEM函数会只申请0x10字节的内存,再来深入探究下其参数的来源:

 https://p2.ssl.qhimg.com/t01365a876ef4ff817f.png

来看下运行时的计算过程,下图中的乘法运算即v60*v68,最终结果还需要加上0x44(68),调用PALLOCMEM函数时再加上0x40:

 https://p1.ssl.qhimg.com/t0105e2938ac05ce42c.png

此时edi寄存器的值以及ebp-18h的值均来自poc代码中CreateBitmap函数参数(HBITMAP bitmap = CreateBitmap(0x23, 0x1d41d41, 1, 1, NULL))。最终的计算结果为0x23*0x20/(2^3)* 0x1d41d41=0xFFFFFF8C,在调用PALLOCMEM函数时又分别加上了0x44、0x40字节,0xFFFFFF8C+0x44+0x40= 0x100000010,正是在此整形溢出导致的实际申请内存时只申请了0x10字节,在EngRealizeBrush后续对申请的内存赋值时导致破坏了紧邻的Pool结构,最终触发蓝屏。


三、漏洞利用

该漏洞的利用过程还是比较精彩的,上文的poc中原作者已经构造好参数,控制最终申请0x10+0x8字节内存,能够越界覆写临近的Pool结构,为了完成提权过程可以使用多种利用方式,作者发布的Exploit使用的是混合利用Bitmap和Palette对象完成,首先控制在发生越界的SURFOBJ对象后面喷射两个相邻的Bitmap对象和Palette对象,利用越界覆写第一个Bitmap对象结构的sizlBitmap成员,获取相对地址读写权限,再利用被修改的Bitmap对象修改相邻的Palette对象的cEntries成员,继而控制这个Palette对象修改第二个Palette对象的pFirstColor成员,完成任意地址读写,替换当前进程的Token为SYSTEM进程的Token,整个利用过程也就完成了。

3.1喷射

因为产生越界读写的SURFOBJ对象是在Paged Session Pool中分配的,对应的也需要利用它的特性完成喷射,第一次申请内存将首先占据内存页(0x1000字节大小)的开始部分,后续申请的内存则会从内存页的末尾开始向前排列(盗用Saif文章原图如下):

 https://p1.ssl.qhimg.com/t01b13922b5b4fc0f6f.png

具体做法如下:

a)首先申请2000个0xFE8字节大小的空间,用于清理内存空间,内存页末尾留出0x18字节。

b)利用创建Windows窗体类时成员lpszMenuName,控制申请0x18字节内存占用上一步空出的0x18字节。虽然窗体类tagCLS是在Desktop Heap中分配内存,但是lpszMenuName却是在内核池中分配的。

c)释放第一步申请的0xFE8大小的空间,便于下一步布置相邻的Bitmap以及Palette对象。

d)依次申请0x7F8字节大小的Bitmap对象以及0x7E8字节大小的Palette对象,使相邻的这两个对象占据上一步释放的空间,关于如何确定内核中Bitmap对象申请的空间大小跟CreateBitmap函数的参数之间没有确定的公式,只能不断尝试,不过CreatePalette函数参数与对应的内核中申请空间的大小已经有公开的资料了。

e)释放第二步申请的窗体类,这样最后0x18字节内存就变成为了Free状态,在漏洞触发时申请的0x18内存就落入了这些内存空洞之间。

下面将exploit代码中喷射的代码和利用的代码注释回来,重新开始调试exploit从堆喷到执行Shellcode的步骤,同时继续在PALLOCMEM运行完毕后下断,查看发生越界的SURFOBJ对象所在的内存地址以及喷射之后越界对象地址附近的内存布局:

 https://p0.ssl.qhimg.com/t01c338c3181e9d4706.png

可以看到发生越界的SURFOBJ对象0xfd4a8ff0相邻的两个对象就是我们布局的一个Bitmap对象(0xfd4a9000)以及Palette对象(0xfd4a97f8),利用喷射完成内存地址的稳定布局之后就可以触发漏洞了。

3.2相对地址读写

实现相对地址读写的目的主要是能够利用只触发漏洞一次,完成从受限的越界写入转化成可以多次覆写其它更多地址的功能,注意漏洞触发时并非只有我们在分析漏洞成因的时候发现的越界覆写Pool Header,同时覆盖的还有相邻内存池的其它数据结构:

 https://p2.ssl.qhimg.com/t0110c9e73b1dc01836.png

在写exploit中我们主要关注*(_DWORD *)(v16 + 0x3C) = a3这段代码,除去发生越界的SURFOBJ对象缓冲区0x10字节以及相邻的Bitmap对象的Pool header0x8字节、_BASE_OBJECT 0x10字节,实际还会覆盖相邻的Bitmap对象的sizlBitmap结构的cy成员(0x3C-0x10-0x10-0x8=0x14):

 https://p2.ssl.qhimg.com/t0189b8d767cda91825.png

下面继续运行exploit覆盖该地址的数据,也可以使用windbg的gdiobjdump插件更方便的查看覆盖的数据格式,这里不再介绍了:

 https://p4.ssl.qhimg.com/t010d30ec969d780097.png

因为修改了Bitmap对象的sizlBitmap结构,现在就能利用Bitmap对象的GetBitmapBits、SetBitmapBits函数完成功能更容易操作的越界读写操作,因此也就拥有了喷射在与Bitmap对象紧邻的Palette对象数据结构的数据的操作能力,具体需要读写那些Palette对象成员可以研究下Palette对象的结构:

 https://p1.ssl.qhimg.com/t01c9faf70f3297a79c.png

其中cEntries代表结构体末尾的 PALETTEENTRY数组的成员个数,pFirstColor指针则指向PALETTEENTRY数组的第一个成员的地址。下面通过利用上一步的Bitmap对象修改相邻Palette对象的cEntries为0xFFFFFFFF,就相当于是扩展了Palette对象操作的内存空间是pFirstColor指针指向的内存空间之后的任意地址。这部分流程也比较简单,首先从喷射的Bitmap对象中找到被漏洞触发越界覆写sizlBitmap结构的那个Bitmap对象,调用GetBitmapBits函数看看那个能越界就行,不再赘述。第二步根据喷射时Bitmap与Palette对象的位置关系,调用GetBitmapBits获取Bitmap对象的数据,定位到其中包含的Palette对象的cEntries成员,修改后调用SetBitmapBits函数重新写入,下图即为被修改前后的cEntries:

 https://p1.ssl.qhimg.com/t010d231eb6615d4b0e.png

至此就被覆写cEntries的Palette对象就拥有相对地址读写的能力了。

3.3任意地址读写

虽然已经能够完成相对地址读写的能力,但是还是局限在特定的内存空间内,所以还需要再做一点手脚完成对任意地址内存空间的操纵能力,最终完成替换Token的操作。具体做法其实类似上一步骤覆写cEntries,这里只需要覆写pFirstColor指针即可,需要修改什么地址的内存将pFirstColor指针指向该地址就行了,首先也是先寻找到被修改cEntries结构的Palette对象,不再赘述。根据喷射的特点,每隔0x1000就存在一个喷射的Palette对象,直接调用SetPaletteEntries函数对下一个内存页的Palette对象pFirstColor成员进行修改即可:

 https://p1.ssl.qhimg.com/t01632011501c6f44e1.png

注意此处pFirstColor的值被修改为了0xfd4a8000,这个地址是第一步喷射时和发生越界的SURFOBJ对象在同一个内存页的Bitmap对象。设置成这个地址的目的是利用该地址处Bitmap对象的tag值Gh15做标示,再遍历所有喷射的Palette对象这个pFirstColor值被修改的Palette。

 https://p4.ssl.qhimg.com/t01d8e96278431cb714.png

到此就已经具备任意地址读写的能力了,需要修改任意地址的内存只需利用被修改cEntries的Palette去再次修改第二个Palette的pFirstColor,再对其调用SetPaletteEntries、GetPaletteEntries函数读取或修改内存数据。

3.4Shellcode

进行到这里就可以松口气了,内核提权Shellcode目前一般都采用替换当前进程的Token为SYSTEM进程的Token,这里没有什么需要特别说的,首先获取当前进程和SYSTEM进程的EPROCESS结构地址,利用任意地址读写将SYSTEM进程的Token写入到当前进程的Token位置就完事了。提权成功:

https://p3.ssl.qhimg.com/t0195e9ad8ebe654dfe.png

 

四、总结

微软最后在PALLOCMEM函数申请内存之前添加了ULongLongToULong函数做检测,如果发生了整形溢出不会跳转到申请内存的代码,已经被Patch的代码:

 https://p5.ssl.qhimg.com/t01b430578ac4f234c1.png

除了exp所使用的这种漏洞利用方式以外,Saif也提到了其它两种方式,一种是利用两个相邻的Bitmap对象完成,由一个Bitmap对象去覆写另一个Bitmap的pvScan0来完成任意地址读写,另一种方式是利用一个Bitmap直接去修改一个Palette对象的pFirstColor指针,有兴趣的湿敷可以尝试下。

附件链接:

https://share.weiyun.com/d1ed23e7750d6a37e3db268ac9ad84ef 


参考资料:

Saif El-Sherei的代码及文章:

https://github.com/sensepost/gdi-palettes-exp 

(完)