Aggregator
The Story Of CVE-2021-1648
Author: k0shl of 360 Vulcan Team
SummaryIn January 2021 patch tuesday, MSRC patched a vulnerability in splwow64 service, assigned to CVE-2021-1648(also known as CVE-2020-17008), which merged my two interesting cases which bypass the patch of CVE-2020-0986, one of them also be found by Google Project Zero((https://bugs.chromium.org/p/project-zero/issues/detail?id=2096).actually this include one EoP and two info leak cases.
This vulnerability was planned to patch in October 2020, but MSRC seems found some other serious security problems in service, so they postpone the patch for four months.
BackgroundIn this blog, I don't want to talk more about the mechanism of splwow64, there are a lot of analysis of CVE-2020-0986 before, so let's focus on the vulnerability.
After CVE-2020-0986 had been patched, I make a quick bindiff on splwow64 and gdi32full, and found there are two check added after patch.
One is that Microsoft added two printer handle(or aka cookie?) check functions named "FindDriverForCookie" and "FindPrinterHandle", it will check printer driver handle which store in a global variable.
__int64 __fastcall FindDriverForCookie(__int64 a1) { v3 = qword_1800EABA0; if ( qword_1800EABA0 ) { do { if ( a1 == *(_QWORD *)(v3 + 56) ) //check driver index break; v3 = *(_QWORD *)(v3 + 8); } while ( v3 ); if ( v3 ) ++*(_DWORD *)(v3 + 44); } RtlLeaveCriticalSection(&semUMPD); return v3;// return driver heap } __int64 *__fastcall FindPrinterHandle(__int64 a1, int a2, int a3) { for ( i = *(__int64 **)(v3 + 64); i && (*((_DWORD *)i + 2) != v5 || *((_DWORD *)i + 3) != v4); i = (__int64 *)*i ) //check printer handle ; }Another is that MSRC added two pointer check functions "UMPDStringPointerFromOffset" and "UMPDPointerFromOffset" to check if pointer is validate.
FindDriverForCookie and FindPrinterHandle bypassFirst, I don't know the purpose that Microsoft add FindDriverForCookie and FindPrinterHandle, maybe it's not for mitigation? After quick review, I found there is a command named 0x6A that can set printer handle which the value we can controll in global variable of service to bypass this two check functions.
__int64 __fastcall bAddPrinterHandle(__int64 a1, int a2, int a3, __int64 a4) { v9 = RtlAllocateHeap(*(_QWORD *)(__readgsqword(0x60u) + 48), 0i64, 24i64); v10 = (_QWORD *)v9; if ( v9 ) { *(_DWORD *)(v9 + 8) = v6; *(_DWORD *)(v9 + 12) = v5; *(_QWORD *)(v9 + 16) = v8; RtlEnterCriticalSection(&semUMPD); *v10 = *(_QWORD *)(v4 + 0x40); v7 = 1; *(_QWORD *)(v4 + 0x40) = v10; //add print handle which can be controlled by user RtlLeaveCriticalSection(&semUMPD); } return v7; }By invoking command 0x6A, function bAddPrinterHandle will add print handle to driver heap which stored in global variable |qword_1800EABA0|.
//set print handle to 0xdeadbeef00006666 0:007> p gdi32full!bAddPrinterHandle+0x54: 00007ff8`380fc3bc 44897808 mov dword ptr [rax+8],r15d ds:00000000`0108a428=00000000 0:007> p gdi32full!bAddPrinterHandle+0x58: 00007ff8`380fc3c0 4489700c mov dword ptr [rax+0Ch],r14d ds:00000000`0108a42c=00000000 0:007> r r14d r14d=deadbeef 0:007> r r15d r15d=6666 //driver heap stored in global variable 0:007> dq gdi32full+0xEABA0 l1 00007ff8`381baba0 00000000`0108d000 0:007> dq 108d000+0x40 l1 00000000`0108d040 00000000`0108a420 0:007> dq 108a420+0x8 l1 00000000`0108a428 deadbeef`00006666So we can easy bypass printer handle check during invoking Command 0x6D, and hit the vulnerability code.
case 0x6Du: v31 = FindDriverForCookie(*(_QWORD *)(v6 + 24)); v32 = v31; if ( !v31 ) goto LABEL_137; v33 = FindPrinterHandle(v31, *(_DWORD *)(v6 + 32), *(_DWORD *)(v6 + 36)); ... [vulnerability code] CVE-2021-1648: arbitrary address readLet's talk about information disclosure, CVE-2020-1648 includes a arbitrary address read information disclosure.
if ( v51 != -1 ) { v57 = **(unsigned __int16 ***)(v6 + 0x50); //not check v57 if ( v57 ) { v58 = v57[34]; v59 = v58 + v57[35]; if ( (unsigned int)v59 >= v58 && (unsigned int)v59 <= 0x1FFFE ) memcpy_0(*(void **)(v6 + 88), v57, v59); //arbitrary address read } }The code of case Command 0x6D is too long, so I won't post all of them in my blog. In short, it will check destination address of memcpy if it's in "validate" range, the range of |v6+0x58|, but source address |v57| isn't checked, so we can read arbitrary address.
0:007> r rax=0000000000868a00 rbx=000000000001fffe rcx=0000000000000000 rdx=4141414141414141 rsi=0000000000150200 rdi=00000000008688d0 rip=00007ff9fc008403 rsp=000000000210f480 rbp=000000000210f4f9 r8=100297f000000002 r9=000000000022f000 r10=00000fff3c9c801d r11=000000000210f350 r12=0000000000868920 r13=0000000000868910 r14=0000000000000001 r15=0000000000461c50 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206 gdi32full!GdiPrinterThunk+0x1a73: 00007ff9fc008403 0fb74a44 movzx ecx,word ptr [rdx+44h] ds:4141414141414185=????Stack trace:
0:007> k Child-SP RetAddr Call Site 000000000210f480 00007ff7558e78ab gdi32full!GdiPrinterThunk+0x1a73 000000000210f560 00007ff7558e84de splwow64+0x78ab 000000000210f650 00007ff7558e9f28 splwow64+0x84de 000000000210f6b0 00007ff9fe3f2e93 splwow64+0x9f28 000000000210f6e0 00007ff9fe3f45b4 ntdll!RtlDeleteCriticalSection+0x363 000000000210f730 00007ff9fc487bd4 ntdll!RtlInitializeResource+0xce4 000000000210faf0 00007ff9fe42ce51 KERNEL32!BaseThreadInitThunk+0x14 000000000210fb20 0000000000000000 ntdll!RtlUserThreadStart+0x21 Another two cases of CVE-2021-1648Another two cases I reported to MSRC is about bypassing offset check functions "UMPDStringPointerFromOffset" and "UMPDPointerFromOffset", I think MSRC made a mistake in these two functions range check.
Splwow64 is a specail service which is compatible with x86 in x86-64 Windows OS, so it always allocate heap which is 32bits, but in CVE-2020-0986 patch, "UMPDStringPointerFromOffset" and "UMPDPointerFromOffset" only check if offset and |portview+offset| is less than 0x7fffffff.
signed __int64 __fastcall UMPDPointerFromOffset(unsigned __int64 *a1, __int64 a2, unsigned int a3) { [...] if ( v3 <= 0x7FFFFFFF && v3 + a3 <= 0x7FFFFFFF ) { *a1 = v3 + a2; return 1i64; } [...] } signed __int64 __fastcall UMPDStringPointerFromOffset(unsigned __int64 *a1, __int64 a2) { [...] if ( v3 > 0x7FFFFFFF ) goto LABEL_12; v4 = (0x7FFFFFFF - v3) >> 1; *a1 = v3 + a2; v5 = (unsigned int)v4; if ( v3 + a2 ) v2 = wcsnlen((const wchar_t *)(v3 + a2), (unsigned int)v4); [...] return result; }But in splwow64 service, so many heaps even stack is allocated in low address, like this:
0:004> pc splwow64!TLPCMgr::ProcessRequest+0x99: 00007ff6`846d7c71 e826490000 call splwow64!operator new[] (00007ff6`846dc59c) 0:004> p splwow64!TLPCMgr::ProcessRequest+0x9e: 00007ff6`846d7c76 488bf0 mov rsi,rax 0:004> r rax rax=00000000007d7c70 0:004> r rsp rsp=000000000217f400So it is possible to exploit through occupy to some important heaps or stack in splwow64 service, I suggest MSRC in my report to check range of pointer if it's in portview section instead of 0x7fffffff.
two cases crash dump:
0:006> r rax=0000000000000000 rbx=00000000012f8360 rcx=000000001363d9e0 rdx=00000000012f8360 rsi=0000000002d60200 rdi=000000001363d9d8 rip=00007fff728956d2 rsp=0000000002cdf230 rbp=0000000000000001 r8=0000000000000028 r9=0000000012345678 r10=000000007fffffff r11=2222222222222222 r12=00007fff57ea8fe0 r13=0000000001208210 r14=000000000120aa50 r15=00007fff72860000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206 gdi32full!UMPDStringPointerFromOffset+0x12: 00007fff728956d2 4c8b09 mov r9,qword ptr [rcx] ds:000000001363d9e0=???????????????? 0:006> r rax=0000000000000001 rbx=0000000001628360 rcx=0000000042a3c4a1 rdx=0000000001628360 rsi=0000000000ff0200 rdi=0000000000000000 rip=00007fff7289568a rsp=0000000002ecf3d8 rbp=0000000000000001 r8=0000000000000028 r9=0000000041414141 r10=000000007fffffff r11=2222222222222222 r12=00007fff57ea8fe0 r13=0000000001407160 r14=000000000140a000 r15=00007fff72860000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206 gdi32full!UMPDPointerFromOffset+0xa: 00007fff7289568a 4c8b09 mov r9,qword ptr [rcx] ds:0000000042a3c4a1=???????????????? The end of storyIt seems Microsoft redsigned splwow64 printer service, so they postponed the patch for four months, it's really a long time for me to wait a patch since I started my researching on Windows. Hope new printer service will be more secure:P.
Timeline
2020-07-27 Reported to MSRC.
2020-08-19 MSRC decided to put off patch.
2020-08-22 Bounty awarded
2021-01-13 Patch release
通过SMB进行横向移动
友情转载丨安全客2020年度第4季季刊发布
VIPKID SRC邀您共读安全客2020季刊—第4季!
Emotet: A Year in the Life of a Malware
在Cobalt Strike BOF中进行直接系统调用
安全不只是渗透
RedTeamTricks
CobaltStrike内层组件分析(一)
Freebsd UMA内核堆安全特性解读
Workflow的代码注入导致SharePoint RCE(CVE-2020-0646)
我的 2020
记一次shiro反序列化漏洞getshell
The Domain Name System: A Cryptographer’s Perspective
This is the first in a multi-part blog series on cryptography and the Domain Name System (DNS). As one of the earliest protocols in the internet, the DNS emerged in an era in which today’s global network was still an experiment. Security was not a primary consideration then, and the design of the DNS, like […]
The post The Domain Name System: A Cryptographer’s Perspective appeared first on Verisign Blog.