Engineering / /

PHP security update: patch php2shell before it finds you

PHP security update: on Oct 21, 2026, the PHP team shipped emergency releases that fix two critical flaws. Either flaw lets an attacker run code on a web server without logging in. If your site runs PHP, this post tells you which version you need, what to do if you cannot patch today, and which deadline follows next.

TL;DR

  • Oct 21 releases fix two unauthenticated remote code execution flaws, both rated CVSS v4.0 9.2.
  • Patch to 8.5.12, 8.4.27, 8.3.36, or 8.2.35 for your branch.
  • Mod_php carries the highest risk, with a shown exploit that runs before app code.
  • PHP 8.2 security ends Dec 31, 2026, so a patched 8.2 still needs a move soon.
  • A separate Oct 23 advisory asks some setups to disable session upload progress until the next bugfix release.

What happened on Oct 21

The PHP team gave advance notice on Oct 20 of a critical security release set for Oct 21 at 14:00 UTC, in the PHP team's Oct 21 advisory. The release fixed two flaws in PHP core, and researchers call the pair php2shell. Both flaws allow unauthenticated remote code execution, which means an attacker needs no account, no form submission, and no valid session to try them.

This is a runtime flaw, not a plugin flaw. It sits in the engine that runs your code, so it affects WordPress sites, Laravel apps, Drupal sites, and plain PHP scripts in the same way. Our web development team treats runtime patches as a different class of work from feature upgrades, because the fix has to land before anything else you planned that week.

The fixed releases landed on Oct 21: PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35. PHP 8.6.0 RC4, released the same day, also contains the fixes. Snyk's timeline of the release day matches the PHP team's account, including the 14:00 UTC release time. The PHP team's advice is blunt: "There are no complete mitigations, updating is the only safe choice." That sentence sets the tone for everything below. Mitigations can shrink the risk for a day. Only the patch closes it.

The two flaws in plain terms

The first flaw is CVE-2026-109618, which the PHP team calls RCE via OOM corruption. Out-of-memory handling can interrupt PHP while internal request data is only partly built. Request cleanup may then treat the invalid data as valid, and an attacker can steer that cleanup toward code execution.

The second flaw is CVE-2026-109619, which the PHP team calls RCE via stale global state. Request-owned internal state can survive after its backing memory has been freed. A later request handled by the same worker may use that stale state before normal setup resets it.

You do not need to follow every step of either flaw to act on it. The point that matters for a site owner is the trigger point. In the mod_php case, the PHP team says an unauthenticated request can trigger the attack before any application code runs. Your login checks, your firewall rules inside the app, and your input validation never get a chance to run, because the flaw fires earlier in the request.

That is why the usual advice to "check your plugins" misses this one. Plugins run after the engine starts. These flaws live in the engine itself. The fix has to come from the same place: a patched engine.

Who is exposed, and why mod_php tops the list

The PHP advisory says all PHP installations serving web requests are at risk. That list covers PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed. If PHP answers HTTP requests on your server, assume it is in scope until you prove the version is patched.

Mod_php carries the highest risk in the PHP team's own words, because of the shown exploit that runs before app code. PHP-FPM and the other setups are still in scope. A lower risk rating is not a safe rating. It only changes the order in which you patch when you have more than one server to fix.

Exposure also hides in places teams forget to count. A staging server that mirrors production, an old marketing microsite on the same host, a client portal nobody opened in months, and a vendor app running on your stack all count. Patching the main site while a forgotten PHP install answers requests on the same server leaves the hole open. The audit step below exists for that reason.

One more trap is the version number itself. A distribution package can carry a backported fix while the version string still shows an older number, and a patched build can sit next to an unpatched one on the same host. Check the package changelog or the vendor advisory for your host, not just the number on the screen. If your host or vendor has not published a patched package yet, treat that server as unpatched and move to the mitigations below while you wait.

The patch list: which version you need

The rule is simple: stay on your branch and move to its fixed release. Do not jump branches under pressure unless you already planned the jump.

  • PHP 8.5 moves to 8.5.12.
  • PHP 8.4 moves to 8.4.27.
  • PHP 8.3 moves to 8.3.36.
  • PHP 8.2 moves to 8.2.35.

Linux distributions and long-term support vendors received the patches in advance, so check your vendor for updated packages first. On a managed host, the version selector may lag the release by days. Ask the host directly which build it ships and when the patched build lands, and keep the ticket open until you see the fixed version running.

