Safeguard
Application Security

CVE-2021-25288: Buffer overflow in Pillow FLI decoder

CVE-2021-25288 is a buffer overflow in Pillow's FLI decoder, fixed in Pillow 8.1.0. Here's what's affected, the risk profile, and how to remediate.

Aman Khan
AppSec Engineer
8 min read

Pillow, the widely used Python imaging library that powers image handling in countless web backends, data pipelines, and automation scripts, has shipped several memory-safety vulnerabilities in its native C decoders over the years. CVE-2021-25288 is one of a cluster of such issues: a buffer overflow in Pillow's FLI decoder that can be triggered by a specially crafted FLI/FLC animation file. Because Pillow is typically invoked on untrusted, user-supplied images (avatars, uploads, thumbnails, document previews), a flaw in a format-specific decoder like this is a direct path from "attacker uploads a file" to memory corruption inside the application process — a classic supply-chain-adjacent risk, since the vulnerable code isn't in your application logic at all, but in a transitive dependency you likely never audit line by line.

What CVE-2021-25288 actually is

FLI (and its variant FLC) is an old animation/bitmap format originally associated with Autodesk Animator. Pillow includes a native decoder (FliDecode.c) that reconstructs frames using the format's run-length-encoded chunks and palette updates. Like several other Pillow image codecs patched around the same time (SGI RLE, PCX, TIFF, and DDS decoders were all affected by sibling CVEs in the same disclosure wave), the FLI decoder did not sufficiently validate chunk lengths and offsets against the size of the destination buffer before writing decoded pixel data into it. A malicious FLI file can supply frame or chunk parameters that cause the decoder to write past the bounds of its allocated buffer.

The practical impact of this class of bug is primarily denial of service (a crash/segfault when Pillow processes the malicious file), with the more severe theoretical outcome — memory corruption leading to code execution — dependent on heap layout, allocator behavior, and platform hardening (ASLR, stack canaries, etc.). Pillow's own advisories and the corresponding NVD entry describe this as an out-of-bounds write in image decoding, consistent with a classic buffer overflow rather than a purely informational read.

This CVE does not require the attacker to have any access to the target system beyond the ability to get a crafted file processed — for example, by uploading it to a service that generates thumbnails, runs OCR, or otherwise opens images with Pillow. That "attacker controls the input file, victim's code just has to call Image.open() or decode it" pattern is exactly why image-parsing libraries are such a persistent source of real-world vulnerabilities, and why they deserve the same scrutiny as any other third-party dependency in a software bill of materials.

Affected versions and components

The vulnerability lives in Pillow's C-extension FLI decoder, which ships as part of the core Pillow package (the maintained fork of the original PIL). It affects versions of Pillow prior to the release that consolidated the fix for this and several related decoder issues, which landed as part of the Pillow 8.1.0 release. If your project — or any transitive dependency in your Python environment — pins or resolves to a Pillow version older than 8.1.0, and that code path ever decodes FLI/FLC files (directly or via a general-purpose Image.open() call that auto-detects format), it should be considered exposed.

Because Pillow is one of the most widely depended-upon packages in the Python ecosystem — pulled in by web frameworks, ML/data-science tooling, document processors, and CMS platforms — the practical blast radius of "which of our services eventually call into Pillow's image decoders" is often much larger than teams expect, and frequently includes services that don't think of themselves as "image processing" applications at all.

CVSS, EPSS, and KEV context

NVD tracks CVE-2021-25288 as an out-of-bounds write / buffer overflow issue reachable through crafted image input, in the same family as the other Pillow decoder CVEs disclosed around the same time (CVE-2021-25287, -25289, -25290, -25291, -25292, -25293). Vulnerabilities in this cluster share the same general risk shape: network/file-vector, low attack complexity, no privileges required, but they typically depend on the victim application actually invoking the vulnerable decode path on attacker-supplied content. We won't restate a specific numeric CVSS score here, since scoring for this batch of Pillow advisories has varied slightly across sources (NVD vs. the upstream GitHub Security Advisory) and we'd rather point you to the authoritative NVD/GHSA record for the exact current figure than risk repeating a stale or mismatched number.

