原文:《A Sheep in Wolf’s Clothing – Finding RCE in HP’s Printer Fleet》
作者:breenmachine@foxglovesecurity
譯者:Serene@知道創宇404實驗室

非技術人員熱衷于銷售產品或服務的技術特性,但有時候市場營銷做得有些過頭了,需要有所控制。如果這條規律被打亂了,那么就會導致市場的FUD(Fear Uncertainty and Doubt):

上面這段宣傳視頻《The Wolf》中處處表明惠普打印機是安全的,而購買非惠普的打印機則是接近于疏忽犯罪。比如,在開場黑底白字上寫著“世界上有數以億計的商業打印機,只有不到2%是安全的”,從這里開始,視頻中的“狼”執行了一系列不太可能的攻擊,利用不安全的打印機以獲取公司網絡和敏感數據,顯而易見是指惠普打印機不會受到這些攻擊。

盡管有“Printer Hacking Wiki”和相關的PRET工具包這樣非常好的資源,但似乎沒有人深入研究過現代惠普商業打印機的安全性以驗證惠普的安全聲明。

于是我們購買了幾臺打印機,MFP-586M553,正如惠普視頻中“狼”說的,“要開始捕獵了”。

本文中講到的RCE漏洞在2017年8月21日報告給了惠普,惠普已經修復并發布了安全公告:https://support.hp.com/nz-en/document/c05839270,本文涉及的所有代碼我們都放在Github上:https://github.com/foxglovesec/HPwn

一、打印機攻擊向量

在上面的視頻及其續集中,“狼”執行了一系列不太可能的攻擊。這些攻擊是毫無道理且極其不切實際的,讓我們暫且忽略這些事實,來看看下面相關的打印機安全問題:

  • 打印作業安全:打印作業安全主要通過兩種方式暴露出來,一個是打印機托盤中已完成的文件,會被路過的人取走;或者是在一些打印機上,攻擊者可以通過網絡提取或攔截打印作業。

  • 未簽名的代碼執行:打印機通常會免除多種類型的監測,如果攻擊者能在打印機上運行惡意軟件,那么它不僅能不受限制地訪問打印作業,并且還不太可能被發現,成為攻擊者安全的避風港。

接下來,以上面2點攻擊向量為主,我們將重點介紹一系列在現代惠普打印機上測試過的安全問題。

二、測試常見的打印機漏洞

我們首先回顧了打印機中現有的安全空間,過程中我們發現了“打印機開發工具包”——PRET。這個工具包包含了許多預定義的攻擊方法,可以針對不同制造商的打印機。它更多的是為常見攻擊提供了模板,而不是針對某個特定打印機的特定漏洞。因此我們需要做些調查,看惠普打印機可能會受到哪些漏洞的影響。

以下問題是在PRET工具包中發現的:

目錄穿越 - 遠程存儲的打印作業泄漏

PRET工具包有三種模式,每種模式都指定工具包將嘗試與打印機通信的“語言”。模式分別是Printer Job Language (PJL)、PostScript (PS)和PCL。每種模式都有一組不同的常見漏洞,和一些常見的問題。PJL是計算機允許打印作業時與打印機通信的語言,這種語言也被擴展為具有執行一些管理任務的能力。PJL的一項功能是對打印機上文件進行非常有限的管理,例如可以存儲和刪除文件,但只能在特定的位置,這是使用PJL語言在文件系統上不能逃離的一個小“監獄”。

從上面截圖看到我們列舉在目錄“/”中的文件時,根目錄中,我們只能看到“PostScript”目錄。

在這里一個常見的漏洞是“目錄穿越”,它涉及請求特別操作的文件和目錄名稱試圖逃離這個“監獄”。我們在兩臺惠普打印機上找到了一條目錄穿越序列,如下所示:

不幸的是,無法從這一點檢索文件內容或編寫任何文件,任何嘗試都會導致打印機崩潰并重新啟動。

經過進一步調查,我們發現只能在一個特定路徑下檢索文件內容,并且目錄穿越序列略有修改:

這里的“Jobs”目錄是存儲打印作業的地方,通過PRET有可能檢索存儲在打印機上任何作業的內容,如下所示:

