CVE-2022-21999 Windows Print Spooler 权限提升漏洞分析

0收藏

0点赞

浏览量:878

2022-03-22

举报

  • 漏洞简介

2022 年 2 月,微软修补了 CVE-2022-21999 漏洞Windows Print Spooler 存在权限提升漏洞,经过身份认证的本地攻击者可通过在目标系统上运行特制程序来利用此漏洞,成功利用此漏洞的攻击者可在目标系统上以 SYSTEM 权限执行任意代码。漏洞公开当天,国外安全研究员在网上公开了此漏洞的细节及 PoC,此漏洞为 CVE-2020-1030 漏洞补丁的绕过。经验证,此 PoC 可在 Windows 主机上稳定利用。

 

 

通过阅读 CVE-2020-1030 技术文章,我们可以得知:

 

a. 可通过配置 copyfile 下的 module 值来指定 Point and Print dll,使 Spooler 自动加载这个库,但这个库的路径存在一定限制
b. Spooler 会尝试从系统目录、Spooler 驱动程序目录、环境和驱动程序版本等目录去加载 Point and Print DLL,加载 Point and Print DLL 时,Spooler 会搜索以下路径:

1

2

i. **%SYSTEMROOT%\\System32**

ii. **%SYSTEMROOT%\\System32\\spool\\drivers\\<ENVIRONMENT>\\<DRIVERVERSION>**

c. 通过在打印机上配置SpoolDirectory属性可创建任意且可写的目录,利用这点来创建任意用户可写的 ii. 目录
d. 利用 a. 先后加载 AppVTerminator.dll、目标.dll。其中,加载 AppVTerminator.dll 是为了在配置SpoolDirectory后可以终止 Spooler,等待 Spooler 重启并创建相应目录后将目标 DLL 写入该目录,然后 a. 利用加载该 DLL

  • 漏洞分析

建议先看一下这两篇相关研究文章:

 

https://www.accenture.com/us-en/blogs/cyber-defense/discovering-exploiting-shutting-down-dangerous-windows-print-spooler-vulnerability

 

https://research.ifcr.dk/spoolfool-windows-print-spooler-privilege-escalation-cve-2022-22718-bf7752b68d81

 

由于 CVE-2020-1030 漏洞的 PoC 已公开,我们以 CVE-2020-1030 漏洞为基础开始对新的漏洞进行分析。微软在修补 CVE-2020-1030 漏洞时加入了一些安全验证,使用 CVE-2020-1030 的 PoC 已经无法将 SpoolDirectory 直接设置为 C:\Windows\System32\spool\drivers\x64\4 了,SplSetPrinterDataEx 函数中已添加了通过 IsValidSpoolDirectory 函数校验即将要设置的 SpoolDirectory 的路径的逻辑。

 

 

IsValidSpoolDirectory 函数通过调用 AdjustFileName 函数将路径转换为规范路径,然后检查当前用户是否可以以GENERIC_WRITE访问权限打开或创建目标目录,最终检查目录的链接数是否不大于 1,如果校验通过则返回 True。由于我们对 C:\Windows\System32\spool\drivers\x64\4 没有可写权限所以校验失败。

 

 

根据公开的技术文章,下面我们将使用重解析点绕过 IsValidSpoolDirectory 函数的校验成功设置 SpoolDirectory。我们先将 SpoolDirectory 设置为当前用户可操作的路径,如 C:\Users\test\AppData\Local\Temp\testtest\4,这样就通过 IsValidSpoolDirectory 函数的校验成功设置 SpoolDirectory。然后再创建重解析点,将 C:\Users\test\AppData\Local\Temp\testtest\ 链接到 C:\Windows\System32\spool\drivers\x64,等待 Spooler 创建 Everyone 具有可写权限的目录 C:\Windows\System32\spool\drivers\x64\4。

 

 

Print Spooler 需要重启才能初始化 SpoolDirectory 指定的目录,也就是 C:\Users\test\AppData\Local\Temp\testtest\4 -> C:\Windows\System32\spool\drivers\x64\4。通过配置 Point and Print dll 为 AppVTerminator.dll 来停止 Print Spooler ,等待它重启调用 BuildPrinterInfo 函数(另外,调用 EnumPrinters 函数可加速调用 BuildPrinterInfo)来设置目标文件夹。但在这里又出现一次校验,在 BuildPrinterInfo 函数调用 CreateEverybodySecurityDescriptor、GetSecurityDescriptorDacl、SetSecurityInfo 等函数为目标路径设置 Everyone 可以访问的安全描述符之前,会调用 IsPathALink 和 IsModuleFilePathAllowed 函数进行校验。

 

 

如下图所示,IsModuleFilePathAllowed 函数会判断要目标路径是否在 SystemDirectory 或 DriverDirectory 路径下,如果满足条件就返回 True。其中,SystemDirectory 为 C:\Windows\system32,DriverDirectory 为 C:\Windows\system32\spool\DRIVERS\x64。Print Spooler 在设置目录前需要保证要目标不在系统目录或驱动目录中,满足条件才会去设置目录。由于我们想要创建的目录是 C:\Windows\System32\spool\drivers\x64\4,并不满足条件,也就不能通过 IsModuleFilePathAllowed 函数处的校验。

 

 

为了绕过此处的限制,我们将 SpoolDirectory 设置为 UNC 路径,即 \\localhost\C\$\Users\test\AppData\Local\Temp\testtest\4,而不是 C:\Windows\System32\spool\drivers\x64\4。这样在IsModuleFilePathAllowed 函数中校验的路径就是 UNC\localhost\C\$\Windows\System32\spool\drivers\x64\printers\x64\4,而不是 C:\Windows\System32\spool\drivers\x64\4,就不能通过 _wcsnicmp 函数和 SystemDirectory 或 DriverDirectory 匹配上,从而绕过这个判断。

 

 

当我们有权限向 C:\Windows\System32\spool\drivers\x64\4 写入之后,将想要加载的 DLL 复制到该目录,然后通过 SetPrinterDataEx 函数配置 Point and Print dll 为 目标 DLL。Print Spooler 服务会调用 localspl!SplSetPrinterDataEx 函数进行处理,最终会在 SplLoadLibraryTheCopyFileModule 函数中调用 LoadLibraryW 函数来加载相应模块,在调用 LoadLibraryW 函数之前会通过 IsModuleFilePathAllowed 函数来判断目标 DLL 是否在 C:\Windows\system32、C:\Windows\system32\spool\DRIVERS\x64 以及 C:\Windows\system32\spool\DRIVERS\x64\4 目录下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

// SplSetPrinterDataEx     

    if ( !v11

      && !wcsncmp(pKeyName, L"CopyFiles\\"0xAui64)

      && !(*(_DWORD *)(*((_QWORD *)hPrinter + 0x15+ 168i64) & 0x4000000) )

    {

      SplCopyFileEvent(hPrinter, pKeyName);

    }

 

// SplCopyFileEvent

    if ( !(unsigned int)SplGetPrinterDataEx(

                          hPrinter,

                          v7,

                          L"Module",

                          (unsigned int *)&v20,

                          (unsigned __int8 *)pszModule,

                          v19,

                          &v19)

      && v20 == 1 )

    {

      v3 = CreateFullyQualifiedNameFromPSpool(hPrinter, &v18);

      if ( v3 )

      {

        v10 = SplLoadLibraryTheCopyFileModule((__int64)hPrinter, pszModule);

 

// SplLoadLibraryTheCopyFileModule

        if ( (unsigned int)GetDriverDirectory(&DriverDirectory, MaxLength, pIniEnvironment, 0i64, pIniSpooler) )

        {

          if ( !(unsigned int)MakeCanonicalPath(pszModule, &LibFileName)

            || !(unsigned int)IsModuleFilePathAllowed(&LibFileName, &DriverDirectory)

            || (hModule = LoadLibraryW(&LibFileName)) == 0i64 )

          {

            if ( (unsigned int)GetIniDriverAndDirForThisMachineEx(

                                 *(_QWORD *)(hPrinter + 0x40),

                                 MaxLength,

                                 &IniDriverAndDir,

                                 (struct _INIDRIVER **)&hPrinter,

                                 pIniEnvironment) )

            {

              v12 = StringCchCopyW(&IniDriverModulePath, 0x104i64, &IniDriverAndDir);//

                                              // 0:004> du beddc0   //IniDriverAndDir

                                              // 00000000`00beddc0  "C:\Windows\system32\spool\DRIVER"

                                              // 00000000`00bede00  "S\x64\4\"

              v6 = StatusFromHResult(v12);

              if ( !v6 )

              {

                v13 = StringCchCatW(&IniDriverModulePath, 0x104i64, pszModule);

                v6 = StatusFromHResult(v13);

                if ( !v6 )

                {

                  if ( (unsigned int)MakeCanonicalPath(&IniDriverModulePath, &LibFileName) )

                  {

                    if ( (unsigned int)IsModuleFilePathAllowed(&LibFileName, &IniDriverAndDir) )

                    {

                      hModule = LoadLibraryExW(&LibFileName, 0i64, v6 + 8);

                      if ( !hModule )

                        v6 = GetLastError();

                    }

                  }

                }

IsModuleFilePathAllowed 函数中判断目标是否在 C:\Windows\System32 目录或 DriverDirectory/IniDriverAndDir 目录或子目录下,所以我们可以利用漏洞在 C:\Windows\system32\spool\DRIVERS\x64 目录下创建可写任意文件夹(如 C:\Windows\system32\spool\DRIVERS\x64\5 ),然后将目标 DLL 复制过去并利用 Point and Print 来加载,如下图所示:

 

  • 补丁分析

以下为补丁前后版本的修改情况,BuildPrinterInfo 函数发生了变化。

 

 

补丁前,在 BuildPrinterInfo 函数中会先尝试创建目标目录,同时共享 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE 权限(即 FILE_SHARE_VALID_FLAGS),成功执行后获得句柄(hObject)。如果创建失败的话会尝试以 FILE_SHARE_VALID_FLAGS 权限打开该目录,如果当前用户没有相应权限也会打开失败。在通过IsPathALink 函数及 IsModuleFilePathAllowed 函数校验之后,会调用 SetSecurityInfo(hObject, SE_FILE_OBJECT, 4u, 0i64, 0i64, pDacl, 0i64) 函数来设置该目录。

 

 

补丁后的 BuildPrinterInfo 函数只是尝试以 FILE_SHARE_READ 权限打开目标目录,并且删掉了之前使用 CreateEverybodySecurityDescriptor、GetSecurityDescriptorDacl、SetSecurityInfo 等函数为目标路径设置 Everyone 读写访问、执行等所有可能的访问权限,也就是不会再去创建新的 SpoolDirectory 了,也不会修改原本目录的权限。

 



(来源:看雪论坛)

(原文链接:https://bbs.pediy.com/thread-271956.htm

发表评论

点击排行

钓鱼邮件-如何快速成为钓鱼达人

一、前言在大型企业边界安全做的越来越好的情况下,不管是 APT 攻击还是红蓝对抗演练,钓鱼邮件攻击使用的...

【渗透实战系列】| 1 -一次对跨境赌博类APP的渗透实战(getshell并获得全部数据)

本次渗透实战主要知识点:1.app抓包,寻找后台地址2.上传绕过,上传shell3.回shell地址的分析4.中国蚁剑工...

HTTPS - 如何抓包并破解 HTTPS 加密数据?

HTTPS 在握手过程中,密钥规格变更协议发送之后所有的数据都已经加密了,有些细节也就看不到了,如果常规的...

无线电安全攻防之GPS定位劫持

一、需要硬件设备HackRFHackRF 连接数据线外部时钟模块(TCXO 时钟模块)天线(淘宝套餐中的 700MHz-2700MH...

记一次Fastadmin后台getshell的渗透记录

1.信息搜集先来看看目标站点的各种信息后端PHP,前端使用layui,路由URL规则看起来像ThinkPHP,那自然想到...

华为防火墙实战配置教程,太全了

防火墙是位于内部网和外部网之间的屏障,它按照系统管理员预先定义好的规则来控制数据包的进出。防火墙是系...

ADCS系列之ESC1、ESC8复现

对原理感兴趣的可以去https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf看原文,这里只...

【干货分享】利用MSF上线断网主机的思路分享

潇湘信安&nbsp;Author 3had0w潇湘信安一个不会编程、挖SRC、代码审计的安全爱好者,主要分享一些安全经验、...

扫描二维码下载APP