CVE-2016-0040 滥用GDI 内核提权漏洞

漏洞背景

本人技术有限,有很多可能没有分析到位的地方请各位大佬指点。

GDI利用技术很早就流行了,不过在国内 文章搜出来基本就几篇。正好前段时间看到了利用GDI 提权漏洞,赶紧学习一波。

在2016年 2月份 微软 修复了 CVE-2016-0040 内核级别的特权提升漏洞。该漏洞成因为 在 ntoskrnl 的WMI子系统中利用未初始化的堆栈变量

漏洞分析

一般分析一个CVE 我都会先去Kali 里面看看

CVE-2016-0040 滥用GDI 内核提权漏洞_第1张图片

虽然 微软 说影响情况很多 不过在kali中只提供了 Win7 sp0 /sp1 X64的利用。最后在GitHub上找到了 EXP 和利用的源代码

利用成功情况

CVE-2016-0040 滥用GDI 内核提权漏洞_第2张图片

先对EXP 进行 裁剪 造成崩溃

CVE-2016-0040 滥用GDI 内核提权漏洞_第3张图片

可以看到裁剪 POC 在3环 通过控制码 对0环 WMIDataDevice进行通信

而发送 的数据为 WMIRECEIVENOTIFICATION 这个结构

CVE-2016-0040 滥用GDI 内核提权漏洞_第4张图片

刚开始再 32位下 分析

CVE-2016-0040 滥用GDI 内核提权漏洞_第5张图片
CVE-2016-0040 滥用GDI 内核提权漏洞_第6张图片

可以看到触发了漏洞 查看 堆栈 是在 nt!WMIPRECEIVENOTIFICATIONS函数中。之后在wrk 源码中发现了此函数 (为分析漏洞已删除 无关代码)

NTSTATUS WMIPRECEIVENOTIFICATIONS(

    PWMIRECEIVENOTIFICATION RECEIVENOTIFICATION,

    PULONG OUTBUFFERSIZE,

    PIRP IRP

    )

    #DEFINE MANY_NOTIFICATION_OBJECTS 16

    ULONG I;

    PWMIGUIDOBJECT GUIDOBJECT;

    ULONG HANDLECOUNT;

    PHANDLE3264 HANDLEARRAY;

    OBJECT_EVENT_INFO *OBJECTARRAY;

    OBJECT_EVENT_INFO STATICOBJECTS[MANY_NOTIFICATION_OBJECTS];  //本地

#ENDIF

    HANDLECOUNT = RECEIVENOTIFICATION->HANDLECOUNT;          //攻击点  r14d

    HANDLEARRAY = RECEIVENOTIFICATION->HANDLES;

    //

    // CREATE SPACE TO STORE THE OBJECT POINTERS SO WE CAN WORK WITH THEM

    //

    IF (HANDLECOUNT > MANY_NOTIFICATION_OBJECTS)

    {

        OBJECTARRAY = WMIPALLOC(HANDLECOUNT * SIZEOF(OBJECT_EVENT_INFO));

        IF (OBJECTARRAY == NULL)

        {

            RETURN(STATUS_INSUFFICIENT_RESOURCES);

        }

    } ELSE {

        OBJECTARRAY = STATICOBJECTS;

    }     

#IF DBG

    RTLZEROMEMORY(OBJECTARRAY, HANDLECOUNT * SIZEOF(OBJECT_EVENT_INFO));

