CVE-2021-1648:Windows 10 splwow64权限提升分析

 

近日,作者分析了微软1月补丁日修复的一个漏洞CVE-2021-1648,这是一个可以在splwow64进程空间进行任意地址读写的漏洞。由于splwow64是一个普通权限的进程且splwow64在IE浏览器的白名单目录里,因此这个漏洞可以用于提权低权限的IE浏览器渲染引擎进程从而绕过IE浏览器的沙箱。这篇文章主要介绍一下splwow64的机制和CVE-2021-1648的成因。

环境:Windows10 20H2 2020-12补丁

 

splwow64机制

​ wow64是微软为了在64bit系统兼容运行32bit程序的一个组件,具体来说,在64bit程序调用32bit的CreateDC Windows会用splwow64.exe处理这个调用,64bit程序与splwow64.exe使用lpc进行通信。

​ splwow64在splwow64!GDIThunkingVIALPCThread里调用NtCreatePort开启处理信息的端口,在这里我们可以计算得到调用NtSecureConnectPort与splwow64通信的端口名称

​ 我们可以从xp sp1代码\NT\base\ntos\lpc\lpcconn.c中NtSecureConnectPort的注释中得到一些lpc通信机制的信息,这里摘抄部分注释如下

1、NtSecureConnectPort通过PortName参数连接server端口,PortName必须与NtCreatePort指定的一致
2、server端通过NtListenPort接收请求,client端在server端接收处理请求、返回NtCompleteConnectPort执行结果前阻塞
3、server在接受请求后返回给client端一个参数PortHandle,PortHandle与名称无关与client进程有关。client使用PortHandle调用NtRequestWaitReplyPort从server端接收/发送信息

​ splwow64在splwow64!TLPCMgr::ProcessRequest处理接收的消息,这里过滤了传入消息的长度,只处理DataSize=0x20长度的消息,

并将某些类型合法消息的[0x30],[0x40],[0x38]作为参数传递给Gdi32full!GdiPrinterThunk,这里传递的参数都是调用者可控的。

 

漏洞分析

​ CVE-2021-1648出现在0x6d消息的处理过程中,CVE-2021-1648是在CVE-2020-0986补丁的基础上出现的,CVE-2020-0986的补丁在gdi32full!GdiPrinterThunk里主要加了两个缓解FindDriverForCookie、FindPrinterHandle和UMPDPointerFromOffset、UMPDStringPointerFromOffset,由于Gdi32full!GdiPrinterThunk传入的参数可控,这两个缓解实际上都是可以绕过的。

FindDriverForCookie、FindPrinterHandle绕过

​ Gdi32full!FindDriverForCookie的功能是根据传入的值a1,从一个全局变量gdi32full+EABA0里遍历得到偏移[7]的位置为a1的地址并返回。

​ Gdi32full!FindPrinterHandle的功能是根据传入的参数a1(地址值)、a2、a3,从a1偏移0x40的位置遍历,返回该地址偏移2*4=a2且3*4=a3的偏移值。

这两处缓解可以通过0x6a消息调用一次gdi32full!bAddPrinterHandle来绕过,gdi32full!bAddPrinterHandle调用时参数如下,其中第二、三个参数是调用者可控的。

​ gdi32full!bAddPrinterHandle的功能是把传入的a2、a3写入到global_heap偏移2*4、3*4的位置。需要注意的是这里(QWORD\)(global_heap+0x40)的位置实际上和上文Gdi32full!FindPrinterHandle中*(a1+0x40)相同,

再看一下0x6d消息是如何调用FindPrinterHandle的,这里的参数二、三是我们可控的,这样在gdi32full!bAddPrinterHandle调用中(QWORD\)(global_heap+0x40)写入的内容和gdi32full!FindPrinterHandle中要寻找的内容位置相同且都可控,我们就可以绕过gdi32full!FindPrinterHandle这个检查。

UMPDPointerFromOffset、UMPDStringPointerFromOffset绕过

​ CVE-2020-0986补丁加的另一个缓解是检查指针是否在32bit的范围,

​ 这个缓解可以绕过的原因是splwow64.exe与32bit兼容,所以splwow64中堆栈地址都是32bit范围内的,由于lpc通信过程中server端会开辟内存并拷贝client端传入的消息,我们构造0x6d畸形请求,

LpcRequest.PtrMsgSend = (UINT64)ClientView.ViewRemoteBase;

即发送消息的地址设置为lpc server端的堆地址即可绕过这一缓解。

splwow64中执行server端开辟内存拷贝的过程在splwow64!TLPCMgr::ProcessRequest

任意地址读写

​ 绕过了上述两个缓解,splwow64会调用一个src可控、dst可控的memcpy,由于这里memcpy的src和dst没有检查范围,我们可以通过修改src读dst的内容进行任意地址读,修改dst为目标地址、修改src为目标内容进行任意地址写。

 

总结

​ 这里介绍了splwow64的一些机制并分析了CVE-2021-1648的成因,重点分析了微软针对CVE-2020-0986补丁所加的两个缓解机制的绕过,希望读者读完能有所收获。

 

参考

https://bugs.chromium.org/p/project-zero/issues/detail?id=2096

https://whereisk0shl.top/post/the_story_of_cve_2021_1648

(完)