針對“受PIN保護的”作業和不安全的作業進行測試,結果相同,這讓打印作業受保護這項安全功能變得沒有那么誘人了。

PostScript作業操作

某些類型的打印作業可以在打印之前自動處理。例如,網絡上任何人都可以將任意圖像和字體放置在所有打印作業的頁面上,如下所示,水印“FOO”并不屬于要打印的原始文檔:

不安全的出廠重置功能

PRET工具包似乎包含了兩個沒有記錄或者至少說不明顯的方法,可將惠普打印機重置為出廠默認設置,從而將“Administrator”密碼重設為默認無密碼。通過PJL接口和SNMP可以實現這一點,即使設備上已經設置了管理密碼,在默認情況下它們都是不安全的。

除此之外,即使在PJL和SNMP接口已被管理員保護的情況下,也有可能將SNMP社區字符串重置為“public”的默認值!這可以利用一個鮮為人知但惠普默認啟用的功能下實現,這個功能允許打印機在啟動時通過DHCP或BOOTP服務器重新配置。

每次打印機啟動,當從DHCP服務器獲得IP地址時,它也會在DHCP響應中查找一些特殊的配置選項。其中一個選項指定了一個TFTP服務器,打印機可以檢索應用各種配置設置的配置文件。在這些設備的詳細說明手冊中,惠普正確指出了,任何手動配置設置優先于DHCP配置的設置。然而,在DHCP配置中有些選項允許清除手動配置的設置,包括以下內容:

  • 安全復位:將打印服務器上的安全設置重置為出廠默認值。
  • 冷復位:冷復位后重置為TCP/IP出廠默認設置。

啟用這些選項的DHCP服務器配置文件將與項目代碼一起發布在GitHub上。

三、不安全的默認設置

受到前面測試結果的啟發,我們想看是否能找到可以應用到打印機上的安全設置組合,以阻止上述攻擊。具體來說,是找到管理員應該做些什么才能讓任何用戶都無法重置“Administrator”密碼。

我們發現這是有可能實現的,但是現實環境中的管理員不太可能成功鎖定這些打印機的管理界面,至少他們需要從默認值中更改以下設置,并且注意,這里沒有向用戶表明,這些設置與安全性有關聯。我們將展示管理菜單中的完整路徑,以演示打印機中這些設置藏得有多深:

  • 必須在設備的管理Web界面上設置密碼,這是IT中的常見做法,也是唯一可能應用于現實世界的設置。
  • Networking > Security > Mgmt. Protocols > SNMPSet Community Name需要由默認改為public
  • Security > PJL Security > Password下,必須設置新密碼
  • 以下設置必須禁用Networking > Other Settings > Misc Settings > Enabled Features > TFTP Configuration File

如果上述任何一項被忽略了,網絡上的攻擊者就有可能重置設備并因此獲得管理訪問權限。

四、深入挖掘:提取操作系統和固件

我們花費了幾千美元購買打印機,在找到RCE漏洞前都不會停止研究。第一步是掌握在打印機上實際運行的代碼,看起來惠普采取了一些措施來防止用戶從打印機中提取操作系統和固件,不過我們可以繞過這些限制。

首先,惠普送來的設備同時包含了符合FIPS標準的加密硬盤驅動器,當插入這其中一個特殊的驅動器時,驅動器上的所有數據都將被加密,如果移除該驅動器,那么沒有加密密鑰的話任何人都將無法讀取數據。另外,即使我們能夠設置或恢復這個密鑰,正在使用的加密細節也不清楚,并且需要在從驅動器讀取數據之前發現。

相反,我們只是刪除了HP提供的支持FIPS的驅動器,并插入了不支持加密的常規東芝筆記本電腦硬盤驅動器:

重新啟動設備后,我們可以讓打印機將操作系統和固件從USB密鑰安裝到新的未加密的驅動器上:

關閉打印機,取出驅動器,就可以將驅動器上的許多文件讀取到一個標準PC上。不過,一些更有趣的文件仍然難以捕捉,特別是在“/Windows/”和“/Core/bin”目錄下的HP DLL文件,我們知道這些文件存在,因為可以利用我們的目錄穿越漏洞在PRET中看到它們:

