CVE-2026-45034: Phpoffice/phpspreadsheet PHPSpreadsheet Has A Patch Bypass Vulnerability
CRITICALCVE-2026-45034 is a CRITICAL phpspreadsheet has a patch bypass vulnerability (CWE-502: Deserialization of Untrusted Data) affecting Phpoffice/phpspreadsheet libraries in the Packagist ecosystem. The issue involves phpspreadsheet has a patch bypass for cve-2026-34084. Remediation is available by upgrading affected packages to version 1.30.5.
CVE-2026-45034 at a Glance
How to Fix CVE-2026-45034
To remediate this vulnerability, upgrade all affected Phpoffice/phpspreadsheet dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade phpoffice/phpspreadsheet to 1.30.5
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for phpoffice/phpspreadsheet
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-45034, inspect your project dependencies for Phpoffice/phpspreadsheet:
inspect dependencies for phpoffice/phpspreadsheet
Analyze the terminal output to locate any version of Phpoffice/phpspreadsheet in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.30.5), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-502: Deserialization of Untrusted Data, where the product deserializes untrusted data without sufficiently ensuring that the resulting data will be valid. In Phpoffice/phpspreadsheet, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-45034 Affected Phpoffice/phpspreadsheet Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
phpoffice/phpspreadsheet
|
Packagist |
< 1.30.5
|
1.30.5
|
References
Frequently Asked Questions
What is CVE-2026-45034?
Which Phpoffice/phpspreadsheet versions are affected?
What version fixes CVE-2026-45034?
Is phpoffice/phpspreadsheet affected?
What CWE is associated with CVE-2026-45034?
How can I check Packagist dependencies for CVE-2026-45034?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-87m4-826x-3crx",
"modified" : "2026-06-08T23:00:14Z",
"published" : "2026-06-08T23:00:14Z",
"aliases" : [ "CVE-2026-45034" ],
"summary" : "PHPSpreadsheet has a patch bypass for CVE-2026-34084 ",
"details" : "## Summary\n\nCVE-2026-34084 was patched by the helper `File::prohibitWrappers`. The helper calls `parse_url($filename, PHP_URL_SCHEME)` and then checks `is_string($scheme) && strlen($scheme) > 1` to reject stream wrappers such as `phar://`, `php://`, `data://` or `expect://`. The check is not equivalent to \"does the path contain a wrapper\". When the input has the form `phar:///path/file.phar/inner` with three or more slashes after the scheme, `parse_url` returns boolean `false` instead of returning the scheme string. The `is_string($scheme)` branch is therefore skipped, the helper returns without throwing, and the caller proceeds. PHP's stream layer, however, still treats `phar:///...` as a valid phar wrapper and opens the underlying phar file. The result is that `IOFactory::load($attackerPath)` walks past the patch and still touches the phar wrapper. On PHP 7.x, simply reaching the phar wrapper via `is_file` is enough for PHP to automatically deserialize the phar metadata, which in turn invokes the magic methods `__wakeup` and `__destruct` of an attacker controlled object and gives full RCE. On PHP 8.x, automatic metadata deserialization for plain file ops was removed, so the chain at the PhpSpreadsheet layer reduces to a phar wrapper file read primitive, and RCE only resurfaces if the downstream consumer ever calls `Phar::getMetadata`.\n\n## Vulnerable code\n\nThe file `vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php` is byte identical across all six latest tags listed below:\n\n```php\npublic static function prohibitWrappers(string $filename): void\n{\n $scheme = parse_url($filename, PHP_URL_SCHEME);\n if (is_string($scheme) && strlen($scheme) > 1) {\n throw new Exception(\"Stream wrappers are not permitted as file paths: {$filename}\");\n }\n}\n```\n\nFor input `phar://x/dummy.csv` the call returns the string `\"phar\"` and the throw fires correctly. For input `phar:///work/exploit.phar/dummy.csv` the same call returns `false` and the throw is skipped, which is the bypass.\n\n## Confirmed affected versions\n\nTested on 2026-05-03. The `prohibitWrappers` source on disk is identical across all six tags.\n\n| Branch | Latest tag | PHP under test | Result |\n|---|---|---|---|\n| 1.x | 1.30.4 | 7.4 | bypass plus full RCE, gadget wrote marker file |\n| 2.1.x | 2.1.16 | 8.3 | bypass |\n| 2.4.x | 2.4.5 | 8.3 | bypass |\n| 3.10.x | 3.10.5 | 8.3 | bypass |\n| 5.6.x | 5.6.0 | 8.3 | bypass |\n| 5.7.x | 5.7.0 | 8.3 | bypass |\n\nVersion 1.30.4 is the latest tag of the 1.x branch, which is the only branch that still supports PHP 7.x. Version 5.7.0 is the latest tag overall on Packagist at the time of testing.\n\nNo branch beyond 1.30.x allows any release before Php 8. For branches beyond 1.30.x, although the code identified above is in error, and will be corrected, it does not lead to any security exposure. That would require a `Phar::getMetadata` call, which is not present in PhpSpreadsheet. If possible and reasonable, the release notes for the fix on the other branches will include `release-note: security`.\n\n## Reproduction\n\nRequires Docker only, no local PHP install. Run:\n\n```sh\nbash run.sh\n```\n\nThe script does the following in order: build `exploit.phar` using `php:7.4-cli` with `phar.readonly=0`, install `phpoffice/phpspreadsheet:5.7.0` through composer and run `exploit.php` on `php:8.3-cli` to show that the bypass still works against the latest tag, then install `1.30.4` and run again on `php:7.4-cli` to show the full RCE chain. All output is teed to `evidence.txt`.\n\n`exploit.php` ships two controls. The negative control uses `phar://x/dummy.csv` to confirm that the patch still rejects the standard wrapper form. The positive control uses `phar:///work/exploit.phar/dummy.csv` to show that the three slash variant slips through. On PHP 7.4 the gadget writes the file `pwned_marker` containing the lines `WAKEUP: phpspreadsheet-bypass` and `DESTRUCT: phpspreadsheet-bypass`, which is the proof that attacker controlled code ran inside the victim process.\n\n## Suggested fix\n\nDo not rely on `parse_url` to detect wrappers, because its behavior depends on the slash count and on the PHP version. Either of these is safe:\n\n```php\npublic static function prohibitWrappers(string $filename): void\n{\n if (str_contains($filename, '://')) {\n throw new Exception(\"Stream wrappers are not permitted as file paths: {$filename}\");\n }\n}\n```\n\nAlternatively, run the path through `realpath()` first, since `realpath` returns `false` for any wrapper prefixed path.\n\n## Files in this report\n- [build-phar.php](https://github.com/user-attachments/files/27317345/build-phar.php): builds `exploit.phar` with a gadget object in its metadata.\n- [exploit.php](https://github.com/user-attachments/files/27317343/exploit.php): main PoC, with the negative and positive controls.\n- [exploit.phar](https://github.com/user-attachments/files/27317356/exploit.phar.txt): prebuilt phar, the gadget writes a marker when deserialized.\n- [composer.json](https://github.com/user-attachments/files/27317341/composer.json): spec used by composer to install the version under test.\n- [run.sh](https://github.com/user-attachments/files/27317342/run.sh): end to end reproducer through Docker.\n- [evidence.txt](https://github.com/user-attachments/files/27317339/evidence.txt): log captured from the most recent `run.sh` invocation.",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "phpoffice/phpspreadsheet"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.30.5"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 1.30.4"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-87m4-826x-3crx"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/PHPOffice/PhpSpreadsheet"
}, {
"type" : "ADVISORY",
"url" : "https://github.com/advisories/GHSA-q4q6-r8wh-5cgh"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-502" ],
"severity" : "CRITICAL",
"github_reviewed" : true,
"github_reviewed_at" : "2026-06-08T23:00:14Z",
"nvd_published_at" : null
}
}