CVE-2022-21999

SecIN發表於2022-08-08

一、   漏洞資訊

CVE-2022-21999(CVE-2022–22718)是微軟2月週二補丁所爆出來的印表機本地提權漏洞。其本質上是CVE-2020-1030的繞過。

二、   測試環境及漏洞復現

測試環境

POC:https://github.com/ly4k/SpoolFool

靶機: win10 x64 專業版,1909

漏洞復現

wKg0C2I4YomAH8N0AAEC456pCtU803.png

三、   漏洞成因分析

Print Spool

  1. 主要元件

Print Spooler是列印後臺處理服務,即管理所有本地和網路列印佇列及控制所有列印工作。如果此服務被停用,本地計算機上的列印將不可用。主要元件如下圖所示:

wKg0C2I4YqCAeowJAABLUzFBSnU927.png

具體元件資訊微軟官方檔案有詳細闡述,感興趣的同學可透過參考連結1進行查閱。

2.Spool directory

當使用者列印文件時,列印作業被spool送到一個預定的位置,稱為spool directory。另外spool directory在每臺印表機裝置上都是可以配置(修改)的,而且它會賦予所有使用者擁有FILE_ADD_FILE許可權。

透過在印表機的登錄檔項設定spool
directory的值來支援單個spool directory。

wKg0C2I4YseAPOh2AAB6e2vYDyc290.png

3.Point and Print

Point and Print 是為驅動程式分發而設計的多種印表機共享技術之一。在Point and Print中,驅動程式(v4 之前的型號)和配置檔案會自動從列印伺服器下載。安裝可透過自定義的 Point and Print DLL 進行擴充套件。該DLL是透過在印表機配置中定義 CopyFiles 登錄檔項來實現的。

4.相關函式

Print spool提供以下四個函式用於管理配置資料

wKg0C2I4YtAJJzPAAB3koqgL2o744.png