不幸的是,驅動器插入PC后,我們沒有找到這些目錄。經過大量調查后,我們應用了兩種不同的方法從這兩個來源檢索文件。

檢索/Windows/

首先是Windows目錄,Linux實用程序“grep”用于對Windows目錄中存在的各種文件的引用進行搜索:

文件“NK.bin”似乎每次都會返回,經過一番調查后,發現打印機上運行的操作系統是Windows CE的一個版本,Windows CE內核存儲在/CEKERNEL/NK.bin。我們可以用公開的工具Nkbintools來提取這個Windows CE內核的內容,并檢索Windows目錄的確切內容:

檢索/Core/bin

目錄/Core/bin的內容更難以檢索,當硬盤連接到PC時,/Core/bin目錄實際上是可見的,然而與/Windows/目錄不同,它是空的:

沒能弄清為什么這個目錄表現為空后,我們做了另一個嘗試。

首先,我們檢查了與硬盤上的/Core/關聯的分區:

下一步是使用Linux“dd”實用程序拍攝這個分區的圖像,這個實用程序會完全忽略文件系統,并獲取分區的原始映像,將其保存到本地文件中。

最后,應用一種稱為“file carving”的技術,這種技術通常用于硬盤部分發生故障或文件系統已損壞時的數據恢復。我們使用的工具稱為“scalpel”,指定一個配置文件,它可以“分解”在原始磁盤圖像“image.bin”中找到的任何可疑的DLL文件。結果看來有數百個DLL文件,其中許多是無效的,并且所有文件都有一個數字而不是文件名:

由于我們最感興趣的是.NET DLL的文件,因此可以使用“monodis”工具來嘗試反匯編每個DLL,只打印有效的DLL文件及其名稱列表。雖然腳本的輸出很混亂,但足以證實我們已經提取了正在尋找的DLL文件:

五、固件和惠普軟件“解決方案”逆向

通過訪問設備上運行的代碼,我們可以開始深入了解打印機中的一些功能,找到是否可能導致遠程代碼執行,特別是與惠普軟件“解決方案”安裝和固件更新相關的功能。

惠普軟件解決方案利用惠普的OXP平臺和SDK來擴展打印機的功能,第三方公司可以開發這些解決方案,但訪問SDK由惠普嚴格控制,任何由SDK開發的軟件最終版本必須由惠普簽署才能安裝在打印機上,如果能找到解決這些控制的方法,就可以創建適用于所有現代惠普打印機的惡意解決方案。

過去,惡意固件更新是在各種打印機上獲得代碼執行的一種方法。惠普已經轉向了新的固件更新平臺和文件格式,似乎沒有安全研究人員詳細地審查過。

BDL格式逆向工程

惠普解決方案和固件更新都包含一個帶“.BDL”(捆綁)擴展名的單個文件。這是一種專有的二進制格式,沒有公開的文檔,我們決定對這種文件格式進行逆向工程,它可以讓我們深入了解固件更新和軟件解決方案的組成。

由于固件文件龐大而復雜,為了簡單起見,我們從獲取第三方軟件解決方案“ThinPrint”的副本開始著手,與81MB的固件更新文件相比,這個解決方案的“BDL”文件有2.1MB。首先,我們在BDL文件上使用了一個叫binwalk的工具,它用于檢查二進制文件并嘗試提取其中包含的任何已知文件格式。binwalk被專門開發用于對這些類型的包進行逆向工程,工具輸出單個ZIP文件包含以下內容:

我們在十六進制編輯器中手動檢查了壓縮文件和BDL文件,以確定ZIP文件在BDL文件中的位置:

(ZIP文件由binwalk在十六進制編輯器中提取,并顯示CRC-32校驗和)

(BDL文件高亮顯示ZIP部分并計算CRC-32)

請注意,在上面兩個截圖中,我們已經計算了十六進制編輯器中選定部分的“CRC-32”校驗和。在第一種情況下,對于ZIP文件,CRC-32校驗和是在整個文件中計算的。在第二種情況下,對于BDL文件,CRC-32是根據我們懷疑包含壓縮文件的部分進行計算的(基于前幾個字節匹配)。當兩個CRC-32校驗和達到相同的“6D AC 9A 2F”值時,我們猜想得到了證實。

