Windows PrintDemon提權漏洞分析
發布時間 2020-05-211.漏洞概述
微軟在5月12日的安全更新中公開了一個Windows本地提取漏洞(CVE-2020-1048),該漏洞的描述為:
“Windows Print Spooler服務不恰當地允許任意的文件系統寫入,存在特權提升漏洞。攻擊者利用此漏洞能夠用系統特權運行任意代碼,從而實現:程序的安裝、查看、更改或數據刪除,以及創建具有完整權限的帳戶。要利用此漏洞,攻擊者必須登錄到受影響的系統并運行特定腳本或應用程序”。
該漏洞由安全研究人員Alex Ionescu和Yarden Shafir發現,并被命名為PrintDemon。Print Spooler是系統自帶的打印后臺處理服務,管理所有本地和網絡打印隊列,控制著所有打印工作。Print Spooler在Windows系統中已存在多年,從微軟發布的補丁頁面可知該漏洞影響Windows7至Windows10 1909的幾乎所有版本。
2.漏洞驗證
啟明星辰ADLab安全研究員對該漏洞進行了分析和驗證,實現了在低權限的標準用戶下寫入系統目錄,測試操作系統為Windows 10 x64企業版2016(長期服務版),測試步驟如下:
(1)在測試系統中創建一個標準用戶test,并使用該標準用戶登錄系統。查看其所屬用戶組,確認其不是管理員用戶組。
(2)在test賬戶下,嘗試在系統目錄下創建文件夾或者寫入文件,均失敗。
(3)然后執行如下PowerShell命令,以期在系統目錄下創建文件myport.txt。
(4)重啟測試系統并登錄test用戶,可以看到在系統目錄下已生成了myport.txt文件,查看內容確實包含了測試字符串。該結果表明:低權限的 test用戶突破了無法修改系統資源的安全限制。
3.漏洞原理
該漏洞涉及到Windows打印機的工作機制,為更好的理解漏洞成因,首先簡單介紹打印機基礎知識,然后再分析漏洞成因。
打印機工作機制
Windows系統的打印機有兩個核心組件:打印機驅動和打印機端口。
■ 打印機驅動
在添加一個打印機時,需要安裝打印機驅動。在MSDN文檔描述中,早期系統要求只有具備SeLoadDriverPrivilege權限的用戶才能安裝打印驅動,但為了便于標準用戶安裝驅動,從Windows Vista開始,只要打印機驅動是已經存在的可立即使用的驅動,就不需要任何特權即可安裝。例如,通過一條PowerShell命令即可安裝“Generic / Text-Only”驅動。
■ 打印機端口
在添加一個打印機時,需要設置打印機的端口。Windows支持多種類型的打印機端口:LPT1端口、USB端口、網絡端口和文件等。如果設置端口為文件,則意味著打印機將數據打印到指定文件。例如,通過一條PowerShell命令即可添加一個輸出到指定文件的打印端口:
Add-PrinterPort -Name "C:\windows\Temp\myport.txt"
實際上,該操作是在注冊表中增加一個REG_SZ類型的值。
準備好驅動和端口后,通過一條PowerShell命令即可創建一個打印機。
打印機創建完畢后,通過一條PowerShell命令即可打印數據到指定端口:
"PrintTest!" | Out-Printer -Name "PrintTest"
由于PrintTest打印機的端口是文件c:\windows\Temp\myport.txt,因此打印命令執行后,數據“Print Test!”將會被寫入(即打?。┑皆撐募?。
針對端口是文件的打印過程,spooler打印服務程序以impersonating方式來模擬當前用戶的特權進行文件寫入。因此,如果端口文件在受保護的系統目錄(例如C:\Windows\system32), 則非管理員下的PowerShell打印作業就會失敗。
脫機打印的機制
在Windows系統上,如果系統配置啟用了假脫機服務,則所有的打印任務都不是立即執行。相反,系統使用Print Spooler來管理脫機打印任務。具體來說,當用戶調用打印操作后,系統將打印作業存儲在特定的假脫機文件夾中。
默認情況下,Windows生成的脫機打印任務文件為.SPL文件,此外Windows還會創建后綴名為.SHD的shadow文件并同SPL文件做關聯。創建shadow文件的用途是:在打印程序出現問題或者打印任務被掛起后,PrintSpooler依然可以通過SHD文件恢復打印任務。
在Windows系統重啟或Print Spooler服務重啟之后,.SHD和.SPL文件會被重新讀取以恢復打印任務。
打印提權的原理
脫機打印機制使得Windows系統在重啟后會恢復可能存在的未執行打印任務。但是,重啟后的Printer Spooler服務程序直接使用了System權限來恢復未執行的打印作業。對于打印機端口為文件的打印任務,打印文件的寫入也就在System權限下被執行。因此,系統重啟使得脫機打印任務具備了System權限的任意文件寫入能力。
打印機的設置除PowerShell腳本外,通過系統控制面板也能設置。具體來說,通過“設備和打印機”能添加打印機并設置端口。
但如果設置打印端口名為“C:\Windows\system32\myport.txt”,則會失敗。
為何設置同樣文件名的打印機端口,通過控制面板會失敗,而通過PowerShell 命令則可以成功呢?通過分析這兩種方式對spooler程序執行流程的影響,發現spooler程序對通過PowerShell命令行添加打印機端口方式缺乏安全校驗。
具體來講,針對PowerShell命令添加打印機端口,spooler程序直接設置了相應的打印機端口注冊表項;針對控制面板添加打印機端口,spooler程序會首先嘗試創建該端口文件,創建失敗后就不會再設置相應的注冊表項。
進一步分析相關API發現,Windows系統提供了兩種添加打印機端口的API,分別是AddPort函數和XcvData函數。其中MSDN對AddPort的描述:
“AddPort函數瀏覽網絡以查找現有端口,并彈出對話框供用戶選擇。AddPort函數應該通過調用EnumPorts來驗證用戶輸入的端口名稱,以確保不存在重復的名稱。AddPort函數的調用方必須具有訪問端口所連接的服務器的SERVER_ACCESS_ADMINISTER權限。要添加端口而不顯示對話框,可調用XcvData函數而不是AddPort ”。
通過控制面板添加打印機在底層是調用了AddPort函數,該函數會觸發spooler程序對端口的合法性校驗。通過PowerShell命令添加打印機在底層則是直接調用XcvData函數,該函數不會觸發spooler程序對用戶添加的端口進行安全校驗。因此,測試程序AddPort.exe通過該函數在標準用戶權限下也能設置打印機端口為受保護目錄中的文件。
漏洞補丁的分析
分析漏洞修復后的版本發現,微軟在關鍵函數LcmCreatePortEntry(最終創建打印機端口的函數)中添加了相應的端口合法性檢查代碼。下圖是關鍵函數LcmCreatePortEntry在修復前和修復后的Call Graph對比,可以看出:補丁的核心是通過函數PortIsValid對端口進行合法性檢查。
根據上文的分析可知,標準用戶是無法在系統目錄中創建文件的,把端口設置為系統目錄下的文件會導致PortIsValid檢測不到目標文件,從而判定要設置的端口是非法的。因此,在補丁修復后,標準用戶添加打印端口為系統目錄下文件的打印機就會始終失敗,從而避免了系統重啟時恢復惡意的打印服務。
4.修復建議
由于該漏洞能影響眾多的Windows系統版本,而且可以在標準用戶下發起漏洞攻擊,建議受影響的用戶及時進行系統更新或安裝漏洞補丁。
此外,微軟的安全更新只是對打印端口API進行了更嚴格的校驗。但是,如果惡意文件端口在漏洞修復前已經創建,則漏洞攻擊實際已經生效,此時進行系統更新仍然是不安全的。建議用戶先使用PowerShell命令Get-PrinterPort來檢查系統中是否存在可疑的打印機端口,在刪除可疑端口后再實施系統更新。
參考鏈接:
[1]https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1048
[2]https://docs.microsoft.com/en-us/windows/win32/printdocs/addport
[3]https://docs.microsoft.com/en-us/previous-versions/ff564255(v%3dvs.85)
[4]https://windows-internals.com/printdemon-cve-2020-1048/