在正式進入明天的主題前,先來聊聊 Imagemagick 的漏洞歷史吧!
ImageMagick [[1]] 是一套開源的圖片處理工具,除了最常見的格式轉換外,也能做縮放、裁切、合成、旋轉、調色、產生縮圖等操作,並提供 Command Line 與多種語言的 API。
最簡單的使用方式大概長這樣:
magick input.jpg -resize 800x600 output.png
ImageMagick 並不是只把 JPEG 的 Pixel 搬到 PNG 裡而已,它背後還有幾個很重要的角色:
label:、caption:、https:,輸入看起來像檔名,實際上卻是在要求 ImageMagick 執行某種動作。curl、Ghostscript 等外部程式來幫忙。換句話說,ImageMagick 看到的可能是格式、Metadata、URL、外部檔案與其他程式的組合包。這個 Parser Boundary 一旦沒有切乾淨,圖片就不再只是圖片了。
然而,由於 ImageMagick 支援的超兩百種檔案類別加上其免費、開源的生態,導致其被廣泛運用到許多 Web Servers/Applications (甚至包含 Wordpress、Drupal 等大型 CMS),一旦它出了什麼漏洞可都是大新聞呢!
2016 年爆出的 ImageTragick [[2]] 其實是一組漏洞,其中最知名的 CVE-2016-3714 可以讓攻擊者在伺服器處理惡意圖片時做到 Command Injection,最後一路走到 RCE。
在 recall 一次 ImageMagick 中所謂的 delegate 可以註冊外部的 lib 來處理不同類型的協議,其中一條 delegate 長這樣:
<delegate decode="https"
command=""curl" -s -k -o "%o" "https:%M""/>
%o 是輸出檔名,而 %M 會被替換成輸入中的 URL。ImageMagick 接著把展開後的整段 Command String 交給 Shell 執行;偏偏當時對 %M 的過濾又不完整,攻擊者只要在圖片中引入的 URL 中跳脫原本的引號達成命令字串拼接就可以 Command Injection。
而 SVG、MVG 這類格式剛好又能在內容中引用外部圖片,例如概念上可以寫成:
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg";|id ")'
pop graphic-context
整條 Data Flow 就變成:
使用者上傳 SVG/MVG
-> 圖片內容引用一個 URL
-> URL 被代入 Delegate 的 %M
-> Delegate Command 經由 Shell 執行
-> Command Injection
當年的修補除了更新版本外,也建議透過 policy.xml 關閉不需要的 MVG、MSL、HTTPS 等 Coder/Delegate。現在 ImageMagick 官方的 Security Policy [[3]] 也仍然建議:如果服務會處理公開來源的檔案,就應限制格式、關閉外部 Delegate、禁止 Indirect Read,並把處理程序放進 Sandbox。
其實筆者自己對這漏洞印象算蠻深刻的,這可是我當年第一年在金盾獎(2023 年)解開的題目呢(挺)
註:當年甚至把 flag 放在 /etc/flag,得知的方法是在前端原始碼找到 Base64 Encoded HTML 註解、、、
時間快轉六年,ImageMagick 又出現了 CVE-2022-44268 [[4]]。這次不是 Command Injection,而是 Arbitrary File Read/Information Disclosure:攻擊者可以讓 ImageMagick 在轉換 PNG 時讀取本機檔案,再把內容塞進輸出的圖片中。
PNG 除了 Pixel Data 外,還能帶上 tEXt Chunk。每個 tEXt Chunk 都是一組 Keyword 與文字內容;正常情況下拿來放作者、描述之類的資訊完全沒問題,但如果 Keyword 叫做 profile,事情就開始有趣了。
攻擊者可以準備一張概念上包含以下 Metadata 的 PNG:
Keyword: profile
Value: /etc/passwd
舊版 PNG Decoder 讀到它後,會把這組資料交給 SetImageProperty(image, "profile", "/etc/passwd", ...)。偏偏 profile 在 ImageMagick 裡不是普通的自由欄位,而是一個有特殊語意的 Property;底層會把 Value 當成檔案路徑,呼叫 FileToStringInfo() 讀取檔案,再透過 SetImageProfile() 把內容內嵌回去圖片。
於是整條 work Flow 變成:
PNG tEXt Chunk(profile=/etc/passwd)
-> SetImageProperty("profile", "/etc/passwd")
-> FileToStringInfo("/etc/passwd")
-> 檔案內容成為 Image Profile
-> 寫入轉換後的 PNG
-> 攻擊者下載輸出圖片並還原內容
只要 ImageMagick 的執行身分讀得到目標檔案,而且 Web 服務會把轉換後的圖片回傳,原本沒有任何下載功能的圖片 API 就這樣多出了一個 File Read Oracle。
這個漏洞的 Upstream Patch [[5]] 很短,有點可愛(?):PNG Parser 遇到 profile 時,不再原封不動地把它當成共用 Property,而是改名成 png:profile,一如既往地用禁止來 patch 漏洞(
if ((LocaleCompare(key,"version") == 0) ||
+ (LocaleCompare(key,"profile") == 0) ||
(LocaleCompare(key,"width") == 0))
(void) FormatLocaleString(key,MagickPathExtent,"png:%s",
text[i].key);
在根據考察後,其實 Imagemagick 的漏洞發展史不只這樣,像是在 2016 年以前會發現大多數的研究都是 Memory Corruption (Pwn) 相關:
而因為跟接下來的主題比較無關所以沒寫,但其實 Imagemagick 還發生過在處理 PS/EPS/PDF 等檔案類型時因為使用了 GhostScript 預設的 -dSAFER Sandbox 發生過一系列災難,也值得被好好看一下。Project Zero Issues[6]
所以明天要介紹的 LLM Finding 到底是誰呢~