以上函式去執行與print`s Key(pValueName控制)相關的登錄檔操作。

當我們透過上述函式修改print的配置時,有一個大前提就是印表機需要以PRINTER_ACCESS_ADMINISTER許可權被開啟。

成因分析

  1. SetPrinterDataEx分析

帶有 CopyFiles 登錄檔項的 SetPrinterDataEx 會導致spooler自動載入在 Module 值中分配的 Point and Print DLL。其呼叫鏈如下:

wKg0C2I4YvKAL21IAABXilgaE60337.png

使用者層呼叫SetPrinterDataEx函式,Winspool會向本地Print Spooler(LocalSpl)傳送一個RPC請求,後者會將請求route 到SplSetPrinterDataEx 中的本地列印提供程式實現。

wKg0C2I4YvAIOdjAAC0gZlXyM267.png

在localspl.dll!SplSetPrinterDataEx中會對pKeyName的值進行匹配

wKg0C2I4YweAOkVqAAA1kP9IKrA747.png

當 pszKeyName 以”CopyFiles\”字串開頭時觸發Load事件,會呼叫SplCopyFileEvent去處理。

wKg0C2I4YxGAHTbLAAAlDeYsdPQ775.png

SplCopyFileEvent首先透過呼叫SplGetPrinterDataEx獲取模組路徑長度,為其申請相應的記憶體,接著再次透過SplGetPrinterDataEx獲取模組完整路徑。最後將要載入的模組路徑傳入SplLoadLibraryTheCopyFileModule。

wKg0C2I4Yx6AEIhOAABlAjX70tE413.png

除錯資訊如下:

wKg0C2I4YyaAR2mEAADvExGSU3w328.png

SplLoadLibraryTheCopyFileModule會對模組路徑進行驗證,只有透過驗證後才可以載入DLL。

wKg0C2I4YzaAXP3AADqzsrbgc894.png

MakeCanonicalPath將模組路徑轉換為規範路徑。

wKg0C2I4Yz6AE5foAACpz2K4qco134.png

IsModuleFilePathAllowed函式將驗證模組規範路徑是否在system路徑或者印表機驅動目錄下。

wKg0C2I4Y0eAKI1eAABcPOKGLIY456.png

  • System路徑:DLL只能直接在C:\Windows\System32\路徑下才可以(不包括下層目錄)。
  • 印表機驅動目錄路徑:DLL可以在印表機驅動目錄下的任意路徑。

wKg0C2I4Y1aAH2ihAABJx3rvODw882.png

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> %

wKg0C2I4Y3yY9AAAxnSUpzPU824.png

我們可以從第二個路徑做文章,也就是印表機驅動程式目錄,我們可以利用一個不存在的版本(4)驅動程式目錄來進行利用。如果我們能夠建立具有讀寫許可權的目錄,那麼missing path可能存在程式碼執行機會。然後可以將任意 DLL 放置到檔案路徑中,並透過
SetPrinterDataEx 呼叫它。

四、   漏洞利用分析

利用流程

  1. 建立印表機

為獲取PRINTER_ACCESS_ADMINISTER許可權,要在本地建立一臺新的印表機。

wKg0C2I4Y6SARTWPAACPU2bEaD0411.png

2.設定Spool directory

透過SetPrinterDataEx設定Spool directory,獲取一個擁有FILE_ADD_FILE的許可權的檔案目錄。

wKg0C2I4Y7WAMWWlAAA6m94LpT0889.png

這裡需要特殊說明一下,在使用CVE-2020-1030漏洞補丁更新後,SetPrinterDataEx在設定Spool directory時會對其傳入的路徑進行一次檢查。

wKg0C2I4Y72AaGKfAAAloyc8KnY114.png

IsValidSpoolDirectory將要設定的Spool directory路徑轉換為規範路徑後,測試其是否具有GENERIC_WRITE許可權。

wKg0C2I4Y8WAKfoAAABxwT5GlaE907.png

驗證成功後,在印表機登錄檔項對Spool directory進行更改。

3.符號連結

設定自定義Spool directory目的是我們想透過它去執行任意DLL,但在透過我們對SetPrinterDataEx可知,其在載入DLL前會對其路徑進行一次檢查(必須是system路徑或印表機驅動目錄),此時我就可以透過符號連結(重解析)來繞過。

wKg0C2I4Y9qAR5ZHAAAf0hjSJGo407.png

即讓自定義的路徑[\localhost\C$/spooldir)指向C:\Windows\System32\spool\drivers\x64\,至於這裡為什麼要用UNC路徑,後面會進行解釋。

wKg0C2I4YOAMfRLAAA0ve1wfQk122.png

4.重新啟動Spooler服務

在第2步中的設定Spool directory,並沒有實際進行建立,僅是在登錄檔中進行設定。而實際建立目錄僅在 Spooler初始化時才會被建立。但此時我們的印表機已經被建立,Spooler的初始化也已經完成。看起來很難完成,但車到山前必有路,可以透過將 AppVTerminator.dll 載入到 Spooler中,強制 Spooler重新啟動以建立目錄。

wKg0C2I4ZBmAbq3pAABA4CtEgY8908.png

AppVTerminator的Dllmain可直接結束當前程式(spoolsv.exe)。

wKg0C2I4ZCOAAIHdAABqKtc9LI8747.png

Spooler被強制結束後,會觸發其恢復機制,從而達到重啟服務的目的。

Spooler重啟之後會透過localspl!SplCreateSpooler
呼叫 localspl!BuildPrinterInfo 時會建立spool directory。在 localspl!BuildPrinterInfo
賦予FILE_ADD_FILE 許可權之前,進行最後檢查以確保目錄路徑不在印表機驅動程式目錄中。

Tips:此項路徑檢查同樣是在CVE-2020-1030的補丁中新增。

wKg0C2I4ZCyACeX6AABaAfgp2Gs939.png

綜上所述,想要成功載入一個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許可權載入)。

wKg0C2I4ZFqASLHAACHEWbYS48203.png

符號連結

CVE-2022-21999實際上是CVE-2020-1030的繞過,其核心是UNC路徑和符號連結。這裡簡單介紹一下符號連結。

符號連結是將自己連結到一個目標檔案或目錄的路徑上。當系統識別到符號連結時,它會跳轉到符號連結所指向的目標中去,而不改變此時的檔案路徑。實際上,我認為這就是高階快捷方式。

如下圖所示,為印表機驅動程式目錄建立符號連結\localhost\C$\Users\jiabin3\AppData\Local\Temp\c0e601f7-92b2-4167-b18f-3a23c65eaa94。然後可以看到,開啟此路徑之後
,其內部的目錄(檔案)結構與印表機驅動程式目錄是一致的。唯一的區別是他們的路徑不一樣,我認為這是一種高階對映方式。

wKg0C2I4ZGyAMu9NAAB2mgnwzXI014.png

參考連結

  1. Print Spool元件介紹

https://docs.microsoft.com/en-us/windows-hardware/drivers/print/introduction-to-spooler-components

  1. Windows Print Spooler Privilege
    Escalation

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

  1. Windows 符號連結

https://sspai.com/post/66834