PrintNightmare
時間線
2021年6月29號:深信服安全研究員在Github上分布了名為《
PrintNightmare (CVE-2021-1675): Remote code execution in Windows Spooler Service》相關POC和漏洞信息
2021年6月30號:安全研究員@cube0x0在github上分布了使用Impacket的pyhton EXP。
2021年7月1號:安全研究員@cube0x0在github更新了C# Implementation of CVE-2021-1675的EXP。
2021年7月2號:微軟披露Windows Print Spooler Remote Code Execution VulnerabilityCVE-2021-34527,表示已知該漏洞存在在野利用。該漏洞目前為零日狀態,微軟暫未發布修復。
...部分安全廠商把在github上披露的《PrintNightmare (CVE-2021-1675): Remote code execution in Windows Spooler Service》認為是新的CVE-2021-34527,也有部分廠商認為不是。
個人認為PrintNightmare (CVE-2021-1675): Remote code execution in Windows Spooler Service》為新的CVE-2021-34527,所以下面統一稱PrintNightmare (CVE-2021-1675): Remote code execution in Windows Spooler Service》為新的CVE-2021-34527。。。。。
本人也是第一次分析漏洞,可能存在錯誤,希望大家多多包涵。
Print Spooler
Print Spooler是管理打印過程的可執行文件。打印管理涉及檢索正確打印機驅動程序的位置、加載該驅動程序、將高級函數調用假脫機到打印作業中、安排打印作業進行打印等。后臺處理程序在系統啟動時加載并繼續運行,直到操作系統關閉。
Print spooler 是一種管理打印過程的軟件服務。后臺處理程序接受來自計算機的打印作業并確保打印機資源可用。
任何經過身份驗證的用戶都可以遠程連接到域控制器打印后臺處理程序服務,并請求更新新的打印作業。
Print Spooler歸 SYSTEM 所有
微軟一般建議禁用:
域控制器和 Active Directory 管理系統需要禁用打印后臺處理程序服務。推薦的方法是使用組策略對象 (GPO)。
如果啟用了Print Spooler服務,可以使用一些已知的 AD 憑據向域控制器的打印服務器請求新打印作業的更新,并告訴它向某個系統發送通知。當打印機將通知發送到任意系統時,它需要針對該系統進行身份驗證。
因此,我們可以使Print Spooler服務針對任意系統進行身份驗證,并且該服務將在此身份驗證中使用計算機帳戶。
RpcAddPrinterDriver
向服務器添加打印機驅動程序 (RpcAddPrinterDriver),RpcAddPrinterDriver可以在在打印服務器上安裝打印機驅動程序并鏈接配置、數據和打印機驅動程序文件。
主要語法為:
DWORD RpcAddPrinterDriver( [in, string, unique] STRING_HANDLE pName, [in] DRIVER_CONTAINER* pDriverContainer
pName:
此參數是一個指向字符串的指針,該字符串指定該方法所操作的打印服務器的名稱。這必須是遠程過程調用 (RPC) 綁定到的域名系統 (DNS)、 NetBIOS、 互聯網協議版本 4 (IPv4)、互聯網協議版本 6 (IPv6)或通用命名約定 (UNC)名稱,并且它必須唯一標識網絡上的打印服務器。
pDriverContainer:
該參數是指向 指定打印機驅動程序 信息的DRIVER_CONTAINER結構的指針。DRIVER_CONTAINER 結構的Level成員的值必須是 0x00000002、0x00000003、0x00000004、0x00000006 或 0x00000008。
返回值:
成功返回零 (ERROR_SUCCESS) ,失敗返回非零 Windows 的錯誤代碼
1.收到此消息后,服務器必須執行以下指定的驗證步驟:
打印服務器名稱參數。DRIVER_CONTAINER 參數。
2.然后驗證參數
驗證該cVersion所述的構件DRIVER_INFO 結構包含在由指向DRIVER_CONTAINER pDriverContainer 比0x00000004嚴格以下。如果此驗證失敗,則返回 ERROR_PRINTER_DRIVER_BLOCKED。
驗證pDriverContainer 參數 指向的 DRIVER_CONTAINER 中包含的DRIVER_INFO結構的pEnvironment成員不是“Windows ARM”。如果此驗證失敗,則返回 ERROR_NOT_SUPPORTED。
如果打印客戶端請求的安裝是打印機驅動程序升級,打印服務器應該執行以下額外的驗證步驟:
驗證當前安裝的打印機驅動程序不是類打印機驅動程序。驗證如果當前安裝的打印機驅動程序的驅動程序版本為 0x00000004,則當前安裝的打印機驅動程序沒有更新的驅動程序日期,或者如果驅動程序日期相同,則當前安裝的打印機驅動程序沒有更新的制造商 -提供的驅動程序版本號。驗證如果當前安裝的打印機驅動程序的驅動程序版本為 0x00000004,則打印服務器上沒有打印機共享并且也使用當前安裝的打印機驅動程序。
如果此驗證失敗,打印服務器必須返回 ERROR_PRINTER_DRIVER_BLOCKED。
3.如果參數驗證失敗
服務器必須立即使操作失敗并向客戶端返回一個非零錯誤響應。否則,服務器必須按如下方式處理消息并向客戶端發送響應:
將打印機驅動程序文件復制到目的地。如果復制操作失敗,服務器必須立即使調用失敗并向客戶端返回一個非零錯誤響應。創建打印機驅動程序對象,使用特定于實現的機制來確定打印機驅動程序對象的每個屬性的布爾值。<313>如果任何客戶端注冊了服務器對象更改的通知,則必須向它們廣播通知。返回操作的狀態。
添加或更新打印機驅動程序
要將打印機驅動程序 (“OEM 打印機驅動程序”)添加或更新到打印服務器 (“CORPSERV”),客戶端(“TESTCLT”)執行以下步驟。
1.使用RpcEnumPrinterDrivers枚舉現有的打印機驅動程序。
RpcEnumPrinterDrivers用來枚舉安裝在指定打印服務器上的打印機驅動程序。
2.如果打印機驅動程序不存在或客戶端請求更新打印機驅動程序,那么我們可以使用RpcAddPrinterDriver 將驅動程序添加到打印服務器。
- 客戶端確保打印機驅動程序的文件位于服務器可訪問的位置。所以,我們可以讓客戶端可以共享包含文件的本地目錄,或使用SMB)協議將文件放入服務器上的目錄中。
- 然后客戶端分配并填充一個DRIVER_INFO_2 結構
DRIVER_INFO_2 結構提供有關打印機驅動程序的信息
pName = L"OEM 打印機驅動程序"; pEnvironment = L"Windows NT x86"; /* 驅動程序兼容的環境 */ pDriverPath = "\\\\CORPSERV\\C$\\DRIVERSTAGING\\OEMDRV.DLL"; pDataFile = "\\\\CORPSERV\\C$\\DRIVERSTAGING\\OEMDATA.DLL"; pConfigFile = "\\\\CORPSERV\\C$\\DRIVERSTAGING\\OEMUI.DLL";
- 客戶端分配一個DRIVER_CONTAINER driverContainer 結構并初始化然后包含 DRIVER_INFO_2 結構。
DRIVER_CONTAINER 結構通過使用DRIVER_INFO結構提供有關打印機驅動程序的信息。DriverInfo成員指定限定了打印機驅動程序的屬性的結構。
- 客戶端調用 RpcAddPrinterDriver。
RpcAddPrinterDriver( L"\\\\CORPSERV", &driverContainer );
- 服務器添加打印機驅動程序并返回 0(成功)。

