CVE-2024-45048 Overview
CVE-2024-45048 is an XML External Entity (XXE) vulnerability in PhpSpreadsheet, a pure PHP library for reading and writing spreadsheet files. The flaw allows an attacker to bypass the library's XML filter by supplying a double-encoded XML payload. Successful exploitation lets an attacker read the contents of local files on the server, even when PHP error reporting is muted. The issue is tracked as CWE-611: Improper Restriction of XML External Entity Reference and is resolved in PhpSpreadsheet version 2.2.1.
Critical Impact
An attacker who convinces a user or application to load a crafted spreadsheet file can exfiltrate arbitrary local file contents from the server processing the file.
Affected Products
- PHPOffice PhpSpreadsheet versions prior to 2.2.1
- PHP applications that ingest user-supplied spreadsheet files through PhpSpreadsheet readers
- Web services and SaaS platforms that offer spreadsheet import functionality backed by PhpSpreadsheet
Discovery Timeline
- 2024-08-28 - CVE-2024-45048 published to the National Vulnerability Database (NVD)
- 2026-06-17 - Last updated in NVD database
Technical Details for CVE-2024-45048
Vulnerability Analysis
PhpSpreadsheet includes an XmlScanner component that inspects XML payloads before parsing to block XXE payloads. The scanner detected the declared character set once and converted the XML to UTF-8, but did not re-validate the resulting document for a secondary encoding declaration. An attacker could craft a document with a benign-looking outer encoding wrapping a malicious inner XML that carried external entity declarations. Once decoded, the inner payload reached the parser without being re-scanned, allowing entity resolution against local files.
Because the parser errors are silenced in many production configurations, the attacker relies on side channels or reflected output to retrieve leaked file contents. The vulnerability requires user interaction, typically the upload or opening of a crafted spreadsheet file.
Root Cause
The root cause lies in src/PhpSpreadsheet/Reader/Security/XmlScanner.php. The toUtf8() method used a single regular expression pass to detect the XML encoding and did not consistently re-check the document after transcoding. Double-encoded XML therefore evaded the XXE prevention checks. The patch introduces a findCharSet() helper that is applied both before and after conversion, and aborts loading when a suspicious double-encoded document is detected.
Attack Vector
Exploitation requires the victim application to open an attacker-supplied spreadsheet. The attack is delivered over the network with no authentication required, but relies on user interaction to trigger file loading. On success, the attacker reads local files accessible to the PHP process, which can include configuration files, credentials, and application source code.
// Patch excerpt from XmlScanner.php - hardening toUtf8() against double-encoded XML
private function toUtf8(string $xml): string
{
- $pattern = '/encoding="(.*?)"/';
- $result = preg_match($pattern, $xml, $matches);
- $charset = strtoupper($result ? $matches[1] : 'UTF-8');
-
+ $charset = $this->findCharSet($xml);
if ($charset !== 'UTF-8') {
$xml = self::forceString(mb_convert_encoding($xml, 'UTF-8', $charset));
- $result = preg_match($pattern, $xml, $matches);
- $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+ $charset = $this->findCharSet($xml);
if ($charset !== 'UTF-8') {
throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
}
Source: PHPOffice PhpSpreadsheet commit bea2d4b
Detection Methods for CVE-2024-45048
Indicators of Compromise
- Spreadsheet files (.xlsx, .xml, .ods) containing nested or double <?xml encoding="..."?> declarations
- Presence of <!DOCTYPE> and <!ENTITY SYSTEM ...> blocks referencing local file paths such as file:///etc/passwd or php://filter/
- PHP process reading unexpected files immediately after handling a user-uploaded spreadsheet
- Reader\Exception entries in application logs mentioning Suspicious Double-encoded XML after upgrading, indicating attempted exploitation
Detection Strategies
- Inspect inbound spreadsheet uploads at the application gateway for XML external entity declarations before passing them to PhpSpreadsheet
- Instrument the PHP application to log the file paths accessed during spreadsheet import and alert on reads outside expected directories
- Correlate spreadsheet upload events with subsequent outbound network connections initiated by the PHP-FPM or web worker process
Monitoring Recommendations
- Enable PHP error logging for libxml warnings and monitor for suppressed external entity load attempts
- Track file integrity on sensitive configuration files that the web application should not normally read
- Alert on unexpected DNS or HTTP callbacks originating from application servers immediately after spreadsheet processing
How to Mitigate CVE-2024-45048
Immediate Actions Required
- Upgrade PhpSpreadsheet to version 2.2.1 or later across all applications and dependencies
- Audit composer.lock files and container images to identify transitive uses of phpoffice/phpspreadsheet
- Restrict which users can upload spreadsheets and validate file provenance in workflows that ingest documents from external sources
Patch Information
The fix is delivered in PhpSpreadsheet 2.2.1 via commit bea2d4b30f24bcc8a7712e208d1359e603b45dda. Full technical background is available in the GitHub Security Advisory GHSA-ghg6-32f9-2jp7. The maintainers state there are no supported workarounds; upgrading is the only remediation.
Workarounds
- No official workaround exists per the vendor advisory; upgrading to 2.2.1 is required
- As a defense-in-depth measure, run PHP workers under a least-privilege account that cannot read sensitive files such as /etc/shadow or application secrets
- Disable external entity loading globally in the PHP process where possible
# Upgrade PhpSpreadsheet via Composer to the patched release
composer require phpoffice/phpspreadsheet:^2.2.1
# Verify the installed version
composer show phpoffice/phpspreadsheet | grep versions
# Defense-in-depth: disable external entity loading in application bootstrap
# Add to a PHP bootstrap file loaded before any XML parsing
# libxml_disable_entity_loader(true); // PHP < 8.0
# For PHP 8+, ensure LIBXML_NOENT is never passed to loaders
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.
