CVE-2018-8120 漏洞分析及利用
作者:b2ahex
1. 背景介绍
An elevation of privilege vulnerability exists in Windows when the Win32k component fails to properly handle objects in memory. An attacker who successfully exploited this vulnerability could run arbitrary code in kernel mode. An attacker could then install programs; view, change, or delete data; or create new accounts with full user rights.
To exploit this vulnerability, an attacker would first have to log on to the system. An attacker could then run a specially crafted application that could exploit the vulnerability and take control of an affected system.
The update addresses this vulnerability by correcting how Win32k handles objects in memory.
漏洞原因是在 win32k!SetImeInfoEx 中没有对指针进行验证就直接使用,当指针指向0地址时,系统可能会引用用户层可控的数据,实现任意地址写到任意代码执行的功能。
2. 漏洞分析
对比 kb4103712 补丁前后 win32k.sys 文件,在 win32k!SetImeInfoEx 中增加对指针是否为0的验证。
补丁前:
.text:BF810067 push ebp
.text:BF810068 mov ebp, esp
.text:BF81006A mov eax, [ebp+atagWINDOWStation]
.text:BF81006D test eax, eax
.text:BF81006F jz short loc_BF81008B
.text:BF810071 mov ecx, [eax+14h] ;没有检查ecx,如果下面条件判断成功会跳到loc_BF81008F,执行rep movsd
.text:BF810074 push esi
.text:BF810075 mov esi, [ebp+r3buf]
.text:BF810078 mov edx, [esi]
.text:BF81007A mov eax, ecx
.text:BF81007C
.text:BF81007C loc_BF81007C: ; CODE XREF: SetImeInfoEx(x,x)+21j
.text:BF81007C cmp [eax+14h], edx
.text:BF81007F jz short loc_BF81008F
.text:BF81007F
.text:BF810081 mov eax, [eax+8]
.text:BF810084 cmp eax, ecx
.text:BF810086 jnz short loc_BF81007C
.text:BF810088
.text:BF810088 loc_BF810088: ; CODE XREF: SetImeInfoEx(x,x)+2Fj
.text:BF810088 xor eax, eax
.text:BF81008A
.text:BF81008A loc_BF81008A: ; CODE XREF: SetImeInfoEx(x,x)+43j
.text:BF81008A pop esi
.text:BF81008B
.text:BF81008B loc_BF81008B: ; CODE XREF: SetImeInfoEx(x,x)+Aj
.text:BF81008B pop ebp
.text:BF81008C retn 8
.text:BF81008F ; ---------------------------------------------------------------------------
.text:BF81008F
.text:BF81008F loc_BF81008F: ; CODE XREF: SetImeInfoEx(x,x)+1Aj
.text:BF81008F mov eax, [eax+2Ch]
.text:BF81008F
.text:BF810092 test eax, eax
.text:BF810094 jz short loc_BF810088
.text:BF810096 cmp dword ptr [eax+48h], 0
.text:BF81009A jnz short loc_BF8100A5
.text:BF81009C push edi
.text:BF81009D push 57h
.text:BF81009F pop ecx
.text:BF8100A0 mov edi, eax
.text:BF8100A2 rep movsd
.text:BF8100A4 pop edi
.text:BF8100A5
.text:BF8100A5 loc_BF8100A5: ; CODE XREF: SetImeInfoEx(x,x)+35j
.text:BF8100A5 xor eax, eax
.text:BF8100A7 inc eax
补丁后:
.text:BF810A96 push ebp
.text:BF810A97 mov ebp, esp
.text:BF810A99 mov eax, [ebp+arg_0]
.text:BF810A9C test eax, eax
.text:BF810A9E jnz short loc_BF810AA4
.text:BF810AA0
.text:BF810AA0 loc_BF810AA0: ; CODE XREF: SetImeInfoEx(x,x)+15j
.text:BF810AA0 xor eax, eax
.text:BF810AA2 jmp short loc_BF810AC2
.text:BF810AA4 ; ---------------------------------------------------------------------------
.text:BF810AA4
.text:BF810AA4 loc_BF810AA4: ; CODE XREF: SetImeInfoEx(x,x)+Aj
.text:BF810AA4 mov edx, [eax+14h]
.text:BF810AA7 test edx, edx ;判断edx是否为0
.text:BF810AA9 jz short loc_BF810AA0
.text:BF810AAB push esi
.text:BF810AAC mov esi, [ebp+arg_4]
.text:BF810AAF mov ecx, [esi]
.text:BF810AB1 mov eax, edx
.text:BF810AB3
.text:BF810AB3 loc_BF810AB3: ; CODE XREF: SetImeInfoEx(x,x)+29j
.text:BF810AB3 cmp [eax+14h], ecx
.text:BF810AB6 jz short loc_BF810AC6
.text:BF810AB8 mov eax, [eax+8]
.text:BF810ABB cmp eax, edx
.text:BF810ABD jnz short loc_BF810AB3
没有对 spklList 进行验证,下面访问该指针成员 piiex (+0x2c),如果 spklList 为0,在未开启零页保护的系统下可以通过 NtAllocateVirtualMemory 申请0地址内存,此时+0x2c的指针数据是可控的:
如果该指针+0x48处为0,代码会执行memcpy操作:
目的地址是由0x2c处的指针决定的,源地址来自参数,查看上一层 NtUserSetImeInfoEx v6经过一次memcpy,源数据来自用户层参数:
3. 生成POC
满足 win32k!NtUserSetImeInfoEx 及 win32k!SetImeInfoEx 中执行memcpy的几个条件后构造代码如下:
memcpy前:
memcpy后:
4. 漏洞利用
由于漏洞可以实现任意地址写,可以利用Pool Spraying布置内存,通过 NtAllocateReserveObject 申请大量IOCO对象,由于漏洞会写入的buf长度是0x15c字节,所以free间隔4个对象的长度,通过 NtQuerySystemInformation 泄露出其中一个紧挨着free内存的IOCO对象,从free内存开始写直到覆盖TypeIndex部分,参考,计算要复制的缓存区起点位置 = [该对象地址 - 0xC - 15b], -0xC 处是 TypeIndex )
nt!_OBJECT_HEADER
+0x000 PointerCount : Int4B
+0x004 HandleCount : Int4B
+0x004 NextToFree : Ptr32 Void
+0x008 Lock : _EX_PUSH_LOCK
+0x00c TypeIndex : UChar
+0x00d TraceFlags : UChar
+0x00e InfoMask : UChar
+0x00f Flags : UChar
+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : Ptr32 Void
+0x014 SecurityDescriptor : Ptr32 Void
+0x018 Body : _QUAD
用户层构造buf时注意构造最后0x23个字节的数据,将 TypeIndex 0x0a修改为0,使 CloseProcedure 指向0x60,我们在此处设置shellcode指针,为了保证漏洞正常触发,确保内核地址+0x48处等于0:
将内存布置为理想的状态后,触发漏洞,检查源缓冲区和目标缓冲区:
最终通过调用 CloseHandle 执行 shellcode ,替换 system token 达到提权的效果
多试几次 :)