資安雷達 2026 年 10 月 21 日

2026-10-21 — Pillow 縮圖遇 lossless JPEG 堆積溢位

primary=https://github.com/advisories/GHSA-8g5g-8vmr-9782 primary=https://github.com/python-pillow/Pillow/commit/242a059aedc1935097d9aa1dc3f099750467ba75 primary=https://github.com/python-pillow/Pillow/releases/tag/13.0.0

Pillow 縮圖遇 lossless JPEG 堆積溢位

GitHub Advisories · 2026-10-20

Pillow 的 Image.thumbnail() 會先叫 JPEG 解碼器用 1/2、1/4 或 1/8 縮小比例解碼,並把緩衝區縮到對應大小;但遇到 lossless(SOF3)JPEG 時,libjpeg-turbo 3.x 不理會縮放要求,照樣吐出整列像素,寫穿堆積配置。

這是 CVE-2026-112403(GHSA-8g5g-8vmr-9782),嚴重度 Critical,CVSS v4 基礎分數 9.2,弱點類型 CWE-122。受影響版本為 Pillow <= 12.3.0,13.0.0 已修補。回報者為 DavidKorczynski,致謝名單包含 Claude(Anthropic)與 Ada Logics。

漏洞機制

JpegImageFile.draft() 是 thumbnail() 自動呼叫的省時優化:目標尺寸遠小於原圖時,讓 libjpeg 直接解出縮小版,省下完整解碼的成本。Pillow 依此把解碼緩衝區縮小。

問題在於 libjpeg-turbo 3.x 對 lossless JPEG 忽略縮放,回傳的仍是全寬度的列。ImagingJpegDecode() 從未檢查解碼器的輸出尺寸是否放得進緩衝區,多出來的資料就寫到配置之外。公告指出,8-bit 灰階 lossless 影像的溢位內容是攻擊者可控的像素資料。

Pillow 的 SOF 解析器接受 SOF3 標記,所以惡意檔案打開時看起來就是一般 JPEG,不需要任何特殊副檔名或旗標。

受影響版本

  • Pillow <= 12.3.0,且使用 libjpeg-turbo 3.0 以上;官方 wheel 內建的就是這個版本,所以 pip install pillow 的使用者都在範圍內。
  • 連結 libjpeg-turbo 2.x 或 IJG libjpeg 的自編版本不受影響。
  • 觸發需要圖片夠大,讓 draft() 選到 2 倍以上縮放:以 thumbnail((256,256)) 與預設 reducing_gap=2.0 而言,寬高都要至少 1024。
  • 只有「draft() 在 load() 之前被呼叫」的路徑會中;open().load() 與 resize() 不受影響。

所以風險集中在接受使用者上傳、然後產生縮圖的服務:頭像、相簿、CMS 媒體庫、文件預覽。公告給出的攻擊複雜度為高(AC:H),但不需要權限也不需要使用者互動(PR:N/UI:N)。

修補與緩解

上游修補為 commit 242a059(PR #10131),在 src/libImaging/JpegDecode.c 加入 11 行檢查,緩衝區放不下就直接回報資料損毀:

if (context->cinfo.output_width > (JDIMENSION)state->xsize ||
    (size_t)context->cinfo.output_components * context->cinfo.output_width >
    (size_t)state->bytes) {
    jpeg_destroy_decompress(&context->cinfo);
    state->errcode = IMAGING_CODEC_BROKEN;
    return -1;
}

換句話說,Pillow 不再相信 libjpeg 的輸出一定符合 header 與縮放比例,而是自己比對。惡意檔案現在會得到 OSError(測試預期訊息為 "broken data stream")。這個修補列在 13.0.0 release 的變更清單中。

該做什麼:

  • 升級到 Pillow 13.0.0;用 pip show pillow 與 PIL.features.version("jpeg") 確認實際版本。
  • 暫時無法升級時,公告列出兩種繞法:在 thumbnail() 前先呼叫 img.load(),或使用 thumbnail(..., reducing_gap=None)。兩者都讓解碼不經過縮放緩衝區,代價是縮圖前要完整解碼,記憶體與 CPU 成本較高。
  • 檢查程式碼裡所有直接呼叫 img.draft() 的地方,它們有同樣的暴露面。

公告同時註明,漏洞影響 main 分支的 commit e41083f(2026-08-29),並採 90 天揭露期。release notes 未提及此 CVE 本身,修補需從 PR #10131 辨認。

原始來源:GitHub Advisory、修補 commit、Pillow 13.0.0 release


End of article
0
Would love your thoughts, please comment.x
()
x