Skip to main content

PhpSpreadsheet EUVDEUVD-2026-38359

| CVE-2026-45034 CRITICAL
Deserialization of Untrusted Data (CWE-502)
2026-06-08 https://github.com/PHPOffice/PhpSpreadsheet GHSA-87m4-826x-3crx
9.2
CVSS 4.0 · Vendor: https://github.com/PHPOffice/PhpSpreadsheet
Share

Severity by source

Vendor (https://github.com/PHPOffice/PhpSpreadsheet) PRIMARY
9.2 CRITICAL
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/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
vuln.today AI
8.1 HIGH

Network-reachable via app loader, AC:H because RCE needs PHP 7.x plus a usable gadget chain and attacker-staged phar; PR:N/UI:N once a vulnerable upload/load path exists.

3.1 AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
4.0 AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/PHPOffice/PhpSpreadsheet).

CVSS VectorVendor: https://github.com/PHPOffice/PhpSpreadsheet

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/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
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
X

Lifecycle Timeline

6
Analysis Updated
Jun 22, 2026 - 21:46 vuln.today
v3 (cvss_changed)
Analysis Updated
Jun 22, 2026 - 21:45 vuln.today
v2 (cvss_changed)
Re-analysis Queued
Jun 22, 2026 - 21:39 vuln.today
cvss_changed
CVSS changed
Jun 22, 2026 - 21:39 NVD
9.2 (CRITICAL)
Source Code Evidence Fetched
Jun 08, 2026 - 23:15 vuln.today
Analysis Generated
Jun 08, 2026 - 23:15 vuln.today

DescriptionCVE.org

Summary

CVE-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.

Vulnerable code

The file vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php is byte identical across all six latest tags listed below:

php
public static function prohibitWrappers(string $filename): void
{
    $scheme = parse_url($filename, PHP_URL_SCHEME);
    if (is_string($scheme) && strlen($scheme) > 1) {
        throw new Exception("Stream wrappers are not permitted as file paths: {$filename}");
    }
}

For 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.

Confirmed affected versions

Tested on 2026-05-03. The prohibitWrappers source on disk is identical across all six tags.

BranchLatest tagPHP under testResult
1.x1.30.47.4bypass plus full RCE, gadget wrote marker file
2.1.x2.1.168.3bypass
2.4.x2.4.58.3bypass
3.10.x3.10.58.3bypass
5.6.x5.6.08.3bypass
5.7.x5.7.08.3bypass

Version 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.

No 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.

Reproduction

Requires Docker only, no local PHP install. Run:

sh
bash run.sh

The 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.

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.

Suggested fix

Do 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:

php
public static function prohibitWrappers(string $filename): void
{
    if (str_contains($filename, '://')) {
        throw new Exception("Stream wrappers are not permitted as file paths: {$filename}");
    }
}

Alternatively, run the path through realpath() first, since realpath returns false for any wrapper prefixed path.

Files in this report

  • build-phar.php: builds exploit.phar with a gadget object in its metadata.
  • exploit.php: main PoC, with the negative and positive controls.
  • exploit.phar: prebuilt phar, the gadget writes a marker when deserialized.
  • composer.json: spec used by composer to install the version under test.
  • run.sh: end to end reproducer through Docker.
  • evidence.txt: log captured from the most recent run.sh invocation.

AnalysisAI

Phar deserialization in PhpSpreadsheet (PHPOffice) is reachable on PHP 7.x because the File::prohibitWrappers helper added to patch CVE-2026-34084 can be bypassed with a three-slash phar URI such as phar:///path/file.phar/inner, where parse_url returns false and the wrapper check is skipped. Remote attackers who can supply a file path to IOFactory::load() achieve full RCE on PHP 7.x branches (1.x up to 1.30.4) and a phar file-read primitive on PHP 8.x branches up to 5.7.0; publicly available exploit code exists with a working Docker reproducer, though EPSS is only 0.04% (12th percentile).

Technical ContextAI

PhpSpreadsheet is a widely used pure-PHP library (Composer package phpoffice/phpspreadsheet, CPE pkg:composer/phpoffice_phpspreadsheet) for reading and writing spreadsheet formats. The root cause is CWE-502 (Deserialization of Untrusted Data) compounded by an incorrect input validation primitive: the patch for CVE-2026-34084 relied on parse_url($filename, PHP_URL_SCHEME) to detect stream wrappers, but parse_url returns boolean false for malformed URIs like phar:///path/file.phar/inner (three slashes after the scheme), so is_string($scheme) is false and the throw is skipped. PHP's stream layer still treats the same path as a valid phar wrapper. On PHP 7.x, automatic phar metadata deserialization on file-stat operations such as is_file triggers __wakeup/__destruct of attacker-controlled gadget objects; PHP 8.x removed that auto-deserialization, so the same bypass yields only a phar file-read unless a downstream caller invokes Phar::getMetadata directly.

RemediationAI

Vendor-released patch: upgrade phpoffice/phpspreadsheet to 1.30.5 or later on the 1.x branch (per advisory GHSA-87m4-826x-3crx); fixes for branches 2.1.x, 2.4.x, 3.10.x, 5.6.x, and 5.7.0 are noted as forthcoming in the GitHub advisory and should be tracked at https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-87m4-826x-3crx. If immediate upgrade is not possible, apply the vendor-suggested code-level mitigation by replacing the parse_url-based check in File::prohibitWrappers with a str_contains($filename, '://') test or by normalizing the path through realpath() (which returns false for any wrapper prefix) - note that realpath will also reject legitimate non-existent relative paths, so test against existing call sites. Additional compensating controls: strictly validate or canonicalize any user-supplied path before passing it to IOFactory::load()/Reader::load(), reject inputs containing ://, and migrate off PHP 7.x to remove the automatic phar-metadata-deserialization sink (this eliminates direct RCE even if the bypass remains reachable, at the cost of an EOL PHP upgrade).

More in PHP

View all
CVE-2012-1823 CRITICAL POC
9.8 May 11

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

CVE-2016-1555 CRITICAL POC
9.8 Apr 21

(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear

CVE-2024-11680 CRITICAL POC
9.8 Nov 26

ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C

CVE-2025-49113 CRITICAL POC
9.9 Jun 02

Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au

CVE-2017-9841 CRITICAL POC
9.8 Jun 27

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

CVE-2025-0108 HIGH POC
8.8 Feb 12

Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers

CVE-2021-25298 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2021-25296 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2013-4983 CRITICAL POC
10.0 Sep 10

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

CVE-2023-6553 CRITICAL POC
9.8 Dec 15

The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1

CVE-2024-46506 CRITICAL POC
10.0 May 13

NetAlertX (formerly PiAlert) versions 23.01.14 through 24.x before 24.10.12 allow unauthenticated command injection thro

CVE-2024-8353 CRITICAL POC
9.8 Sep 28

The GiveWP - Donation Plugin and Fundraising Platform plugin for WordPress is vulnerable to PHP Object Injection in all

Share

EUVD-2026-38359 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy