Writing

Inside PHP CVE-2026-115560: Session Upload Progress & Serialize Handler Mismatch Explained

Cutting through the clickbait around PHP's CVE-2026-115560 (CVSS 6.4) advisory: How runtime changes to session.serialize_handler collide with session.upload_progress to trigger PHP Object Injection, and the one-line php.ini mitigation.

Furkan ÖzayOctober 24, 2026 · 3 min read
czCZAY.DEV // EDITORIALEN
GÜVENLIK

#Güvenlik · 3 MIN READ

FURKAN ÖZAYVOL. 2026 // #2AD7

Whenever a new CVE drops in the PHP ecosystem, sensationalist headlines immediately scream about "total unauthenticated server takeovers." When CVE-2026-115560 (CVSS 6.4) was published by the PHP Group (GHSA-4966-6377-397r), rumors quickly confused it with HTTP wrapper flaws.

As backend engineers, our job is not to panic over headlines—it is to read the primary security advisory and understand the exact execution path. Let's dissect what CVE-2026-115560 actually is, why it happens, which applications are genuinely affected, and how to neutralize it with a single line in php.ini.

· · ·

1. The Core Mechanism: How PHP Serializes Sessions

PHP supports multiple internal formats (session.serialize_handler) for encoding the $_SESSION superglobal before writing it to disk or Redis:

  1. php (Default): Formats entries as key|serialized_value (using the pipe | character as a delimiter).
  2. php_serialize (Introduced in PHP 5.5.4): Formats the entire session array using standard serialize() without special pipe delimiters.

As long as an application uses the exact same serializer when writing and reading a session, everything is safe. Danger arises when data is written using php_serialize (which allows raw | characters inside strings) and later read back using the php handler (which treats everything after | as a serialized PHP object!).

· · ·

2. Where session.upload_progress Enters the Picture

When session.upload_progress.enabled = 1 is active in php.ini, PHP inspects multipart file uploads BEFORE your application code (index.php) even starts executing. If the upload contains a PHP_SESSION_UPLOAD_PROGRESS field, the PHP engine automatically initializes the session during the request startup phase using the default serializer defined in php.ini.

The CVE-2026-115560 Flaw

What happens if your PHP code subsequently changes the handler at runtime using:

php
// Changing the serializer dynamically at runtime before session_start()
ini_set('session.serialize_handler', 'php_serialize');
session_start();

Because the upload progress tracker already initialized session structures under a different handler expectation, a serializer mismatch occurs between the upload progress payload and the runtime session decoder.

An attacker can send a multipart upload containing a payload prefixed with a pipe character (|O:8:"Exploit":...). When the mismatched handler deserializes the session file, it triggers PHP Object Injection (Deserialization of Untrusted Data)!

Info: Why Did It Score CVSS 6.4 Instead of 9.8?

Unlike universal RCE flaws, CVE-2026-115560 requires two specific preconditions: session.upload_progress.enabled must be turned on, AND the application must dynamically override session.serialize_handler at runtime via ini_set(). Standard Laravel or Symfony apps that configure sessions statically are not exposed to the runtime mismatch.

· · ·

3. Immediate Mitigation & Patched PHP Versions

Step 1: Upgrade PHP

The PHP team patched this lifecycle mismatch in the official October 2026 security releases:

  • PHP 8.3.x ➔ Upgrade to patched release
  • PHP 8.4.x / 8.5.x ➔ Upgrade to patched release

Step 2: Disable session.upload_progress in php.ini

Modern web applications track file upload progress on the client side using JavaScript (XMLHttpRequest.upload.onprogress or Fetch streams), making PHP's legacy server-side upload progress session tracking completely redundant—and a recurring attack surface.

Open your production php.ini and disable it permanently:

ini
; Disable legacy server-side upload progress session injection
session.upload_progress.enabled = 0

Restart php-fpm (systemctl reload php8.3-fpm), and the entire attack vector is permanently closed.

Interactive Roadmap & Tool

Read next