PHP 8.1 and older get no new official release from the PHP project. The PHP team did publish minimal source backports for PHP 7.4, 8.0, and 8.1, to help people building from source or without a long-term support vendor. That backport is a bridge, not a plan. If you run 8.1 or older in production, this patch week is also a migration decision, and the Dec 31 clock below makes that decision urgent for 8.2 as well.

After you patch, restart every worker. A patched binary that never loads changes nothing. Verify with php -v on each server, then check the running workers, not just the installed package. On PHP-FPM, that means restarting the pools. On mod_php, that means restarting Apache. A patch that sits on disk while old workers serve traffic is a patch in name only.

Two broken chain links in glowing violet over a dark server rack, one dissolving and one frozen mid-crack, with the two CVE numbers labeled

If you cannot patch today: mitigations that help, and their limits

Some teams cannot patch the same day. A host may not have the package yet, a change freeze may block deploys, or a legacy app may need a test pass first. The PHP advisory lists measures that reduce exposure to specific attack paths. None of them makes an install safe, and the PHP team says so plainly.

For the out-of-memory flaw, the advisory suggests setting memory_limit to -1 in a way apps cannot override, such as php_admin_value in the FPM pool or Apache config. It also suggests a low post_max_size, 8M for apps without file uploads and 32M where uploads are needed, with upload_max_filesize kept below post_max_size. Enforce limits at the web server or proxy level too, for decoded request size, input field count, multipart part count, header count, and nesting depth.

For the stale-state flaw, the advisory suggests setting session.upload_progress.enabled to 0 and restarting every worker, using php.ini, an FPM pool, or server config that applies before request parsing. Do not rely on ini_set or FPM user.ini files for this one. On PHP 8.5, it also suggests setting fatal_error_backtraces to 0 and restarting every worker.

Read those mitigations as a holding pattern with an expiry date. Each one blocks a confirmed path and leaves other variants open. If you apply them, write down the date you applied them, the servers they cover, and the date you will remove them by patching. A mitigation with no expiry date tends to become permanent, and permanent mitigations are how teams forget they are still exposed.

The Oct 23 follow-up you should not mix up with the patch

Two days after the patch, on Oct 23, the PHP team published a separate informational advisory about session upload progress. Keep it apart from the Oct 21 patch in your head, because it has a different status.

The Oct 23 advisory suggests setting session.upload_progress.enabled to 0 until the next regular bugfix release, to fully protect from an unlikely remote code execution vector. It will get a full advisory as CVE-2026-115560 with the next release. As of this post, no new bugfix release has landed since Oct 23, so that advice still stands.

The catch is narrow. It can only occur under an unusual mix of config and app code: the app sets session.serialize_handler to php_binary or php_serialize in config, then switches it to php at runtime with ini_set before calling session_start, with session upload progress still enabled. Apps that never change the serialize handler at runtime can keep the feature enabled.

If you already set session.upload_progress.enabled to 0 as a mitigation on Oct 21, keep it in place and no further action is needed for this advisory. If you are unsure whether your apps switch serialize handlers at runtime, disabling upload progress is the safe default until the next bugfix release. Either way, do not confuse this advisory with a second patch. The Oct 21 releases are the patch. The Oct 23 note is a warning about a separate, unlikely vector that the regular release cycle will handle.

The Dec 31 clock: PHP 8.2 end of life

Patching to 8.2.35 buys time, not a future. PHP 8.2 reaches end of life on Dec 31, 2026, and gets no further security fixes after that date. A site patched to 8.2.35 today is safe from these two flaws and unprotected against the next flaw found in 8.2 after New Year.

The sane target this quarter is 8.3 or 8.4 for production. Our PHP 8.6 release guide covered the full checklist for that move, including the session defaults that can break logins on the way up. Add 8.6 to CI now if you want, but do not make 8.6 your production target on day one. Let it get its first patch release first.

Teams in real trouble are not the ones behind one version. They are the ones who will discover on Jan 2 that their last upgrade plan was written for a version nobody supports. If your estate still runs 8.2, put the 8.3 or 8.4 move on the calendar this month, with a named owner and a staging date. The patch week just showed what happens when a deadline arrives without a plan: everyone patches at once, hosts queue up, and the teams with a tested path move first.

A patch-day checklist that fits a real team