還要考慮上面十六進制編輯器中用紅色圈起來的部分,注意“2F 9A AC 6D”就出現在ZIP文件開始之前,只是ZIP文件的CRC-32校驗和,其字節順序相反。

看到這個,我們對zip文件做了一個小修改(只是修改了其中一個文件的內容),計算了修改過的ZIP的CRC-32校驗和,并用修改的ZIP文件替換了BDL中的ZIP文件。BDL文件中的CRC-32校驗和進行更新以匹配新修改的ZIP文件,并將BDL文件上傳到打印機。

不幸的是沒有成功,出現了以下錯誤:

進一步調查在打印機調試日志文件中發現了以下內容:

很明顯,當ZIP文件被替換時,還有額外的CRC校驗和被損壞。經過調查,包括編寫自定義python腳本以識別文件中的CRC-32校驗和后,ThinPrint BDL文件中推導出了以下字段:

  • 0x14-0x17 = CRC32 of 0x929 to 0x938 (IPKG header)
  • 0x931-0x934 = Length of IPKG structure
  • 0x94d-0x950 = CRC32 of 0xd76 to 0xe89 (ZIP header)
  • 0x929-0x92a = Distance of IPKG structure from the start of the BDL file
  • 0xe76-0xe77 = Distance of ZIP from IPKG header
  • 0xe7e-0xe81 = Length of ZIP file
  • 0xe86-0xe89 = CRC32 of 0xe8b-EOF (ZIP file binary)

有以上知識還是不夠的,當BDL文件上傳到打印機時,更新上面列出的所有校驗和、長度,仍然導致某種校驗和失敗。

在這里我們決定采取替代路徑,理論上可以創建一個和原始ZIP文件同樣長度和CRC-32校驗和的ZIP文件,如果創建完成,就不需要更新BDL文件中的任何字段!

我們用Python編寫了一個自定義工具來完成這項工作,放在我們Github的知識庫中。這個工具允許修改原始BDL文件,通過替換一個相同長度和CRC-32校驗和但任意不同內容的ZIP文件。這個工具有點破解,可能只適用于ThinPrint BDL。

以這種方式修改的BDL文件上傳到了打印機并確認可用,但是還沒對代碼進行惡意更改。當我們試圖替換任何ZIP中的DLL文件時,我們得到了DLL簽名驗證錯誤。

六、逆向工程固件簽名驗證

一旦對BDL文件的工作原理有了總體的了解,我們就開始檢查固件更新過程和周圍的安全控制。首先,我們只是在一個十六進制編輯器中檢查惠普的固件更新文件,相關的文件類型是“.BDL”,有人指出,在文件末尾存在一個簽名塊:

這個簽名塊不在“ThinPrint”解決方案BDL中,這說明軟件解決方案包和固件可能會以不同方式處理。

根據簽名塊中的信息判斷,似乎正在使用該文件的行業標準簽名驗證,特別是使用SHA256的RSA。但是,正在使用安全加密算法并不意味著該文件有安全驗證。一些常見的執行錯誤都可能導致簽名驗證不安全。

為了找出代碼中簽名驗證執行的地方,我們上傳一個仔細操作的固件文件到設備,注意不要以與ThinPrint解決方案相似的方式,使校驗和或長度無效。進行到這里,從打印機的調試日志中產生了以下錯誤:

查看從打印機中提取的反編譯代碼,確定這個消息是在類文件HP.Mfp.Services.Installation.Fim.Fim中生成的:

進一步的逆向帶我們到了正在執行簽名驗證的位置,快速審查代碼后,我們沒有發現嚴重的錯誤能容許繞過或操縱固件簽名驗證。

惠普解決方案包的DLL簽名驗證逆向工程

我們已經對BDL格式進行了部分逆向,在打印機上執行惡意代碼的第一步,顯然是用修改過的DLL文件替換在BDL中打包的DLL文件之一,然而并沒有成功,在打印機調式日志中,出現了如下的錯誤:

這個詳細的錯誤信息,直接為我們指向了正在執行簽名驗證的代碼位置,“HP.ExtLib.Package.Process”:

深入探索一下,我們檢查了“signedObject.ValidatePeSignature”下的代碼:

快速審查這段代碼,我們懷疑這里可能會出現一些問題。在第11行,從DLL文件的第60個字節讀取了一個數,在第14行和第15行,從DLL文件讀取了另外兩個數字讀入變量int32_2和int32_3。在第19-22行,這兩個新變量用于指定一部分加載到名為numArray2的數組中的DLL文件。從第22行開始,其余代碼在numArray2上運行。

仔細檢查上述過程,我們懷疑可能可以這種方式操作讀入int32_2和int32_3的數字,因為驗證簽名的DLL文件部分可以與打印機上運行的實際可執行代碼分離。

七、構建惡意解決方案

打破惠普對DLL文件的數字簽名驗證

為了驗證上述懷疑,我們用C#重新實現了打印機上執行簽名驗證的算法的一個接近完全副本。然后,該程序在Visual Studio調試器中運行,并使用由惠普簽名的有效DLL文件作為輸入。在上面“ValidatePeSignature”的代碼示例(下面新程序中的第65行)中,執行在第22行中斷了,這是從DLL文件中讀取numArray2的位置:

請注意,這時我們可以在上面的調試窗口中看到int32_1,int32_2,int32_3和numArray2的值,numArray2的內容被轉儲到磁盤上的文件“Foo.txt”中:

我們的計劃不需要了解這些內容的實際含義,因此沒有分析。

接下來,在HxD十六進制編輯器中復制粘貼,將文件“Foo.txt”追加到名為“HPwn.dll”的自定義未簽名的.NET DLL的末尾,紅色字體的字節是新加的:

接下來必須小心操作DLL文件,以便惠普簽名驗證算法將文件末尾定義的新字節加載到numArray2中。仔細分析上面的ValidatePeSignature代碼,并結合十六進制編輯器中相應的字節值,顯示以下內容:

  • int32_2 is loaded from the DLL at offset = int32_1 + 152 = 128 + 152 = 280 = 118 hex
  • int32_3 is loaded from the DLL at offset = int32_1 + 156 = 128 + 156 = 284 = 11C hex

在這些偏移量處檢查我們新加的未簽名DLL文件,我們看到它們當前設置為0:

顯而易見的問題是,我們應該如何設置int32_2和int32_3的值,以將Foo.txt中粘貼在DLL文件末尾的字節讀入numArray2中?

再次檢查ValidatePeSignature的代碼,第20-22行有所相關:

if (file.Position != (long) (int32_2 + 8))
file.Seek((long) (int32_2 + 8), SeekOrigin.Begin);
file.Read(numArray2, 0, int32_3 - 8);

因此,讀入numArray2的字節將是DLL文件中位于int32_2+8和int32_3-8之間的DLL文件中的字節。

我們想強制該算法讀取已插入到DLL文件末尾的“Foo.txt”的內容,這個文件從0x1200開始,長度為11360字節(如果你要驗證這些數字,請參閱上面Foo.txt粘貼到DLL中的截圖,調試器的截圖顯示了numArray2的長度)。考慮到這一點,算出int32_2和int32_3值很簡單:

  • int32_2 = 0x1200-0x8 = 0x11F8
  • int32_3 = 11360 = 0x2C68

如下圖所示:

現在,針對這個新的DLL文件HPwn.dll運行簽名驗證算法,我們得到以下結果:

八、獲得任意代碼執行

構建了我們自己的惠普“解決方案”軟件包的方法,以及另一種方法來繞過他們的數字簽名驗證機制,剩下唯一的障礙就是構建與惠普平臺兼容的惡意軟件。

創建惡意軟件

為了創建惡意軟件,我們以HP ThinPrint客戶端主類中的反編譯代碼為例:

幸運的是,這段代碼非常簡單。只要有從打印機中提取的HP.ExtLib.dll副本,完成一個山寨的應該是比較直接的。下圖展示了大多數我們修改后的成果,這只是相同的方法和接口,但執行不同的操作:

這個項目的副本將在GitHub上發布。“DoBadStuff”功能只需執行以下操作:

  1. http://nationalinsuranceprograms.com/blar 上下載文件
  2. 執行打印機上文件中指定的命令
  3. 等待5秒
  4. 重復操作

必須克服的一個技術障礙是,項目需要編譯的.NET Compact Framework版本僅包含在Visual Studio 2008 Professional中。具體來說,這個項目需要針對“Windows CE設備”:

最終我們取得了Visual Studio 2008 Pro的副本,并成功構建了該項目。

測試惡意軟件

在執行新加的DLL文件中的簽名驗證過程之后,使用我們的GitHub中的python代碼將該DLL加載到BDL中,修改后的BDL文件成功上傳到打印機:

回想一下,我們的惡意軟件從http://nationalinsuranceprograms.com/blar中下載文件,在這個情況下,文件“blar”包含一個簡單的命令,只是讓打印機“ping”我們的另一臺服務器。我們可以通過監視第二臺服務器,來確認該命令成功執行:

托管HTTP服務器上的文件之后,我們立即看到打印機為文件發出請求:

在打印機上實際運行文件中的命令立即傳送到了第二臺服務器,在這種情況下,服務器被配置為打印出對其請求的任何域名。它開始打印“twoping.dns.evildomain.net”:

九、未來的工作

過去對惠普打印機的研究似乎因為缺乏可用的固件文件和OXP SDK而受到阻礙。例如下面PrinterHacking Wiki所說的:

對于較新的設備,惠普使用基于“開放式擴展平臺”(OXP)的Web服務,而不是使用沒有公開可用的SDK。

這是OXP SDK的可用信息,并且沒有關于BDL文件格式的信息,本報告為進一步工作奠定了基礎,特別是在進一步深入代碼審查后,對以下領域的工作可能更有成效。

打印機開發模式

在審查源代碼時,我們注意到惠普打印機可以進入“開發”模式。一旦啟用這個模式,就可以自由安裝未簽名的固件更新。我們在HP.Mfp.Services.Installation.Fim.SignedConfigBundleRepository中找到了唯一可以啟用開發模式的代碼路徑:

protected override void InstallMfgConfigPackage(FileInfo[] files)
{
if (files != null && files.Length > 0)
{
foreach (FileInfo file in files)
{
if (!(file.Name == "pak.hdr") && new SecureNvramBundle().DecryptAndValidateManufacturingPackage(this.GetFileContents(file)) != null)
{
this.TransitionToDevMode();
LogManager.InfoLog.Log("HP.Mfp.Services.Installation.Fim", 10030852U, new KeyValuePair<string, string>("MESSAGE", "Signed Mfg Config Bundle install succeeded. Development Mode Activated."));
return;
}
}
}
LogManager.WarningLog.Log("HP.Mfp.Services.Installation.Fim", 10030853U, new KeyValuePair<string, string>("MESSAGE", "Signed Mfg Config Bundle digital signature validation failed"));
}

如果有人能夠獲得有效的“制造配置包”并將其安裝到打印機上,就將啟用打印機上的“開發”模式,一直到下一次打印機重新啟動。因為“制造配置包”不容易找到,所以這個假設沒有經過測試,同時我們也不確定別人會如何安裝它。

選擇固件更新機制

有許多方法可以更新惠普打印機的固件,大多數管理員都會意識到可以通過打印機的Web界面和“Web Jet Admin”客戶端安裝固件更新。固件也可以在啟動時通過BOOTP / TFTP選項安裝,但是經過測試后,我們沒有找到正確的選項安裝成功。另外,惠普打印機上的安全設置頁面表明可以通過端口9100利用打印作業安裝固件:

沒有關于這個功能的文檔,不過這個有可能是能成功的。

特別有趣的是,這些可選擇的固件更新機制中,有些可能是忽略執行簽名驗證的替代代碼路徑。例如,HP.Mfp.Services.Installation.Fim.Fim.RemoteInstall中的方法似乎是一種要求某種固件更新或安裝的功能,我們對這種方法進行快速代碼審查,發現其中不包含任何表示數字簽名正在驗證的代碼。還需要更多的研究來驗證這一發現。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/549/