CVE-2022-21999
一、 漏洞資訊
CVE-2022-21999(CVE-2022–22718)是微軟2月週二補丁所爆出來的印表機本地提權漏洞。其本質上是CVE-2020-1030的繞過。
二、 測試環境及漏洞復現
測試環境
POC:https://github.com/ly4k/SpoolFool
靶機: win10 x64 專業版,1909
漏洞復現
三、 漏洞成因分析
Print Spool
- 主要元件
Print Spooler是列印後臺處理服務,即管理所有本地和網路列印佇列及控制所有列印工作。如果此服務被停用,本地計算機上的列印將不可用。主要元件如下圖所示:
具體元件資訊微軟官方檔案有詳細闡述,感興趣的同學可透過參考連結1進行查閱。
2.Spool directory
當使用者列印文件時,列印作業被spool送到一個預定的位置,稱為spool directory。另外spool directory在每臺印表機裝置上都是可以配置(修改)的,而且它會賦予所有使用者擁有FILE_ADD_FILE許可權。
透過在印表機的登錄檔項設定spool
directory的值來支援單個spool directory。
3.Point and Print
Point and Print 是為驅動程式分發而設計的多種印表機共享技術之一。在Point and Print中,驅動程式(v4 之前的型號)和配置檔案會自動從列印伺服器下載。安裝可透過自定義的 Point and Print DLL 進行擴充套件。該DLL是透過在印表機配置中定義 CopyFiles 登錄檔項來實現的。
4.相關函式
Print spool提供以下四個函式用於管理配置資料
以上函式去執行與print`s Key(pValueName控制)相關的登錄檔操作。
當我們透過上述函式修改print的配置時,有一個大前提就是印表機需要以PRINTER_ACCESS_ADMINISTER許可權被開啟。
成因分析
- SetPrinterDataEx分析
帶有 CopyFiles 登錄檔項的 SetPrinterDataEx 會導致spooler自動載入在 Module 值中分配的 Point and Print DLL。其呼叫鏈如下:
使用者層呼叫SetPrinterDataEx函式,Winspool會向本地Print Spooler(LocalSpl)傳送一個RPC請求,後者會將請求route 到SplSetPrinterDataEx 中的本地列印提供程式實現。
在localspl.dll!SplSetPrinterDataEx中會對pKeyName的值進行匹配
當 pszKeyName 以”CopyFiles\”字串開頭時觸發Load事件,會呼叫SplCopyFileEvent去處理。
SplCopyFileEvent首先透過呼叫SplGetPrinterDataEx獲取模組路徑長度,為其申請相應的記憶體,接著再次透過SplGetPrinterDataEx獲取模組完整路徑。最後將要載入的模組路徑傳入SplLoadLibraryTheCopyFileModule。
除錯資訊如下:
SplLoadLibraryTheCopyFileModule會對模組路徑進行驗證,只有透過驗證後才可以載入DLL。
MakeCanonicalPath將模組路徑轉換為規範路徑。
IsModuleFilePathAllowed函式將驗證模組規範路徑是否在system路徑或者印表機驅動目錄下。
- System路徑:DLL只能直接在C:\Windows\System32\路徑下才可以(不包括下層目錄)。
- 印表機驅動目錄路徑:DLL可以在印表機驅動目錄下的任意路徑。
Tips:匹配演算法是透過是將模組規範路徑去掉其前四個位元組後,進行匹配的。即?\C:\Windows\System32\spool\drivers\x64
printers\變為C:\Windows\System32\spool\drivers\x64
printers\。此種方法存在一定的缺陷,漏洞利用也是針對這種檢查方式的繞過。
2.漏洞點分析
Spooler在載入Point and Print DLL時會依次搜尋一下兩個路徑:
- %SYSTEMROOT%\System32 % SYSTEMROOT% System32
- %SYSTEMROOT%\System32\spool\drivers<ENVIRONMENT><DRIVERVERSION> %
我們可以從第二個路徑做文章,也就是印表機驅動程式目錄,我們可以利用一個不存在的版本(4)驅動程式目錄來進行利用。如果我們能夠建立具有讀寫許可權的目錄,那麼missing path可能存在程式碼執行機會。然後可以將任意 DLL 放置到檔案路徑中,並透過
SetPrinterDataEx 呼叫它。
四、 漏洞利用分析
利用流程
- 建立印表機
為獲取PRINTER_ACCESS_ADMINISTER許可權,要在本地建立一臺新的印表機。
2.設定Spool directory
透過SetPrinterDataEx設定Spool directory,獲取一個擁有FILE_ADD_FILE的許可權的檔案目錄。
這裡需要特殊說明一下,在使用CVE-2020-1030漏洞補丁更新後,SetPrinterDataEx在設定Spool directory時會對其傳入的路徑進行一次檢查。
IsValidSpoolDirectory將要設定的Spool directory路徑轉換為規範路徑後,測試其是否具有GENERIC_WRITE許可權。
驗證成功後,在印表機登錄檔項對Spool directory進行更改。
3.符號連結
設定自定義Spool directory目的是我們想透過它去執行任意DLL,但在透過我們對SetPrinterDataEx可知,其在載入DLL前會對其路徑進行一次檢查(必須是system路徑或印表機驅動目錄),此時我就可以透過符號連結(重解析)來繞過。
即讓自定義的路徑[\localhost\C$/spooldir)指向C:\Windows\System32\spool\drivers\x64\,至於這裡為什麼要用UNC路徑,後面會進行解釋。
4.重新啟動Spooler服務
在第2步中的設定Spool directory,並沒有實際進行建立,僅是在登錄檔中進行設定。而實際建立目錄僅在 Spooler初始化時才會被建立。但此時我們的印表機已經被建立,Spooler的初始化也已經完成。看起來很難完成,但車到山前必有路,可以透過將 AppVTerminator.dll 載入到 Spooler中,強制 Spooler重新啟動以建立目錄。
AppVTerminator的Dllmain可直接結束當前程式(spoolsv.exe)。
Spooler被強制結束後,會觸發其恢復機制,從而達到重啟服務的目的。
Spooler重啟之後會透過localspl!SplCreateSpooler
呼叫 localspl!BuildPrinterInfo 時會建立spool directory。在 localspl!BuildPrinterInfo
賦予FILE_ADD_FILE 許可權之前,進行最後檢查以確保目錄路徑不在印表機驅動程式目錄中。
Tips:此項路徑檢查同樣是在CVE-2020-1030的補丁中新增。
綜上所述,想要成功載入一個DLL我們必須保證以下兩點:
BuildPrinterInfo檢查時,保證Spool directory路徑不以
- C:\ Windows\System32\spool\drivers\x64\開頭
- SplLoadLibraryTheCopyFileModule 檢查時,保證Spool directory路徑以C:\ Windows\System32\spool\drivers\x64\開頭
這樣看起來好像很矛盾,但它的檢查演算法(IsModuleFilePathAllowed)是存在缺陷的(去掉前4個位元組匹配),因此我們可以將Spool directory設定為UNC路徑,例如[\localhost\CKaTeX parse error: Undefined control sequence: \spooldir at position 1: \̲s̲p̲o̲o̲l̲d̲i̲r̲\printers\](fil…/spooldir/printers/),經過設定軟連線後,它的規範路徑是\?\UNC\localhost\C$\Windows\System32\spool\drivers\x64\printers\,因此去掉前四個位元組後與C:\Windows\System32\spool\drivers\x64\也不匹配。成功過掉檢查後,BuildPrinterInfo就會給Spool directory(C:\Windows\System32\spool\drivers\x64\)賦予FILE_ADD_FILE許可權。
5.寫入DLL並載入
Spooler服務成功重啟後,我們可以將想要執行的DLL檔案寫入到C:\Windows\system32\spool\DRIVERS\x64\4,然後透過SplLoadLibraryTheCopyFileModule去載入(以SYSTEM許可權載入)。
符號連結
CVE-2022-21999實際上是CVE-2020-1030的繞過,其核心是UNC路徑和符號連結。這裡簡單介紹一下符號連結。
符號連結是將自己連結到一個目標檔案或目錄的路徑上。當系統識別到符號連結時,它會跳轉到符號連結所指向的目標中去,而不改變此時的檔案路徑。實際上,我認為這就是高階快捷方式。
如下圖所示,為印表機驅動程式目錄建立符號連結\localhost\C$\Users\jiabin3\AppData\Local\Temp\c0e601f7-92b2-4167-b18f-3a23c65eaa94。然後可以看到,開啟此路徑之後
,其內部的目錄(檔案)結構與印表機驅動程式目錄是一致的。唯一的區別是他們的路徑不一樣,我認為這是一種高階對映方式。
參考連結
- Print Spool元件介紹
https://docs.microsoft.com/en-us/windows-hardware/drivers/print/introduction-to-spooler-components
- Windows Print Spooler Privilege
Escalation
- Windows 符號連結