实现RCE的条件如下:
1.一个普通权限的域账户,用另一台计算机使用该域账户登录加入域环境。其中域账户权限如下
2.域控主机需要能够访问到使用上述配置登录的计算机的一个共享目录,在Windows下可以使用smb实现,用管理员权限的powershell运行以下命令即可
mkdir C:\share icacls C:\share\ /T /grant Anonymous` logon:r icacls C:\share\ /T /grant Everyone:r New-SmbShare -Path C:\share -Name share -ReadAccess 'ANONYMOUS LOGON','Everyone' REG ADD "HKLM\System\CurrentControlSet\Services\LanManServer\Parameters" /v NullSessionPipes /t REG_MULTI_SZ /d srvsvc /f REG ADD "HKLM\System\CurrentControlSet\Services\LanManServer\Parameters" /v NullSessionShares /t REG_MULTI_SZ /d share /f REG ADD "HKLM\System\CurrentControlSet\Control\Lsa" /v EveryoneIncludesAnonymous /t REG_DWORD /d 1 /f REG ADD "HKLM\System\CurrentControlSet\Control\Lsa" /v RestrictAnonymous /t REG_DWORD /d 0 /f
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
运行完命令重启生效。
0x01.2 复现
GitHub上有2个公开的exp,python版本的https://github.com/cube0x0/CVE-2021-1675 和C++版本的https://github.com/afwu/PrintNightmare ,其中C++版本的是从Zhiniang Peng (@edwardzpeng) & Xuefeng Li (@lxf02942370)公开的exp fork来的。
这两个版本的exp原理都是一样的,也都是可用的,其中python版本的exp需要按照说明文档安装exp作者的impacket库,其余不需要修改任何东西。
pip3 uninstall impacket
git clone https://github.com/cube0x0/impacket
cd impacket
python3 ./setup.py install
c++版本的exp需要把第112行UNIDRV.DLL的路径修改为域控主机对应的路径,如笔者这里对应的路径应修改为:
//info.pDriverPath = (LPWSTR)L"C:\Windows\System32\DriverStore\FileRepository\ntprint.inf_amd64_19a3fe50fa9a21b6\Amd64\UNIDRV.DLL";
info.pDriverPath = (LPWSTR)L"C:\Windows\System32\DriverStore\FileRepository\ntprint.inf_amd64_83aa9aebf5dffc96\Amd64\UNIDRV.DLL";
其余不需要修改任何东西,使用vs编译即可。
python版本exp命令及RCE截图:
c++版本exp命令及RCE截图:
===============================================================================
0x02.1 漏洞根原
漏洞的关键在于localspl!SplAddPrinterDriverEx中调用InternalAddPrinterDriverEx加载驱动前的验证ValidateObjectAccess是可以被跳过的。如下localspl!SplAddPrinterDriverEx中的汇编代码为存在漏洞可以导致ValidateObjectAccess被绕过的代码。
.text:0000000180085F25 loc_180085F25: ; CODE XREF: SplAddPrinterDriverEx+3F↑j
.text:0000000180085F25 bt esi, 0Fh ; esi=dwFileCopyFlags
.text:0000000180085F29 mov ebx, 0
.text:0000000180085F2E cmovnb ebx, [rsp+58h+arg_30] ; [rsp+0x90]=1
.text:0000000180085F2E ; CF=1,不进行移位
.text:0000000180085F36 test ebx, ebx
.text:0000000180085F38 jz short loc_180085F64
.text:0000000180085F3A mov rax, cs:pLocalIniSpooler
.text:0000000180085F41 xor r9d, r9d
.text:0000000180085F44 and [rsp+58h+var_30], 0
.text:0000000180085F49 xor r8d, r8d
.text:0000000180085F4C xor ecx, ecx
.text:0000000180085F4E mov [rsp+58h+var_38], rax
.text:0000000180085F53 lea edx, [r9+1]
.text:0000000180085F57 call ?ValidateObjectAccess@@YAHKKPEAXPEAKPEAU_INISPOOLER@@W4SERVER_MANAGEMENT_ACCESS_REQUEST@@@Z ; ValidateObjectAccess(ulong,ulong,void *,ulong *,_INISPOOLER *,SERVER_MANAGEMENT_ACCESS_REQUEST)
…
.text:0000000180085F64 loc_180085F64: ; CODE XREF: SplAddPrinterDriverEx+98↑j
.text:0000000180085F64 ; SplAddPrinterDriverEx+BE↑j
.text:0000000180085F64 and [rsp+58h+var_20], 0
.text:0000000180085F6A mov r9d, esi
.text:0000000180085F6D mov eax, [rsp+58h+arg_28]
.text:0000000180085F74 mov r8, r14
.text:0000000180085F77 mov [rsp+58h+var_28], ebx
.text:0000000180085F7B mov edx, r15d
.text:0000000180085F7E mov [rsp+58h+var_30], eax
.text:0000000180085F82 mov rcx, rdi
.text:0000000180085F85 mov [rsp+58h+var_38], rbp
.text:0000000180085F8A call InternalAddPrinterDriverEx
-
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
其中esi为dwFileCopyFlags,是一个调用者可控的参数,bt esi,0xf将esi中偏移0xf的比特位保存到CF标志位,即CF标志位与esi的0x10比特位相同,dwFileCopyFlags=0x8014时CF=1。cmovnb ebx, [rsp+58h+arg_30]即mov if not below,cmovnb会检测CF标志位是否为0且当CF为0时进行移位操作,此时[rsp+0x90]=1,CF=1不会将ebx赋值为1。调试现场如下
由于ebx=0,jz short loc_180085F64
会跳转到InternalAddPrinterDriverEx处执行后续复制并加载驱动的操作,跳过了0x180085F57处ValidateObjectAccess的检测。
0x02.2 InternalAddPrinterDriverEx
===========================================================================================================
==================================================================
RpcAddPrinterDriverEx会在spoolsv!RpcAddPrinterDriverEx处解析,调用到localspl!LocalAddPrinterDriverEx处的回调,并最终由于localspl!SplAddPrinterDriverEx处的验证ValidateObjectAccess无效导致可以调用到localspl!InternalAddPrinterDriverEx加载驱动并执行。
=================================================================================================================================================================================================================================================================================
调用到localspl!SplAddPrinterDriverEx时的栈回溯如下
0:009> k
# Child-SP RetAddr Call Site
00 0000001f7f83e938 00007ffc
fb225852 localspl!SplAddPrinterDriverEx
01 0000001f7f83e940 00007ff6
6c23ba9f localspl!LocalAddPrinterDriverEx+0xa2
02 0000001f7f83e990 00007ff6
6c215ffe spoolsv!AddPrinterDriverExW+0x6f
03 0000001f7f83e9d0 00007ff6
6c212c71 spoolsv!YAddPrinterDriverEx+0x2ce
04 0000001f7f83ea10 00007ffd
027184a3 spoolsv!RpcAddPrinterDriverEx+0x181
…
2021-6的补丁中在spoolsv!RpcAddPrinterDriverEx中调用YAddPrinterDriverEx加载驱动前加了几处校验,如下右为补丁后的spoolsv.exe。补丁后YIsElevated、RunningAsLUA分别校验了当前用户的token和LUA权限,这两处校验在RCE中可以通过IPC被绕过;YIsElevationRequired检验了HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows NT\Printers\PointAndPrint\NoWarningNoElevationOnInstall的注册表项,但是笔者在2021-6全补丁的Windows server和Windows10系统上均未发现有该注册表项,所以这个缓解在目前来看也是无效的。(这两处缓解可能是针对Yunhai Zhang和ZhiPeng Huo提供的CVE-2021-1675的poc)
随后由于spoolsv!AddPrinterDriverExW调用到localspl!LocalAddPrinterDriverEx处的回调,又由于上述分析的localspl!SplAddPrinterDriverEx中验证无效进入localspl!InternalAddPrinterDriverEx的流程。
localspl!InternalAddPrinterDriverEx主要进行了如下操作,其中%spooler%=C:\Windows\System32\spool\
1.ValidateDriverInfo进行驱动签名等的检查
2.CreateInternalDriverFileArray创建spooler目录下的驱动文件,即%spooler%\drivers\x64
3.GetPrintDriverVersion、CheckFilePlatform检查驱动版本和驱动运行平台
4.SplIsCompatibleDriver进行驱动版本和驱动兼容性检查,驱动版本号只能为3
5.CreateVersionDirectory使用提供的驱动版本号,创建spooler目录下驱动版本号目录,由于驱动版本号只能为3,最终目录为%spooler%\drivers\x64\3
6.CopyFilesToFinalDirectory创建%spooler%\3目录下New、Old文件夹,创建New、Old目录下的临时目录,如%spooler%\drivers\x64\3\Old\1、%spooler%\drivers\x64\3\Old\2;并将上传的驱动移动到临时目录下
7.WaitRequiredForDriverUnload加载6中临时目录下的驱动,路径如%spooler%\drivers\x64\3\old\1\xx.dll
=====================================================================================================
localspl!ValidateDriverInfo在如下代码会校验加载驱动的签名,可以使用0x8000的dwFileCopyFlags绕过,0x8000即RpcAddPrinterDriverEx 的API文档中提到的APD_INSTALL_WARNED_DRIVER,翻译过来即强制加载驱动。
0x02.2.2 CreateInternalDriverFileArray
================================================================================================================
localspl!CreateInternalDriverFileArray中会使用如下代码根据RpcAddPrinterDriverEx 的dwFileCopyFlags参数生成CreateFile的参数,a5=1会使用%spooler%目录下路径做为CreateFile的参数;RCE利用时我们上传的驱动此时是在一个UNC路径下,如笔者本地为\\192.168.18.153\share\rev.dll
,所以这里需要构造dwFileCopyFlags&0x10=1使spooler使用我们的UNC路径。
其中a5参数从localspl!LocalAddPrinterDriverEx这里传入,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IYu2mThc-1625827565106)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]
0x02.2.3 SplIsCompatibleDriver
========================================================================================================
==================================================================
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)
6134ca.png)
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)