首页
社区
课程
招聘
[原创]CVE-2018-8120提权漏洞学习笔记
发表于: 2022-4-11 18:42 21961

[原创]CVE-2018-8120提权漏洞学习笔记

2022-4-11 18:42
21961

该漏洞存在与win32k模块中的SetImeInfoEx函数,在该函数中未对tagWINDOWSTATION结构偏移0x14的spkiList进行有效性验证就对其进行解引用操作,而spkList可以为NULL,此时就会对地址0x14进行解引用操作,导致系统崩溃。通过在0地址申请内存的方式可以绕过解引用产生的系统崩溃,函数会继续向下运行执行写操作,通过这个写操作来修改BitMap对象的pvScan0来实现任意地址读写,最终完成提权。

操作系统:Win7 x86 sp1 专业版

编译器:Visual Studio 2017

调试器:IDA Pro,WinDbg

SetImeInfoEx函数的反汇编结果如下:

图1  SetImeInfoEx函数反汇编

第1处的代码是取出参数pWinStation偏移0x14处的内容,参数pWinStation是tagWINDOWSTATION结构,该结构体定义如下:

偏移0x14处的成员是spkList,该成员指向tagKL结构,结构体定义如下:

获取spkList后,就在第2处进行对spkList偏移0x14处的地址进行解引用操作。可是,在执行2处的代码之前,函数并没有验证spkList是否合法。如果spkList为NULL,那么此时2处的代码就是对地址0x14进行解引用,因为0x14不是个合法地址,就回导致系统崩溃。

要验证这个漏洞,需要两个步骤,首先这个漏洞是由于SetImeInfoEx函数对tagWINDOWSTATION结构体中的spkList成员操作产生的,需要首先调用函数CreateWindowStation来创建一个tagWINDOWSTATION结构体,该函数定义如下:

创建完结构体以后,需要通过SetProcessWindowStation来为当前进程设置创建的tagWINDOWSTATION,该函数定义如下:

为当前进程设置完结构体以后,就可以来触发漏洞,通过IDA交叉引用可以发现,只有NtUserSetImeInfoEx函数调用了SetImeInfoEx函数,该函数的反汇编结果如下:

在第1处,函数将参数内容复制到imeInfoEx中作为调用SetImeInfoEx函数的第二个参数,在2处通过调用GetProcessWindowStation来作为调用SetImeInfoEx函数的第一个参数。那么此时,第一个参数就是创建的tagWINDOWSTATION,第二个参数通过调用NtUserSetImeInfoEx时指定,也就是可以由我们来指定。

NtUserSetImeInfoEx函数是未导出函数,所以只能通过自己指定调用号产生系统调用来调用该函数,而NtUserSetImeInfoEx函数的调用号为0x1226,所以,可以使用如下代码来调用NtUserSetImeInfoEx函数:

由此,可以写出如下的POC:

编译运行POC,此时系统就会崩溃,崩溃信息如下,可以看到,产生崩溃的指令是在对[eax+0x14]的地址进行解引用产生的,这条指令就对应图1中的第2处的代码,此时寄存器eax的值为0,所以执行这条指令时,会对0x14地址进行解引用,该地址是并不是有效地址,就导致了系统的崩溃。

由上内容可知,之所以产生崩溃是因为地址0x14是无效地址,因此,可以通过在0地址申请内存的方式让该地址有效,这样执行图1的第2处代码的时候就不会产生系统的崩溃,继续向下执行。在执行图1的第5处的代码的时候,会产生写操作。函数会向spkList偏移0x2C处保存的地址进行写入操作,由于此时spkList为0,所以函数会向0x2C中保存的地址进行写入操作。写入的内容则由传入的参数决定,这个参数又可以在调用NtUserSetImeInfoEx函数时指定。因此,可以通过将地址0x2C赋值为保存了HalQuerySystemInformation函数地址的地址,在调用NtUserSetImeInfoEx函数时指定参数的前4字节为ShellCode地址的方式,来对保存HalQuerySystemInformation函数地址的地址中的内容修改为ShellCode的地址,这样通过NtQueryIntervalProfile函数就可以调用ShellCode。

根据上述思路,此时就可以用以下的代码来实现提权:

可是此时提权并不会成功,接下来通过WinDbg进行分析,首先运行到之前导致系统崩溃处的指令,也就是图1中第2处执行的代码,可以看到虽然此时eax依然为0,但是因为在0地址申请了内存,此时0x14地址是有效的,所以不会产生系统的崩溃。

继续向下运行,就会执行到图1中第3处的代码,此时会将地址0x2C中保存的要修改的保存了HalQuerySystemInformation函数地址的地址赋值给eax,此时eax就不会为0,函数会继续运行。

继续向下运行就会执行下面的判断指令,判断eax+0x48处保存的内容是否为0,如果不为0则会进行跳转,这两条指令对应的就是图1中的第4处代码。此时eax保存的是保存了HalQuerySystemInformation函数地址的地址,该地址偏移0x48处保存的内容并不为0,这样就跳过了memcpy函数的调用,不会成功执行写入操作,导致程序提权失败。

为了成功利用该漏洞,就需要通过BitMap来实现任意地址的读写操作。Bitmap对象的可以通过CreateBitmap函数创建,该函数定义如下:

当用户态程序通过CreateBitmap函数创建了一个Bitmap对象以后,就会在PEB结构中偏移0x094的成员GdiSharedHandleTable所指的结构体数组中增加一项该结构。

而GditSharedHandleTable所指的是GDICELL结构数组,该结构体定义如下:

其中第一项pKernelAddress指向了SURFACE结构体,该结构体定义如下:

该结构体中保存的前两个成员的结构如下:

其中SURFOBJ结构体中的pvScan0中保存的地址指向内核空间之中,在用户态可以通过GetBitmaps和SetBitmaps函数来对pvScan0指向的内核空间中的内容进行读写操作。此时可以创建两个Bitmap对象,分别是hWorker和hManager,这两个Bitmap对象对应的pvScan0分别是wpv和mpv。通过漏洞的任意地址读写的功能,可以将保存wpv的内存地址复制到mpv中,此时在hManager上调用SetBitmapBits就可以修改wpv中保存的数据,在这里将其修改为保存HalQuerySystemInformation函数地址的地址。此时在hWorker上调用SetBitmapBits就可以将HalQuerySystemInformation函数地址修改为ShellCode地址

这种方式之所以能完成,也是因为此时mpv所指向的地址偏移0x48处的内容为0,另外,因此复制的时候会复制0x15C个字节,所以mpv后面的内容要保证不被修改,就需要在复制的源地址处进行正确的赋值。

此时,漏洞利用的代码如下:


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-7-13 15:27 被1900编辑 ,原因:
收藏
免费 7
支持
分享
打赏 + 100.00雪花
打赏次数 1 雪花 + 100.00
 
赞赏  Editor   +100.00 2022/05/06 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (2)
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
学习了,老师要不要私我一下
2022-4-13 15:14
0
雪    币: 8073
活跃值: (6364)
能力值: ( LV12,RANK:207 )
在线值:
发帖
回帖
粉丝
3
没看明白为什么要创建hWorker和hManager两个Bitmap对象的,可以参考这篇 https://bbs.kanxue.com/thread-225436.htm
2023-1-31 10:08
0
游客
登录 | 注册 方可回帖
返回
//