There is no indication that CVE-2021-25288 has an elevated EPSS (Exploit Prediction Scoring System) probability — consistent with most Pillow decoder overflows, which lack known, widely circulated public exploits and are generally leveraged (if at all) in targeted rather than mass-exploitation scenarios. It is also not currently listed in CISA's Known Exploited Vulnerabilities (KEV) catalog. That combination — real memory-safety bug, no confirmed in-the-wild mass exploitation, not on KEV — is common for image-library CVEs and is precisely why they tend to get deprioritized in vulnerability triage, even though the underlying risk (arbitrary file upload leading to memory corruption in a core dependency) is one that's easy for an attacker to exploit opportunistically once a target is identified as running an old Pillow version. Low EPSS and absence from KEV should inform urgency, not eliminate remediation.

Timeline

  • Discovery/Report: The FLI decoder overflow, along with several sibling decoder issues, was identified and reported through Pillow's security advisory process on GitHub, as part of a broader audit of Pillow's native format decoders.
  • Coordinated fix: The Pillow maintainers addressed CVE-2021-25288 together with the other decoder vulnerabilities discovered in the same period, adding bounds validation to the affected decode paths.
  • Public release: The fix shipped in Pillow 8.1.0, released in early January 2021, which bundled fixes for the full set of that disclosure round's decoder CVEs (FLI, SGI RLE, PCX, TIFF, and DDS issues) in a single hardened release.
  • CVE/NVD publication: CVE-2021-25288 was subsequently published with details in the National Vulnerability Database, referencing the corresponding GitHub Security Advisory.

Because this was a coordinated, multi-CVE hardening release rather than an isolated emergency patch, the most reliable remediation signal for any of these Pillow issues — including this one — is simply: are you on Pillow 8.1.0 or later? If yes, this CVE and its siblings from the same release are addressed.

Remediation steps

  1. Identify your Pillow version everywhere it's used. Run pip show Pillow or inspect your lockfiles (requirements.txt, Pipfile.lock, poetry.lock) across every service, script, worker, and CI image — not just your primary application repo. Pillow frequently arrives as a transitive dependency of frameworks, PDF tools, ML libraries, and admin tooling.
  2. Upgrade to Pillow 8.1.0 or later. Since Pillow has continued to release regular security updates after 8.1.0, upgrading to the latest supported release (rather than pinning exactly to 8.1.0) is strongly preferred — it picks up fixes for later CVEs as well.
  3. Re-pin and re-lock. Update your dependency manifests and lockfiles, rebuild affected container images, and redeploy — a fixed version in requirements.txt does nothing until it's actually rebuilt into running artifacts.
  4. Audit code paths that call Image.open() or format-specific decoders on user- or externally-supplied files (uploads, email attachments, scraped content, third-party integrations) to confirm they route through the patched library, including in any vendored or bundled copies of Pillow.
  5. Add input-handling defenses in depth, independent of the library patch: run image processing in a sandboxed or resource-constrained worker (memory/CPU limits, seccomp, restricted filesystem access), validate file types beyond trusting extensions, and consider a allow-list of expected formats if FLI/FLC support isn't actually needed by your application.
  6. Verify with a dependency/SBOM scan, not just a manual check — this is exactly the kind of transitive, easy-to-miss dependency that automated software composition analysis is built to catch.

How Safeguard helps

CVE-2021-25288 is a textbook illustration of why supply chain visibility has to extend past your own code and into every native library your dependencies pull in. A team can review every line of its own image-handling logic and still ship a vulnerable FLI decoder because it's buried three layers deep in a transitive dependency graph.

Safeguard continuously generates and monitors software bills of materials across your services, so a Pillow version below a patched release — whether it's a direct dependency or nested inside another package — is surfaced automatically, with the specific CVE, affected component, and remediation path attached, instead of relying on someone manually cross-referencing pip freeze output against advisories. Safeguard correlates that exposure with real-world signals like EPSS trends and KEV status so your team can prioritize the Pillow instances that matter most (internet-facing upload handlers processing untrusted images) ahead of low-risk internal tooling, rather than treating every CVE with equal urgency. And because these decoder-hardening releases tend to arrive in batches — as 8.1.0 did — Safeguard's continuous scanning means you catch the next Pillow advisory the moment it's published, across every repository and artifact, rather than discovering months later during a point-in-time audit that a forgotten microservice never got the memo.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.