CVE-2021-34527 分析
原文中是說通過繞過 RpcAddPrinterDriver 的身份驗證。那么可以在打印服務器中安裝惡意驅動程序來達到LPE 和 RCE。
在微軟文檔中我們可以知道RpcAddPrinterDriver中還會額外的驗證

<311>驗證為
Windows 服務器檢查客戶端用戶是否具有 SERVER_ACCESS_ADMINISTER 權限。

<312>驗證為:
The parameter validation performed by RpcAddPrinterDriver is not supported by Windows NT 3.1, Windows NT 3.5, Windows NT 3.51, Windows 95, Windows NT 4.0, Windows 98, Windows 2000, Windows Millennium Edition, Windows XP, Windows Server 2003, Windows Vista, Windows Server 2008, Windows 7, or Windows Server 2008 R2.
我們到知道上面整個添加或更新客戶端和服務端的交互都是通過RPC來進行,其中我們需要加載安裝驅動程序,那么一定需要客戶端具有SeLoadDriverPrivilege權限。

那么我們想要在遠程服務器添加安裝驅動程序,那么第一步需要繞過SeLoadDriverPrivilege權限的檢查。
按照原文的意思我們可以看一下當客戶端需要調用RPC時。那么我們可以使用Process Monitor監視一下
Print Spooler服務在運行中的過程。
設置規則

