{"id":"CVE-2026-112405","kind":"vulnerability","aliases":["CVE-2026-112405","GHSA-cp9v-662q-vvx9"],"related":[],"title":"Pillow: NULL pointer dereference in Image.merge via an unvalidated band 0","description":"### Summary\n`Image.merge()` checks the mode of every input band except band 0. If band 0 is a single-band `I` or `F` image, the native merge routine reads a pixel pointer that is NULL for those modes and the process crashes. The IPTC plugin reaches this from a plain `Image.open()` followed by a load, and IPTC has no magic-byte prefilter, so a crafted file with no recognizable header can drive it.\n\n### Details\nThe validation loop skips band 0:\n\n- `src/PIL/Image.py:3795` iterates `for band in bands[1:]` and checks mode and size only for those.\n\nFor an `I` or `F` image the 8-bit plane `image8` is NULL (only `image32` is set). `ImagingMerge` dereferences `bands[0]->image8[y]` for the multi-band output modes:\n\n- `src/libImaging/Bands.c:302`, `:311`, `:321`\n\nThe IPTC loader builds the band list from an attacker-typed sub-image and passes it to merge with the sub-image at index 0:\n\n- `src/PIL/IptcImagePlugin.py:156-160`\n- `src/PIL/IptcImagePlugin.py:169` registers the opener with no accept function.\n\n### PoC\n```python\nfrom PIL import Image\n\n# Band 0 is a single-band \"I\" image: its image8 plane is NULL. merge() validates only\n# bands[1:], so the native ImagingMerge dereferences the NULL plane for band 0.\nband0 = Image.new(\"I\", (4, 4))\nImage.merge(\"RGB\", [band0, Image.new(\"L\", (4, 4)), Image.new(\"L\", (4, 4))])\n```\n\nRun it:\n```\n$ python 02_poc.py\nSegmentation fault (core dumped)\n```\n\nObserved on a source build of commit `3e2ab96`.\n\nUnder AddressSanitizer:\n```\n==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000\n    #0 ImagingMerge   src/libImaging/Bands.c:311\n    #1 _merge         src/_imaging.c:2492\nSUMMARY: AddressSanitizer: SEGV src/libImaging/Bands.c:311 in ImagingMerge\n```\nThe null address confirms the dereference of the missing `image8` plane for band 0.\n\n### Impact\nNULL pointer dereference in a C extension. A crafted file opened and loaded through the IPTC path crashes the worker process. This is a hard crash, not a Python exception an application can catch.","summary":"Pillow's Image.merge() skips validating band 0, so a single-band I or F image as the first band makes the native code dereference a NULL pointer. A crafted file opened through Pillow's IPTC plugin can crash the worker process, a denial of service rather than code execution.","severity":"high","cvss":8.7,"epss":0.0029100000392645597,"exploited":false,"exploited_at":null,"products":[{"name":"pillow","fixed":["13.0.0"],"affected":">= 12.0.0, < 13.0.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-476","image-processing","package","pillow","python","self-hosted"],"not_affected_if":["no-untrusted-image-input","pillow-13-or-later","pillow-below-12.0.0"],"how_to_check":"pip show pillow (affected if version >= 12.0.0 and < 13.0.0)\nIf you process untrusted images with Pillow, check whether the worker runs in a restartable process","fix":"Upgrade pillow to 13.0.0 or later.\nUntil then, reject or avoid untrusted files that Pillow opens via the IPTC path.","status":"open","published_at":"2026-10-19T22:17:08+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T20:10:17+00:00","updated_at":"2026-10-23T21:57:19+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-23T20:10:17Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade pillow to 13.0.0 or later.\nUntil then, reject or avoid untrusted files that Pillow opens via the IPTC path.","summary":"Pillow's Image.merge() skips validating band 0, so a single-band I or F image as the first band makes the native code dereference a NULL pointer. A crafted file opened through Pillow's IPTC plugin can crash the worker process, a denial of service rather than code execution.","category":"package","who_cares":["python","pillow","image-processing","self-hosted"],"confidence":0.8,"analyst_note":"","how_to_check":"pip show pillow (affected if version >= 12.0.0 and < 13.0.0)\nIf you process untrusted images with Pillow, check whether the worker runs in a restartable process\nunknown","needs_analyst":false,"not_affected_if":["pillow-below-12.0.0","pillow-13-or-later","no-untrusted-image-input"],"severity_if_unknown":"high"},"cost_usd":0.000449,"input_tokens":2798,"output_tokens":339,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-112405':14A,22A '-160':246C '-2026':13A,21A '-476':25B '0':11A,41B,96C,99C,165C,202C,243C,263C,291C,334C,362C '02_poc.py':311C '0x000000000000':333C '1':173C,280C,338C '156':245C '169':248C '2492':341C '302':213C '311':214C,337C,346C '321':215C '3795':167C '3e2':323C '4':295C,296C,302C,303C,306C,307C '662q':18A '8':189C 'ab96':324C 'accept':254C 'address':332C,351C 'addresssanit':326C,328C,343C 'applic':395C 'attack':226C 'attacker-typ':225C 'band':10A,40B,46B,54B,93C,95C,98C,104C,164C,170C,172C,201C,209C,221C,262C,268C,279C,290C,361C 'band0':292C,299C 'bit':190C 'build':219C,320C 'byte':146C 'c':369C 'catch':397C 'check':87C,175C 'code':58B,83B 'commit':322C 'confirm':352C 'core':314C 'cp9v':17A 'craft':64B,150C,372C 'crash':73B,126C,381C,389C 'cve':12A,20A 'cwe':24B 'denial':78B 'derefer':4A,59B,200C,285C,354C,366C 'detail':159C 'drive':157C 'dump':315C 'error':327C 'everi':91C 'except':94C,393C 'execut':84B 'extens':370C 'f':49B,107C,186C 'fault':313C 'file':65B,151C,373C 'first':53B 'follow':136C 'function':255C 'ghsa':16A 'ghsa-cp9v-662q-vvx9':15A 'hard':388C 'header':155C 'host':34B 'imag':27B,50B,108C,187C,230C,240C,261C,270C 'image-process':26B 'image.merge':6A,37B,86C,297C 'image.new':293C,300C,304C 'image.open':135C 'image32':196C 'image8':192C,203C,272C,358C 'imagingmerg':199C,284C,335C,348C 'impact':363C 'import':260C 'index':242C 'input':92C 'iptc':70B,128C,141C,217C,379C 'iter':168C 'l':301C,305C 'list':222C 'load':139C,376C 'loader':218C 'loop':162C 'magic':145C 'magic-byt':144C 'make':55B 'merg':111C,235C,276C,339C 'miss':357C 'mode':89C,122C,176C,211C 'multi':208C 'multi-band':207C 'nativ':57B,110C,283C 'null':2A,61B,119C,194C,275C,287C,350C,364C 'observ':316C 'open':66B,251C,374C 'output':210C 'packag':29B 'pass':232C 'path':380C 'pil':259C 'pillow':1A,23A,30B,35B,68B 'pixel':115C 'plain':134C 'plane':191C,273C,288C,359C 'plugin':71B,129C 'poc':256C 'pointer':3A,62B,116C,365C 'prefilt':147C 'process':28B,76B,125C,384C 'python':31B,257C,310C,392C 'rather':81B 'reach':130C 'read':113C 'recogniz':154C 'regist':249C 'rgb':298C 'routin':112C 'run':308C 'segment':312C 'segv':329C,344C 'self':33B 'self-host':32B 'servic':80B 'set':198C 'singl':45B,103C,267C 'single-band':44B,102C,266C 'size':178C 'skip':38B,163C 'sourc':319C 'src/_imaging.c':340C 'src/libimaging/bands.c':212C,336C,345C 'src/pil/image.py':166C 'src/pil/iptcimageplugin.py':244C,247C 'sub':229C,239C 'sub-imag':228C,238C 'summari':85C,342C 'type':227C 'unknown':331C 'unvalid':9A 'valid':39B,161C,277C 'via':7A 'vvx9':19A 'worker':75B,383C 'y':204C","nicknames":[],"mitigations":[],"who_affected":null,"tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-19T22:17:08+00:00","kev":null,"cvss_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","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-cp9v-662q-vvx9","url":"https://github.com/advisories/GHSA-cp9v-662q-vvx9","severity":"high","published_at":"2026-10-21T20:30:25+00:00","modified_at":"2026-10-21T20:30:26+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-112405","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112405","severity":null,"published_at":"2026-10-19T22:17:08+00:00","modified_at":"2026-10-20T19:48:50+00:00","first_seen_at":"2026-10-23T21:57:19+00:00","fetched_at":"2026-10-23T22:28:57+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}