CVE-2026-115560: PHP's Unpatched Session Bug and the One-Line Fix
PHP's new session bug only reaches apps that switch serialize handlers at runtime with upload progress on. How to check yours, and where the one-line fix must go.

If you followed PHP's emergency advice on Wednesday, you already have the fix for the bug it warned about on Friday. PHP's new informational advisory describes CVE-2026-115560, an unlikely but real path to remote code execution through session upload progress, and the protection is the same single setting it recommended for PHP2Shell: session.upload_progress.enabled=0.
The bug works only when three things line up in one application, and most PHP sites match none of them. PHP isn't shipping an emergency release for it: the fix comes with the next regular bugfix release, which has no date yet. The Hacker News covered the announcement; this post is the practical side of it. It shows how to check your code and configuration for the three conditions, where the setting has to live to take effect, and why the line that closes this bug does not close PHP2Shell.
Set session.upload_progress.enabled=0 till next PHP release to block an unlikely RCE vector (CVE-2026-115560). Already recommended for CVE-2026-109619 Wed, keep disabled. Only apps with non-default serialize_handler switching it to "php" at runtime can be affected. #php #phpc
— The PHP Foundation (@ThePHPF) October 23, 2026
The three conditions, and how to check each one
Every PHP session is stored as text between requests, and session.serialize_handler decides the format of that text (our older guide to sessions and authentication in PHP covers the basics). PHP's advisory says an application is exposed only when all three of these are true at once:
The configuration sets
session.serialize_handlertophp_binaryorphp_serialize. The default isphp, so this is a deliberate change in php.ini, a pool file, a virtual host or a.user.ini.Application code switches it back to
phpat runtime withini_set(), before it callssession_start().Session upload progress is still enabled. It is on by default.
I'd start with the code, because the second condition is the rare one. Search your application and its dependencies for the setting's name rather than for one exact call, so that a switch made through a variable, or through the options array of session_start(), still turns up:
grep -rn --include='*.php' "serialize_handler" /var/www/your-appKeep vendor/ in that search. The 2015 PHP bug report about this pattern pointed at Zend Framework code that switched the handler, so a match can sit in a library you never wrote. No matches at all means the second condition can't be met, and PHP says applications that never change the serialize handler at runtime “can keep the feature enabled”.
For the first and third conditions, ask PHP the way your web server runs it. On the command line, php -i reads the CLI's own configuration, and on Debian and Ubuntu that is a different php.ini from the one PHP-FPM or Apache loads. A throwaway script answers for the real thing:
<?php
// check.php: open it through your site, read the output, then delete it.
header('Content-Type: text/plain');
echo 'PHP ', PHP_VERSION, ' (', PHP_SAPI, ")\n";
echo 'session.serialize_handler = ', ini_get('session.serialize_handler'), "\n";
echo 'session.upload_progress.enabled = ', ini_get('session.upload_progress.enabled'), "\n";A handler of php means the first condition fails. An upload_progress.enabled of 1 means the feature is live. Delete the file when you're done, because it tells anyone who finds it exactly which PHP version you run.
Where the switch has to go
The fix is one line, but it only works in the right place. PHP's manual lists session.upload_progress.enabled as changeable per directory or in the main configuration, never from code, so ini_set('session.upload_progress.enabled', '0') returns false and changes nothing. The official php:8.4 Docker image ships without a php.ini, so on it the feature stays on until you switch it off.
PHP's advisory for the PHP2Shell flaws says to set it “administratively and restart every worker”, and PHP's security announcement adds: “Do not rely on ini_set() or FPM .user.ini.” In practice, that means one of these:
You run PHP as | Put the setting in | Then |
|---|---|---|
PHP-FPM (behind Nginx, or Apache with proxy_fcgi) | the pool file: | restart PHP-FPM |
Apache with mod_php | the virtual host: | restart Apache |
The official PHP Docker image | a file in | rebuild and redeploy the image |
Any of them, server-wide | the php.ini of the SAPI that serves the site, or a file in its | restart that SAPI |
I'd use the pool or virtual-host line where you can. On the official php:8.4-fpm image, a pool-level php_admin_flag stopped upload tracking even with a .user.ini beside the script trying to turn it back on. A .user.ini that sets it to 0 does work once PHP-FPM reads it, but PHP-FPM reads it only when the web server passes the document root. Without that, the same file was skipped silently and uploads were still tracked, which is exactly the kind of quiet failure PHP's warning is about.
After the restart, load the check script again. Seeing session.upload_progress.enabled = 0 there is the proof I'd trust: editing a file without restarting leaves every running worker on the old value.
Switching it off costs very little. Session upload progress matters only to upload bars that ask the server for a percentage while a file is still arriving. A bar built on the browser's own XMLHttpRequest upload events doesn't use it, and PHP's manual already suggests disabling the option “in applications that do not use upload progress bars”.
Why switching serialize handlers is the dangerous part
PHP hasn't published the technical details yet, so here is what its documentation says about the moving parts, and why these three conditions are a known bad combination.
Session upload progress writes to the session while a file is still uploading, “before any scripts are executed”, in the words of PHP's manual. What it writes comes from the client: the value of the upload's progress field becomes part of the session key, and the file's name and form field name are stored with it. PHP's configuration reference warns that these names “are not validated and may contain PHP code”.
That write uses the serializer from the configuration, php_serialize or php_binary. The script then switches to php and starts the session, so the same bytes are read back in a different format. The php format marks where each key ends and its serialized value begins with a | character. On the official php:8.4-fpm image with default settings, one ordinary upload left this in the session file before the script had run a single line (the progress field's value was check):
upload_progress_check|a:5:{s:10:"start_time";i:1792759175;s:14:"content_length";i:208;…Read with the wrong handler, client-chosen text that contains a | can decide what PHP unserializes. Unserializing data an attacker shaped is PHP object injection. It turns into code execution only if the application loads classes whose magic methods can be chained into something harmful, so the risk depends on each codebase as much as on PHP.
None of this is new. In December 2015, PHP bug #71101 described upload progress data written with one handler and read with another after an ini_set() switch, and warned that “arbitrarily session data injection is possible”. The maintainers didn't treat it as a vulnerability, since it needs the application to make an unusual change itself. In 2021 they turned it into a documentation issue titled “serialize_handler must not be switched for existing sessions”.
PHP hasn't said whether CVE-2026-115560 is that old report or a new route to the same place. My guess is that the attention around PHP2Shell sent someone back to a corner the project had filed away.
The one-line fix does not cover PHP2Shell
This is the misreading I'd watch for. PHP says that if you applied session.upload_progress.enabled=0 for PHP2Shell and kept it, “no further action is needed” for CVE-2026-115560. That sentence is about the new bug alone. For the two PHP2Shell flaws, PHP's release announcements are blunt: “There are no complete mitigations, updating is the only safe choice.”
Here is how the three issues compare, from PHP's own advisories:
CVE | What PHP calls it | Severity | Fixed in | Does upload_progress=0 protect you? |
|---|---|---|---|---|
CVE-2026-109618 | RCE via OOM corruption | Critical, CVSS 9.2 | 8.5.12, 8.4.27, 8.3.36, 8.2.35 | No: update |
CVE-2026-109619 | RCE via stale global state | Critical, CVSS 9.2 | 8.5.12, 8.4.27, 8.3.36, 8.2.35 | Only on the session paths: update |
CVE-2026-115560 | An unlikely RCE vector in session upload progress | Not published yet | The next regular bugfix release | Yes, until the fix ships |
So the order of work is: update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or to your distribution's patched package, for PHP2Shell, then keep the upload progress switch off for CVE-2026-115560 until the regular release ships. PHP 8.1 and older no longer get updates from the PHP project. Our guide to confirming that PHP2Shell fixes reach the patched workers covers checking that the new build is the one actually serving requests, and the PHP2Shell explainer covers how those two flaws work.
I'd leave the switch off after the fix ships too, unless something on your site uses it. PHP's advisory for CVE-2026-109619 makes the same suggestion: it “might be prudent” to keep the feature disabled if you don't need it, “even after applying the patch”.
What PHP hasn't said yet
The advisory is short, and the gaps matter for planning. PHP has not said:
which PHP versions are affected;
which releases will contain the fix, or when they ship;
how severe the bug is, as a score;
whether it shares a root cause with CVE-2026-109619.
A full advisory is due with the next release. No exploitation of CVE-2026-115560 has been reported so far.
The attacks since 21 October target the two PHP2Shell flaws instead, and they started early. Amazon said its honeypots logged an attempt on 17 October, four days before the flaws were disclosed, and Cloudflare saw attempts climb from tens of thousands in the first 45 minutes after disclosure to millions by early 22 October. Sophos and Arctic Wolf reported attempts to install a Mirai botnet variant. Previdian, Wordfence and others report scanning and probing but no confirmed code execution, and researchers say a reliable exploit is proving harder to build than expected.
What I'd do today
Run the grep first. If nothing in your code or its vendor folder touches serialize_handler, CVE-2026-115560 can't reach you through this path, and your real work this week is still the PHP2Shell update. If something does, switch upload progress off at the pool or virtual-host level today, restart, confirm it with the check script, and then change the code so the handler is set once in the configuration and never switched. One setting closes this bug, but it was never a substitute for patching PHP2Shell.
Frequently asked questions
Is CVE-2026-115560 being exploited?
No exploitation has been reported as of 23 October 2026. The attacks seen since 21 October target the two PHP2Shell flaws, CVE-2026-109618 and CVE-2026-109619.
Does updating to PHP 8.4.27 or 8.5.12 fix CVE-2026-115560?
No. Those releases fix PHP2Shell. PHP will fix CVE-2026-115560 in its next regular bugfix release; until then, set session.upload_progress.enabled=0.
Can I turn off session upload progress with ini_set()?
No. The setting can only be changed per directory or in the main configuration, so ini_set() returns false. Set it in php.ini, the PHP-FPM pool (php_admin_flag) or the Apache virtual host, then restart PHP.
Will disabling session upload progress break file uploads?
No. Uploads keep working. Only progress bars that poll the server for upload status lose their data; bars built on the browser's XMLHttpRequest upload events are unaffected.