#ENDIF

        } ELSE IF (RECEIVENOTIFICATION->ACTION == RECEIVE_ACTION_CREATE_THREAD) {

                    GUIDOBJECT = OBJECTARRAY[0].GUIDOBJECT;//

                    GUIDOBJECT->USERMODECALLBACK = (PUSER_THREAD_START_ROUTINE)(ULONG_PTR)RECEIVENOTIFICATION->USERMODECALLBACK.HANDLE;

                    GUIDOBJECT->EVENTQUEUEACTION = RECEIVE_ACTION_CREATE_THREAD;

                    GUIDOBJECT->USERMODEPROCESS = USERMODEPROCESS;

                    GUIDOBJECT->STACKSIZE = STACKSIZE;

                    GUIDOBJECT->STACKCOMMIT = STACKCOMMIT;

                    THREADLISTHEAD = &GUIDOBJECT->THREADOBJECTLIST;

                    INITIALIZELISTHEAD(THREADLISTHEAD);

从函数源码 可以看到 如果 当传入的HandleCount 为0时 就会使用本地一个数组 。可以看到 这个数组并没进行 初始化 ,往下看 看到 在CHK 版本中进行了 初始化(。。。) 之后函数 会进行检查一些操作 当action 为RECEIVE_ACTION_CREATE_THREAD 时 看到 对 ObjectArray 进行了使用 ,不过当设置 HandleCount 为0 时 ObjectArray 是未初始化 ,在下面部分 又对这个初始化的指针 进行了使用。在利用之后 就造成了典型的内核 任意内存 读写

GDI利用

GdiShareHandleTable Win32k!gpentHmgr的部分 对应进程中每个GDI对象

CVE-2016-0040 滥用GDI 内核提权漏洞_第7张图片
CVE-2016-0040 滥用GDI 内核提权漏洞_第8张图片

而其中每一项都使用 GDICELL64 此结构

CVE-2016-0040 滥用GDI 内核提权漏洞_第9张图片

而通过 一个GDI Handle 我们就可以知道表中的地址

CVE-2016-0040 滥用GDI 内核提权漏洞_第10张图片

在GDICELL64 结构中 pKernelAddress 指向 SURFACE

CVE-2016-0040 滥用GDI 内核提权漏洞_第11张图片
CVE-2016-0040 滥用GDI 内核提权漏洞_第12张图片

在 SURFOBJ 结构中 ,sizlBitmap 它是一个SIZEL 结构 系统 通过 该变量 来确定 bitMap位图的长和宽 而 pvScan0和pvBits成员变量都表示 指向bitMap 位图的指针

在该利用 还有两个 关键的 函数 GetBitmapBits 该函数 主要用于 读取 pvScan0 或者 pvBits 指针指向 的cBytes字节 的Bitmap位图内容 相反 SetBitmapBits 函数 则是对指向的bitMap位图 进行写入

CVE-2016-0040 滥用GDI 内核提权漏洞_第13张图片
CVE-2016-0040 滥用GDI 内核提权漏洞_第14张图片

在一些 paper 中 利用技巧为 创建两个BitMap对象 (Manager/Worker) 之后通过 控制 Manager Bitmap 对象 的 sizelBitmap或者 pvScan0 成员 去 控制 Worker BitMap 对象 pvScan0 成员 的目的 最终实现内核任意内存 读写

总结 来说 就是 Manager 控制 着 Worker 读取 和 写入 的地址,而Worker 则通过 Set和Get BitmapBits 去对 该地址 进行 读写

最关键一点 需要 把Manager 的 pvScan0 指向 Worker的 pvScan0 一般通过漏洞去设置

CVE-2016-0040 滥用GDI 内核提权漏洞_第15张图片

漏洞利用

结合 GDI 利用基本原理 去分析 该漏洞 (分析时 蓝屏真痛苦)

直接 对 nt!WmipReceiveNotifications 下断

CVE-2016-0040 滥用GDI 内核提权漏洞_第16张图片

结合源码 去看 很容易去理解 可以看到 RSI 为本地数组

因为又利用 内核栈喷射技巧 刚开始 调的时候 到后一直 BSOD 浪费了好长时间

CVE-2016-0040 滥用GDI 内核提权漏洞_第17张图片
CVE-2016-0040 滥用GDI 内核提权漏洞_第18张图片

可以看到 栈被喷成 HmangerAddress

CVE-2016-0040 滥用GDI 内核提权漏洞_第19张图片

在 该地址 加 50 的位置 就是 pvScan0

CVE-2016-0040 滥用GDI 内核提权漏洞_第20张图片

跟进去看一下 就是 利用代码中 创建的 位图

CVE-2016-0040 滥用GDI 内核提权漏洞_第21张图片

在当初寻找相关文章的时候,我一直在想漏洞与 该 利用技术 的关系 。文章都说 利用 漏洞 去写 pvScan0 。之后我一直调试去找这个地方 。一直没找到。最终还是在此漏洞 该EXP 并没有这么做 .

在 这位置 它把 pvBits 给了 pvScan0 .可以在看一下 SURFOBJ64结构

CVE-2016-0040 滥用GDI 内核提权漏洞_第22张图片
CVE-2016-0040 滥用GDI 内核提权漏洞_第23张图片

注意 现在 操作的Manager 结构

之后 走几步 该函数 就结束了

CVE-2016-0040 滥用GDI 内核提权漏洞_第24张图片

而此时 的情况 为 pvBits 里为它本身, pvScan0 为pvBits

那在哪把 hManger 的pvScan0 写成 hWorker 的 ,直接对 pvScan0 下写入断点

有时候双击去调试 打印的信息都不显示

CVE-2016-0040 滥用GDI 内核提权漏洞_第25张图片

因为调试过 ,为方便调试 插入了 _debugbreak

CVE-2016-0040 滥用GDI 内核提权漏洞_第26张图片
CVE-2016-0040 滥用GDI 内核提权漏洞_第27张图片

命中 了 硬件断点 查看 下 堆栈 是在 SetBitmapBits 中 进行了 memcopy 。此时看一下 hManger 和hWorker

CVE-2016-0040 滥用GDI 内核提权漏洞_第28张图片

可以看到 已经 写进去了

这里关于为什么能 改变 hManger pvScan0 扯一下

在内核函数 离开之后 可以看到上面的图 当时的 hManger pvScan0 为pvBits 而pvBits 里面又是它自己

当调用 setBitmapBits 时 会先去找到 hManger 的pvScan0---->pvBits--->pvBits 所以在写的时候 会直接 从pvBit地址覆盖 也把pvScan0 覆盖掉了

之后 的利用 就是 读取 system token 内容 写入 当前 进程 token 就不在详细 写了

CVE-2016-0040 滥用GDI 内核提权漏洞_第29张图片

最后

分析有错的地方 请大佬指点下 。之后 把 搜到的 paper 和 源码 附上

内核喷射技术


本文作者:Cestlavie呀(看雪ID)

原文链接:https://bbs.pediy.com/thread-246433.htm



看雪推荐阅读:

1、[翻译]绕过数据执行保护

2、[原创]某Unity3D游戏加固产品分析

3、Nexus6P 7.1.2 内核编译修改 TracerPid

4、[原创]阶乘算法性能分析与 DOUBLE FAULT 蓝屏故障排查 PART I

5、[翻译]VR头戴(HTC Vive)设备内的现实危险

你可能感兴趣的:(CVE-2016-0040 滥用GDI 内核提权漏洞)