一 漏洞简介
前段时间,微软公布 Windows PrintNightmare 两个安全漏洞,分别为CVE-2021-1675和CVE-2021-34527。公布几天后,minikatz 率先工具化集成了 CVE-2021-1675 和 CVE-2021-34527 的 EXP。通过查看 minikatz 源码,在 CVE-2021-1675 的 EXP 中,调用的 RPC 函数为 RpcAddPrinterDriverEx;在 CVE-2021-34527 的 EXP 中,调用的 RPC 函数为 RpcAsyncAddPrinterDriver。系统处理这两个 RPC 函数后,都调用了 YAddPrinterDriverEx 函数,但是没有对参数dwFileCopyFlags进行条件判断。由此可设置 APD_INSTALL_WARNED_DRIVER 标志,使添加打印机驱动时,以高权限加载 DLL。
(补丁前后对比:Windows PrintNightmare 漏洞和补丁分析)
本文复现和分析的环境为 Windos Server 2016 Standard。通过afwu 发布的 EXP进行修改修改,复现 CVE-2021-1675。复现文章也挺多,这里推荐一篇,不复现了。值得一提的是,在复现时,除了修改 UNIDRV.DLL 文件路径以外,还需要修改 pConfigFile 赋值的路径。如果采用 1 方式的路径,由于在打开文件句柄之后执行驱动文件的更新,Old 目录还未生成,所以打开句柄时,找不到文件。采用 2 方式的路径,文件句柄打开后,并未释放处于占用状态,导致后面加载 DLL 的时候,加载失败。
通过 Process Moniter 抓取到 EXP 加载 DLL 时,执行过程中的堆栈调用。
该漏洞点在于,调用 YAddPrinterDriverEx 函数时,没有对参数 dwFileCopyFlags 做校验,能够使用 APD_INSTALL_WARNED_DRIVER 标志,导致后面对驱动合法性校验失效,可以任意的加载 DLL,并且为 system 权限。
同样通过 process moniter 抓取 mimikatz 中 CVE-2021-34527 的 EXP 执行数据,加载 DLL 的堆栈调用。
通过地址可以找到,spoolsv.exe 的调用的处理函数为 NThreadingLibrary::TWorkCrew::tpSimpleCallback。
继续调用 TFunction4<unsigned short _,_DRIVER_CONTAINER _,unsigned long,enum Call_Route>::Run(__int64 a1)函数,最后调用 YAddPrinterDriverEx。由此可知 CVE-2021-34527 和 CVE-2021-1675,最终都是调用 YAddPrinterDriverEx 函数,只是 RPC 调用不同,所以可以说这个漏洞是 CVE-2021-1675 的补丁绕过。引发思考,如果再有不同 RPC 调用了 YAddPrinterDriverEx 函数,也是能绕过 CVE-2021-1675 补丁的。
EXP 会通过 RpcBindingSetAuthInfoExW 函数,绑定句柄的认证,授权和安全质量的服务信息。当函数执行成功时,identity.User 设置用户名,代表了权限。如果是低权限用户,执行 ValidateObjectAccess 函数后结果为 0,administrator 用户的权限执行 ValidateObjectAccess 函数后结果为 1。
CVE-2021-1675 是逻辑漏洞,通过 RPC 添加打印机驱动程序的时候,参数 dwFileCopyFlags(v7)的标志位 APD_INSTALL_WARNED_DRIVER(0x8000)为 1 时,_bittest 函数的结果为 1,则 v12 被赋值为 0,从而不执行 ValidateObjectAccess 的检查。
但在 Server 12 的中,a7 的值是固定为 1,一定会执行 ValidateObjectAccess 检测。
MyName:检查驱动名称是否合法,ValidateDriverInfo 的执行流程。
检查是否为本地文件
核对初始化 key
校验驱动文件的合法性
但是当 dwFileCopyFlags 含有 APD_INSTALL_WARNED_DRIVER(0x8000)标志位时,dwFileCopyFlags & 0x8000 的结果为 0x8000,0x8000 取非后值为 0,将会跳过驱动进一步的校验。
v13 的值是由 dwFileCopyFlags 的低 8 位取反后,右移 4 位,再跟 1 做与运算得出。v13 的值决定 CreateInternalDriverFileArray 函数的第 5 个参数(a5)。经过计算,当 dwFileCopyFlags 低 8 位的值为 0x1X 时(X 可为 0-F 中任意值),可以使 a5 为 0。当 a5 的值为 0,可以规避对驱动文件的合法性检查。
通过 CreateFile 打开文件,得到 3 个文件句柄,并保存到 DllAllocSplMem 申请的空间中,用于后面文件更新使用。
dwFileCopyFlags 的成员 pConfigFile、pDataFile、pDriverPath 分别保存了配置文件、数据文件、驱动文件的文件路径。将上述成员中的文件路径下的文件,移动到 C:\Windows\System32\spool\drivers\x64\3 下时,需要经过以下操作:
文件句柄的文件信息进行核对
将文件拷贝到 C:\Windows\System32\spool\drivers\x64\3\new
通过 WaitRequiredForDriverUnload 函数下的 MoveNewDriverRelatedFiles 函数,将 spool\drivers\x64\3 下的同名文件移动到 spool\drivers\x64\3\old\x (x∈[1,3] ) 目录下,再把 spool\drivers\x64\3\new 目录文件,移动到 spool\drivers\x64\3 下,从而实现更新文件。
通过更新 Config 文件,将 spool\drivers\x64\3 下新的配置文件加载起来。
CVE-2021-1675 绕过用户权限检查,可以低用户的权限添加打印机驱动,由于是 RPC 执行打印机添加驱动,所以也能称为 RCE 漏洞。不过实现 RCE 条件是比较苛刻的,至少需要有域控的普通用户,且还需要有共享目录。个人认为该漏洞可用于持久化的操作,得到域控的 administrator 的账号和密码时,在有共享目录、能访问到域控的情况下,打上 6 月份的补丁,也能远程的加载共享目录下的 DLL。
[1]https://www.freebuf.com/vuls/279876.html