imgproxy の ICO ローダー RCE、4.0.16 で修正 (GHSA-hff9-5pqw-6w9f)
imgproxy 4.0.0〜4.0.15 はラップするサイズで ICO データを確保し、未認証の細工した画像リクエストがそれをヒープ破壊に変えます。4.0.16 で修正済み。
要点
- imgproxy 4.0.0〜4.0.15 の ICO 画像ローダーは攻撃者が制御するサイズフィールドを読み、14 を 32 ビットの和で加算してラップさせます。その後、少なすぎるメモリを確保して画像データを書き込むため、細工した ICO はワーカーを異常終了させるかヒープを破壊します。ベンダーアドバイザリは CVSS v3.1 9.8 Critical、CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H と評価しています。
- プロキシに悪意ある画像を処理させられる攻撃者は、この欠陥に到達できます。imgproxy は HTTP で画像変換を提供し、URL 署名に対応していますが、署名検証はデフォルトで無効です。mateofumis と JasonPuglisi が、関連する 2 件の ICO 欠陥とともに開示しました。
- 修正は 2026 年 9 月 28 日に imgproxy 4.0.16 でリリースされ、10 月 6 日のベンダーアドバイザリ公開より 8 日早いものでした。GitHub アドバイザリデータベースへの登録は 10 月 30 日です。現在のリリースは 4.0.20 で、4.0.16 以降に修正が含まれます。
- エクスプロイトも悪用も被害者の報告もなく、2026 年 10 月 31 日時点でアドバイザリは CISA の KEV(Known Exploited Vulnerabilities)カタログに載っていません。今すぐ 4.0.16 以降へ更新し、そのついでに取得元 URL の制限を確認してください。
影響を受けるか?
以下がすべて必要です。
- imgproxy 4.0.0〜4.0.15 を実行している。4.0.16 以降は修正済みです。
- インスタンスが攻撃者の制御する画像を処理している。取得元 URL が無制限の場合だけでなく、許可された取得元へのユーザーアップロードでも起こります。
- ICO ローダーが使用中であり、影響リリースでは常にそうです。ローダーは ICO 形式に自身を登録し、ソースはファイルのマジックバイトで振り分けます。
imgproxy は署名付き URL に対応しています。署名を必須にし、アプリケーションが署名する画像を制御すると、ローダーへのアクセスを制限できます。リバースプロキシでの認証も役立ちます。ただし、ローダーを修正したり悪意ある画像を安全にしたりするものではありません。別のメモリ割り当てアドバイザリは 3.3.0〜4.0.15 を影響範囲に挙げているため、これらの旧版も 4.0.16 以降へ更新してください。
ベンダーは 9 月 28 日に修正をリリースし、10 月 6 日にアドバイザリを公開しました。GitHub アドバイザリデータベースへの登録は 2026 年 10 月 30 日です。このデータベースに依存するツールは、ベンダーを直接監視するツールより遅く情報を得た可能性があります。
| ブランチ | 影響バージョン | 修正バージョン |
|---|---|---|
| imgproxy 4.x | 4.0.0〜4.0.15 | 4.0.16 (2026-09-28) |
実行中のバージョンを確認してください。バイナリは自身のバージョンを表示します。
docker exec imgproxy imgproxy --version
イメージのタグにも含まれますが、latest のような浮動タグではこの節のバージョン確認コマンドが必要です。ソースからデプロイする場合、バージョン定数はチェックアウトのタグの version/version.go にあります。
修正方法
4.0.16 以降に更新してください。現在のリリースは 4.0.20 です。
- Compose ファイルで imgproxy サービスのイメージを
darthsim/imgproxy:4.0.20に設定し、そのイメージを取得してサービスを再作成します。
docker compose pull imgproxy
docker compose up -d imgproxy
- バイナリやソースのデプロイでは、4.0.16 以降をビルドまたはダウンロードし、サービスを再起動します。
取得元の制限は露出を減らしますが、更新の代わりにはなりません。IMGPROXY_ALLOWED_SOURCES には、許可する取得元 URL のプレフィックスを設定します。例:
IMGPROXY_ALLOWED_SOURCES="https://your-bucket.example/"
許可リストは未承認の取得元からの直接取得を防ぎます。ただし、許可されたバケットにアップロードされた悪意ある画像は防げず、SSRF 全般の修正でもありません。内容を信頼できる取得元だけを許可し、制限を有効にしていても更新してください。
根本原因は何か?
ICO ローダーは、ラップしうる 32 ビット加算で攻撃者が宣言したサイズからバッファーを確保し、実際に確保された終端を越えてポインター経由で書き込みます。 脆弱なコードは vips/icoload.c の vips_foreign_load_ico_header で、アドバイザリが 4.0.15 世代のコミット f4fedbb から引用した 154〜164 行目です。
uint32_t data_size = GUINT32_FROM_LE(largest_image_header.data_size);
VipsPel *data = (VipsPel *) VIPS_MALLOC(NULL, data_size + BMP_FILE_HEADER_LEN);
void *actual_data = data + BMP_FILE_HEADER_LEN;
if (vips_foreign_load_read_full(ico->source, actual_data, data_size) <= 0) {
vips_error("vips_foreign_load_ico_header", "unable to read ICO image data from the source");
return -1;
}
この断片の 3 つの事実が欠陥を作ります。data_size は ICO ディレクトリエントリ経由で攻撃者が制御する 32 ビットフィールドです。加算 data_size + BMP_FILE_HEADER_LEN はヘッダー長 14 で 32 ビット算術で行われ、data_size = 0xfffffff5 では和は 3 にラップし、確保は 3 バイトになります。vips_foreign_load_read_full はラップ後ではなく元の data_size を受け取り、それを Go のソースコールバックへ渡し、コールバックは 確保領域の先頭から 14 バイト後、つまり 3 バイトのバッファーの外側へ、ソース内の利用可能なバイトを actual_data 経由でコピーします。
例についてのアドバイザリの算術は次のとおりです。data_size = 0xfffffff5 と data_offset = 22 のディレクトリエントリに 14 バイトの BMP ヘッダーを足すと、(0xfffffff5 + 14) mod 2^32 = 3 になります。エントリに続く 64 バイトは、読み取りが宣言された数ギガバイトのペイロードが不完全だと報告できる前に書き込まれます。
2 つの関連欠陥は同じ関数にあります。BMP ヘッダー再構築は、サイズ検査の前に、微小な確保を越えた固定オフセットで num_colors、bpp、height を読み、範囲外読み取りは再構築ヘッダーの範囲外書き込みになります。また、検証がまったくないため、data_size = 0xfa000068 を宣言する 23 バイトの ICO は、ワーカーに約 4 GB の割り当てを試行させます。非有界割り当てのアドバイザリは、これが 4.0.15 の本番デプロイで確認されたと述べています。
どのように悪用されるか?
完全なエクスプロイトは公開されていません。アドバイザリは細工された値を公開し、非有界割り当ての関連アドバイザリにはチェックサム付きの検証用生成スクリプトが含まれます。手順はアドバイザリのソースからシンクへの経路に基づきます。
- 攻撃者は細工した ICO をホストします。そのディレクトリエントリはラップするサイズ
data_size = 0xfffffff5(data_offset = 22)を宣言し、ファイルは ICO マジック00 00 01 00で始まります。 - 攻撃者は標的の imgproxy にそのファイルを処理させます。取得元無制限のインスタンスへのその URL のリクエストか、アップロードを受け付けるデプロイでの直接アップロードです。
- imgproxy はマジックからソースを ICO と識別し、
vips_icoload_source_goへ振り分けます。 vips_foreign_load_ico_headerは攻撃者のdata_sizeを読み、ラップした 3 バイトを確保し、vips_foreign_load_read_fullは元の宣言サイズを Go コールバックへ渡します。- コールバックは手持ちのソースバイトを書き込んでヒープを破壊し、プロセスは異常終了するか、破壊されたアロケーター状態で継続し、アドバイザリはこれをサービス拒否またはリモートコード実行と評価します。
攻撃の流れは?
細工した ICO をプロキシ経由で運ぶ未認証リクエストが、ラップするサイズフィールドを、いかなる検査も止められないうちにヒープへの書き込みに変えます。欠落した検査は、割り当て前の data_size の検証です。
data_size 0xfffffff5"] --> B["リクエストが imgproxy に到達、
URL 署名を強制しない構成"] B --> C["ソースがマジック 00 00 01 00 で
ICO ローダーへ振り分け"] C --> D["欠落した検査: data_size が
一切検証されない"] D --> E["32 ビット ラップ: ~4 GB 宣言に対し
3 バイトを割り当て"] E --> F["読み取りコールバックが確保の終端を
越えてバイトを書き込む"] F --> G["ワーカー異常終了またはヒープ破壊:
クラッシュ、潜在的な RCE"]
攻撃者に何ができるか?
攻撃者は画像を処理する imgproxy のワーカーを異常終了させ、それを意図的に繰り返せます。これは画像パイプラインへのサービス拒否です。クラッシュを超えて、アドバイザリはヒープ破壊が「may permit remote code execution」(リモートコード実行を許容しうる)と述べ、タイトルは整数オーバーフローがそれを「許す」と言い、CVSS v3.1 9.8 で機密性・完全性・可用性の完全な影響を与えています。条件は「影響を受けるか?」のとおりです。影響バージョン、攻撃者が影響を与えられる画像取得元、ネットワーク到達性です。コード実行を実証するエクスプロイトは公開されていないため、実証された影響はクラッシュで、コード実行はアドバイザリの評価です。
関連 2 件はそれぞれ到達範囲を加えます。BMP ヘッダー再構築の範囲外読み書きはワーカーを決定論的に異常終了させ(glibc の munmap_chunk(): invalid pointer から Go パニック)、CVSS v3.1 7.5 です。非有界割り当ては 23 バイトのファイルからメモリを枯渇させ、これも 7.5 です。どちらも攻撃者が画像を処理させられることが条件です。
2026 年 10 月 31 日時点でどこにも悪用の報告がなく、アドバイザリは KEV カタログに載っていません。
なぜ修正は機能するか?
コミット a821b6a4cb07「Fix segfaults during invalid ICO load」(4.0.16 でリリース)はサイズを検証し、和を一度だけ計算します。
+// Maximum size of an ICO entry data block.
+// Maximum image size in ICO is 256x256 pixels,
+// which gives a maximum of 256 * 256 * 4 bytes for raw RGBA data.
+// 10 MB should be enough for any ICO entry data block,
+// even if an embedded PNG contains metadata.
+// Data blocks larger than this should be considered malicious.
+#define MAX_ICO_ENTRY_DATA_SIZE 10 * 1024 * 1024
+
/**
* ICO ForeignLoad VIPS class implementation (generic)
*/
@@ -153,13 +161,26 @@ vips_foreign_load_ico_header(VipsForeignLoad *load)
uint32_t data_size = GUINT32_FROM_LE(largest_image_header.data_size);
+ // Check if the data size exceeds the maximum allowed ICO entry data size.
+ if (data_size > MAX_ICO_ENTRY_DATA_SIZE) {
+ vips_error("vips_foreign_load_ico_header", "ICO image data is too large");
+ return -1;
+ }
+
// BMP file explicitly excludes BITMAPFILEHEADER, so we need to add it manually.
// We reserve space for it at the beginning of the data buffer.
- VipsPel *data = (VipsPel *) VIPS_MALLOC(NULL, data_size + BMP_FILE_HEADER_LEN);
+ uint32_t full_data_size = data_size + BMP_FILE_HEADER_LEN;
+ VipsPel *data = (VipsPel *) VIPS_MALLOC(NULL, full_data_size);
+ if (!data) {
+ vips_error("vips_foreign_load_ico_header", "unable to allocate memory for ICO image data");
+ return -1;
+ }
+
void *actual_data = data + BMP_FILE_HEADER_LEN;
if (vips_foreign_load_read_full(ico->source, actual_data, data_size) <= 0) {
vips_error("vips_foreign_load_ico_header", "unable to read ICO image data from the source");
+ VIPS_FREE(data);
return -1;
}
10 MB の上限は、1 つの検査でラップと非有界割り当ての両方を除去します。32 ビットの和をラップさせるサイズは上限をはるかに超えるため、いかなる割り当ての前にエラーで拒否されます。単一の full_data_size 変数が一度計算した値をその後のすべての使用に運ぶため、下流に渡されるサイズが割り当てたサイズと食い違うことはもうありません。VIPS_MALLOC 後の NULL 検査は失敗した割り当てをデリファレンスするのではなく処理し、エラー経路の VIPS_FREE(data) はリークを止めます。
同じコミットが、関連する範囲外読み書きを閉じる最小サイズ検査を追加します。
+ // Ensure the data buffer is large enough to contain the BMP header.
+ if (full_data_size < BMP_BITMAP_INFO_HEADER_LEN) {
+ vips_error("vips_foreign_load_ico_header", "ICO image data is too small to contain a valid BMP header");
+ VIPS_FREE(data);
+ return -1;
+ }
+
// Read num_colors and bpp from the BITMAPINFOHEADER
uint32_t num_colors = GUINT32_FROM_LE(*(uint32_t *) (actual_data + 32));
この修正は、このローダーの ICO エントリ経路に対して完全です。サイズは入口で制限され、割り当ては検査され、BMP ヘッダー再構築はヘッダーより小さいバッファーを越えて読み書きできなくなりました。ローダーのテストスイートはアドバイザリの不正 ICO フィクスチャ testdata/ico-bad1.ico 〜 ico-bad3.ico を獲得し、修正済みローダーはクラッシュしません。
侵害されたか確認するには?
被害者もキャンペーンも報告されていないため、公開された侵害指標はありません。アドバイザリが記述するクラッシュ署名を探してください。これが試行の観測可能な痕跡です。
# Worker restarts and ICO-related aborts in the container log.
docker logs imgproxy --since 720h 2>&1 | grep -iE "SIGABRT|panic|ico" | head -20
ワーカーを異常終了させたヒープ破壊の試行は、異常終了と再起動を残します。クラッシュは手がかりであってコード実行の証拠ではありません。プロセスが攻撃者のコードを実行した疑いがある場合は、コンテナーを侵害されたものとして扱い、読み取り可能だったすべてをローテーションしてください。imgproxy では通常、環境変数内のオブジェクトストレージの資格情報が含まれます。想定外の起点から ICO ファイルを運んだリクエストを取得元ログで確認してください。
年表
- 2026-09-24: imgproxy が a821b6a4cb07「Fix segfaults during invalid ICO load」をコミット。
- 2026-09-28: imgproxy が v4.0.16 をリリース。変更履歴に「Fixed several vulnerabilities in ICO image loader」と記載。
- 2026-10-06: ベンダーが 3 件の ICO アドバイザリ(GHSA-hff9-5pqw-6w9f、GHSA-494m-4556-wf5p、GHSA-mmq5-xm89-9pfg)をレビュー済み GitHub セキュリティアドバイザリとして公開。
- 2026-10-30: GitHub アドバイザリデータベースのレビューが 22 時 57 分 UTC にアドバイザリを公開。このデータベースを利用するツールから参照可能になりました。
よくある質問
アドバイザリはなぜリモートコード実行に「may permit」と言うのですか?
実証された効果はクラッシュで、コード実行に至るヒープ破壊は、この欠陥が許すものに対するアドバイザリの評価です。その評価は機密性・完全性・可用性への完全な影響を持つ 9.8 スコアに反映されています。破壊をコード実行まで運ぶ公開エクスプロイトはなく、本記事はアドバイザリ自身の慎重な表現を保持しています。
4.0.16 で十分ですか、4.0.20 が必要ですか?
4.0.16 は 3 件の ICO 修正をすべて持つため、これ以降のリリースはこのアドバイザリに対して安全です。4.0.20 は単に現在のリリースで、その間に依存関係の更新があります。テストするものを固定し、4.0.16 以降で固定してください。
スキャナーがこれを検出しませんでした。なぜですか?
3 件のアドバイザリは CVE 識別子を持たず、GitHub アドバイザリデータベースに届いたのは 2026 年 10 月 30 日、修正の 1 か月後です。CVE フィードかデータベースだけを読むツールはその日まで何も見ていないため、9 月時点のクリーンなスキャンはインスタンスがクリーンなことを意味しません。スキャンではなくバージョンを確認してください。
取得元の制限は実際に役立ちますか?
取得元の制限は未承認の取得元からの直接取得を防ぎます。ただし、許可されたバケット内の攻撃者のファイルを処理するサービスは保護できず、ローダーも修正しません。4.0.16 以降へ更新してください。
同じロットの他の 2 件のアドバイザリは?
ロットには、カスタム BMP ローダーのヒープバッファーオーバーフロー GHSA-55vq-g34m-xp8f と、キャリアグレード NAT アドレスを許可する取得元アドレスガード GHSA-8w7m-q6hg-mfqm も含まれます。すべて 4.0.16 で修正されており、1 回の更新でロット全体をカバーします。BMP ローダーの欠陥は同じ未認証クラッシュ級で、ガードの欠陥は SSRF の隣接です。
Ostorlab で露出した画像パイプラインを棚卸しする
Ostorlab のアタックサーフェスディスカバリーは、インターネットに露出したサービスを棚卸しします。どのデプロイが露出した imgproxy エンドポイントを実行しているかを把握し、どのインスタンスを 4.0.16 以降へ更新するか、どれを信頼できる取得元へ限定するかを判断できます。
参考資料
- ベンダーアドバイザリ GHSA-hff9-5pqw-6w9f「Integer overflow in ICO loader causes remote denial of service and permits remote code execution via heap buffer overflow」。CVSS v3.1 9.8、影響 4.0.0〜4.0.15、修正 4.0.16、mateofumis と JasonPuglisi をクレジット: https://github.com/imgproxy/imgproxy/security/advisories/GHSA-hff9-5pqw-6w9f (2026 年 10 月 31 日確認)
- 関連アドバイザリ GHSA-494m-4556-wf5p「Out-of-bounds heap read/write in ICO loader BMP header reconstruction」、CVSS v3.1 7.5: https://github.com/imgproxy/imgproxy/security/advisories/GHSA-494m-4556-wf5p (2026 年 10 月 31 日確認)
- 関連アドバイザリ GHSA-mmq5-xm89-9pfg「Unbounded memory allocation in ICO loader」、CVSS v3.1 7.5。チェックサム検証済みの検証用生成スクリプトと 4.0.15 本番での確認付き: https://github.com/imgproxy/imgproxy/security/advisories/GHSA-mmq5-xm89-9pfg (2026 年 10 月 31 日確認)
- 関連アドバイザリ GHSA-55vq-g34m-xp8f、カスタム BMP ローダーのヒープバッファーオーバーフロー: https://github.com/imgproxy/imgproxy/security/advisories/GHSA-55vq-g34m-xp8f (2026 年 10 月 31 日確認)
- 関連アドバイザリ GHSA-8w7m-q6hg-mfqm、CGNAT アドレスを許可する取得元アドレスガード: https://github.com/imgproxy/imgproxy/security/advisories/GHSA-8w7m-q6hg-mfqm (2026 年 10 月 31 日確認)
- 修正コミット a821b6a4cb07「Fix segfaults during invalid ICO load」、2026 年 9 月 24 日作成: https://github.com/imgproxy/imgproxy/commit/a821b6a4cb07 (2026 年 10 月 31 日確認)
- imgproxy v4.0.16 リリース、2026 年 9 月 28 日。変更履歴行は「Fixed several vulnerabilities in ICO image loader」: https://github.com/imgproxy/imgproxy/releases/tag/v4.0.16 (2026 年 10 月 31 日確認)
- アドバイザリが引用するコミット f4fedbb にある脆弱な
vips/icoload.c: https://github.com/imgproxy/imgproxy/blob/f4fedbb5c7f6684e7a8e0a9bb7e666773f3c40c0/vips/icoload.c (2026 年 10 月 31 日確認) - Docker イメージ darthsim/imgproxy、36,809,323 プル: https://hub.docker.com/r/darthsim/imgproxy (2026 年 10 月 31 日確認)
- CISA の Known Exploited Vulnerabilities カタログの JSON フィード。アドバイザリで検索: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (2026 年 10 月 31 日確認)
- imgproxy の URL 署名: https://docs.imgproxy.net/usage/signing_url