可以檢測spoolsv.exe的所有

通過閱讀理解漏洞情況,我們可以大概可以定位到這個localspl.dll

通過查看這個DLL的相關信息我們可以知道:

Localspl.dll
Local print provider. Handles all print jobs directed to printers that are managed from the local server.
處理定向到從本地服務器管理的打印機的所有打印作業。
https://docs.microsoft.com/en-us/windows-hardware/drivers/print/introduction-to-print-providers
同時也實現了打印提供程序定義的整套功能
https://docs.microsoft.com/en-us/windows-hardware/drivers/print/local-print-provider
我們在查看這個整套功能中可以清楚看到:
https://docs.microsoft.com/en-us/windows-hardware/drivers/print/functions-defined-by-print-providers

那么我們可以重點關注一下這個DLL
AddPrinterDriver
添加打印機驅動程序 將指定打印機的驅動程序文件添加到指定服務器。
在其它網站上我們可以查到

只有一個DLL會鏈接這個DLL,好吧。
重點看一下導出的函數列表

這里有個SplAddPrinterDriverEx ,聽名字可以大概知道他的作用
直接提取c:\windows\sysytem32\localspl_dll
使用IDA打開然后追蹤SplAddPrinterDriverEx。

可以看到a4的值是可控的,ValidateObjectAccess 是 Spooler Service 的常規安全檢查,那么普通用戶可以繞過安全檢查并添加驅動程序。

從微軟文檔中我們可以看到 print spooler 遠程系統上引用或從遠程系統復制和將打印機驅動程序或其他插件作為本地系統調用時的一些安全措施
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-prsod/340e969b-3243-4116-bf79-47c45bb40264
Windows 實現可以執行以下一項或多項操作:
限制非管理用戶安裝打印機驅動程序。檢查打印機驅動程序的數字簽名。在下載此類組件或首次執行該組件之前,提示用戶同意。
上面的則可以繞過這些安全措施來使用非管理用戶安裝沒有簽名的打印機驅動程序

然后返回InternalAddPrinterDriverEx中
我們在漏洞原文中可以看到作者描述了文件復制的情況

同樣我們在InternalAddPrinterDriverEx中也找到了相關文件復制操作

跟進一下CopyFilesToFinalDirectory

C:\Windows\System32\spool\drivers\x64\3\C:\Windows\System32\spool\drivers\x64\3\oldC:\Windows\System32\spool\drivers\x64\3ew

結合漏洞原文我們可以知道復制pDataFile ,pConfigFile ,pDriverPath之后的文件夾為:C:\Windows\System32\spool\drivers\x64\3ew
然后再復制到 C:\Windows\System32\spool\drivers\x64\3中
并加載
C:\Windows\System32\spool\drivers\x64\3\ [pDataFile]和 C:\Windows\System32\ spool\drivers\x64\3\[pDriverPath] 進入 Spooler 服務中。
在Process中我們可以更為清晰地看到這一過程:
我們可以在
DRIVER_INFO_2 結構提供有關打印機驅動程序的信息
pName = L"OEM 打印機驅動程序"; pEnvironment = L"Windows NT x86"; /* 驅動程序兼容的環境 */ pDriverPath = "\\\\CORPSERV\\C$\\DRIVERSTAGING\\OEMDRV.DLL"; pDataFile = "\\\\CORPSERV\\C$\\DRIVERSTAGING\\OEMDATA.DLL"; pConfigFile = "\\\\CORPSERV\\C$\\DRIVERSTAGING\\OEMUI.DLL";
這里定義好pDriverPath,pDataFile,pConfigFile這3個DLL的值/路徑。
在Process中可以看到讀取了DRIVER_INFO_2 結構中的3跟DLL

我們可以看一下堆棧,然后在ida中追一下,


按照漏洞原文的意思,這里我們可以重點關注一下這3個DLL的路徑判斷
pDataFile =pConfigFile =pDriverPath=

這里不多描述。建議有興趣的同學自己去找一下。
然后具體看一下復制文件的過程




需要使用驅動程序升級的備份功能,
舊版本將備份到 C:\Windows\System32\spool\drivers\x64\3\old\1\ 文件夾中。

最后就是加載我們的任意DLL進入 Spooler 服務中,這樣就完成了漏洞利用。

如果在遠程RCE中我們把pConfigFile 設置為UNC(Universal Naming Convention)地址就可以了。
注意:
創建的smb服務允許匿名訪問。驗證使用普通域用戶的用戶名和密碼。需要在域環境內。
理論上說影響所有運行有打印機服務的windows機器。
CVE-2021-34527 復現
目前公開的EXP主要有:
C++
https://github.com/hayasec/PrintNightmare
python/C#
https://github.com/cube0x0/CVE-2021-1675
以及本地提權的
https://github.com/hlldz/CVE-2021-1675-LPE
1.本地提權復現
使用的是
https://github.com/hlldz/CVE-2021-1675-LPE
環境為:

復現不難,因為spoolsv.exe是x64的,所以我們這里使用Cobalt Strike的是x64的dll。

執行漏洞利用時,需要將 DLL 路徑作為漏洞利用的第一個參數。就可以了!
CVE-2021-1675-LPE.exe PAYLOAD_DLL_PATH


2.遠程RCE復現
使用的是
https://github.com/cube0x0/CVE-2021-1675
環境為:
攻擊主機:WIN10 域普通用戶 text 域內主機

攻擊主機:windows server 2019 域控(DC)

按照https://github.com/cube0x0/CVE-2021-1675 的smb設置方法,在域內一臺主機上提供匿名訪問權限的共享文件

把惡意的DLL放進分享目錄并允許匿名訪問,在域控或目標主機上必須能直接獲取到文件。
否則:
報錯 Error: code: 0x5 - rpc_s_access_denied 說明smb還不能匿名訪問

這里使用的是C#版本的EXP來進行演示利用

在DC對應的文件夾中,我們可以看到

可以看到Cobalt Strike成功上線

防御方法
微軟建議
確定 Print Spooler 服務是否正在運行
運行以下命令:
Get-Service -Name Spooler
如果 Print Spooler 正在運行或該服務未設置為禁用,請選擇以下選項之一以禁用 Print Spooler 服務,或通過組策略禁用入站遠程打印:
選項 1 - 禁用 Print Spooler 服務
如果禁用 Print Spooler 服務適合您的企業,請使用以下 PowerShell 命令:
Stop-Servie -Name Spooler -ForceSet-Service -Name Spooler -StartupType Disabled
選項 2 - 通過組策略禁用入站遠程打印
還可以通過組策略配置設置:
計算機配置/管理模板/打印機
禁用“允許打印后臺處理程序接受客戶端連接:”策略以阻止遠程攻擊。
限制 ACL
為驅動程序目錄和所有子目錄添加拒絕規則,防止 SYSTEM 帳戶修改其內容。
$Path = "C:\Windows\System32\spool\drivers"$Acl = (Get-Item $Path).GetAccessControl('Access')$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule("System", "Modify", "ContainerInherit, ObjectInherit", "None", "Deny")$Acl.AddAccessRule($Ar)Set-Acl $Path $Acl
檢測方法
EventID = '11' and Image like 'spoolsv.exe' and TargetFilename like 'C:\Windows\System32\spool\drivers\x64\3\'EventID 316Message INFO 316 NT AUTHORITY\SYSTEM 已添加或更新 Windows x64 Version-3 的打印機驅動程序 1234。文件:- UNIDRV.DLL, kernelbase.dll, 123.dll。無需用戶操作。