Patch weeks fail on process, not on effort. The checklist below is the shape we use on client builds, compressed for a single patch. It follows the same four steps we use on every project: Discovery and Architecture, Design and Prototyping, Engineering and Build, Deployment and Scaling.

First, inventory every PHP runtime. List each server, each container image, and each host, with its branch and its running version. Include staging, old microsites, and vendor apps. You cannot patch a runtime you did not list.

Second, patch in risk order. Mod_php first, then internet-facing PHP-FPM and LiteSpeed setups, then internal apps. Restart every worker after each patch and verify the running version, not just the installed package.

Third, apply the holding mitigations only where patching waits on a vendor, and write down their expiry date. A mitigation without a patch date is a risk you accepted in silence.

Fourth, test the paths that matter. Logins, file uploads, sessions, and payment callbacks are the flows a runtime change touches first. The Ecovers site and automation system we rebuilt is a good example of why this step matters: the first response and the follow-up sequence only work if sessions and uploads behave after the change, so we test those flows by hand before we call a patch done.

Fifth, schedule the branch move. If you patched 8.2, book the 8.3 or 8.4 move now. If you patched 8.1 through a backport or a vendor package, book the supported-branch move now. The next advisory will not wait for your roadmap.

Patch day is also a good day to fix the habit that made it hard. Put PHP version checks in your monthly maintenance pass, with the support end dates for each branch on the same page. A team that checks versions monthly never learns about a critical patch from a headline. It learns about it from its own list, with a plan already in place.

Ascending staircase of glowing violet server blocks with a small figure walking up toward light, labeled with the four patched PHP versions

Still on a PHP version from before Oct 21?

We audit PHP apps, find every runtime in the estate, patch to the fixed release, and move production to a supported branch without breaking logins, uploads, or checkout. See how we handle PHP upgrades and maintenance

Frequently asked questions

Which PHP versions fix the two flaws? PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35, all released Oct 21, 2026. PHP 8.6.0 RC4 also contains the fixes, but release candidates are test builds and do not belong in production. Stay on your branch and move to its fixed release, then restart every worker and verify the running version.

Am I affected if I run PHP-FPM, not mod_php? Yes. The PHP advisory lists PHP-FPM, mod_php, php-cgi, FrankenPHP, and LiteSpeed as at risk. Mod_php carries the highest risk because of a shown exploit that runs before app code, but PHP-FPM is still in scope. Patch in risk order, with mod_php first, and do not treat a non-mod_php setup as safe.

What if my host has not published the patched version yet? Treat the server as unpatched until the host confirms a patched build. Apply the holding mitigations from the PHP advisory, such as disabling session upload progress and setting a low post_max_size, and restart every worker. Keep the host ticket open, write down the date you applied the mitigations, and patch the day the package lands.

Does disabling session upload progress fix the flaws? No. It blocks a confirmed path for the stale-state flaw, but not other variants, and it does not fix the out-of-memory flaw at all. The PHP team says only an update is a complete solution. Use the setting as a holding measure while you wait to patch, not as a substitute for patching.

What is CVE-2026-115560 and the Oct 23 advisory? It is a separate, unlikely vector that the PHP team flagged on Oct 23, 2026. It needs an unusual mix: an app that sets session.serialize_handler to php_binary or php_serialize in config, then switches it to php at runtime before session_start, with upload progress enabled. The advice is to set session.upload_progress.enabled to 0 until the next regular bugfix release, when a full advisory will follow.

What should I do if I am still on PHP 8.2? Patch to 8.2.35 now, then plan the move to 8.3 or 8.4 this quarter. PHP 8.2 security support ends Dec 31, 2026, so 8.2.35 protects you from these two flaws but not from the next flaw found after that date. Book the branch move with a named owner and a staging date, instead of letting the deadline arrive unplanned.

Should I wait for PHP 8.6 instead of patching now? No. Patch your current branch this week, and test 8.6 in CI in parallel. PHP 8.6 general availability is set for Nov 19, 2026, and a new branch still needs its first patch release before it is a sensible production target. Patching now and testing 8.6 at the same time costs little, while waiting leaves a known critical flaw open for weeks.

More from Buzzin Tech Hub

Have a project in mind?

Tell us what you're working on and we'll tell you honestly what it'll take to build it right.

Start Your Project →
VOLT // QUICK SCOPE Need a 60-second stack or growth audit?