WHM - Plesk - CWP, PHP, WordPress

PHP2Shell: How to Patch PHP on Plesk and cPanel

On October 21, 2026, the PHP team shipped emergency fixes for two critical bugs in PHP itself: CVE-2026-109618 and CVE-2026-109619, nicknamed PHP2Shell. Both are rated 9.2 (CVSS v4.0), both allow remote code execution without a login, and attacks started days before the patch was public. If you run a server with Plesk, cPanel or plain PHP-FPM, this one is worth dropping everything for.

Here is the short, practical version: how to check what you are running, how to patch it, what to do if you can’t patch today, and how to look for signs that someone got in first.

Why updating WordPress is not enough

The bugs live in the PHP engine, in the code that handles a request before WordPress, WooCommerce or any plugin even starts. A fully updated WordPress site on an old PHP build is still exposed. The fix has to happen at the PHP level on the server.

The vulnerable code goes back to PHP 5.4, so practically every version you are likely to meet is affected. Only PHP running on the command line (cron scripts, Composer) is considered low risk. Anything serving web requests is not.

Which PHP versions fix it

PHP branchFixed in
8.58.5.12
8.48.4.27
8.38.3.36
8.28.2.35
8.1 and olderNo official release

If you are on PHP 8.1, 8.0, 7.4 or older, the PHP project will not release an official build. For those sites the real fix is moving to PHP 8.2 or newer, or using a vendor backport if your control panel offers one. cPanel’s EasyApache 4 already backports the fix to ea-php74, ea-php80 and ea-php81. Plesk’s fixes for PHP 7.4 to 8.1 were still in development when I wrote this.

Also keep in mind that distributions often patch PHP without changing the version number. Ubuntu 24.04’s fixed package still says 8.3.6, for example. If your version looks old, check the package changelog before panicking.

Step 1: Find every PHP your server uses

On a Plesk server there is rarely just one PHP. You usually have the system PHP plus several Plesk-managed versions, and each domain can use a different one. The command line PHP may not be the one serving your sites, so check all of them.

# system PHP
php -v

# Plesk-managed PHP versions
ls /opt/plesk/php/
/opt/plesk/php/8.3/bin/php -v

# list handlers known to Plesk
plesk bin php_handler --list

# is Apache loading mod_php?
apachectl -M | grep -i php

To see what live web requests actually use, drop a small test file into a site, open it once in the browser, then delete it right away:

<?php
echo PHP_SAPI, " ", PHP_VERSION, "\n";
echo "upload_progress=", var_export(ini_get("session.upload_progress.enabled"), true), "\n";
echo "memory_limit=", ini_get("memory_limit"), "\n";
echo "post_max_size=", ini_get("post_max_size"), "\n";

Do not leave a public phpinfo() page lying around for this. Attackers love those.

Step 2: Take a backup, then patch

An emergency patch is still a change on a production server. Take a snapshot or at least a database and file backup first. If you are on cPanel, my MySQL backup and restore guide covers the database part.

Plesk on Linux

Plesk shipped patched PHP 8.2 to 8.5 builds on the day of the release. The official instructions are in the Plesk support article. In the panel, go to Tools & Settings, then Updates and Upgrades, and install the available updates. From the shell, the installer does the same job:

plesk installer --select-release-current --upgrade-installed-components

Plesk’s PHP-FPM services are named like plesk-php83-fpm. List yours and restart them:

systemctl list-units | grep -i fpm
systemctl restart plesk-php83-fpm

If you also use the PHP that comes from your operating system, update that too (apt update && apt upgrade on Debian and Ubuntu, dnf upgrade on RHEL-based systems) and restart php8.3-fpm or its equivalent.

cPanel / WHM

EasyApache 4 version 25.91 includes the fixed builds (see the EasyApache 4 changelog). Update it from the shell:

dnf update ea-* -y        # or: yum update ea-* -y
/scripts/restartsrv_httpd
systemctl restart ea-php83-php-fpm

If you want a calmer look at what cPanel gives you and what it hides, I wrote about it in Beyond cPanel: Taking Full Control of Your Hosting Stack.

Do not skip the restart

This is the step people forget. A PHP-FPM worker or Apache process that started before the update keeps running the old, vulnerable code. Restart PHP-FPM, Apache or nginx, and any long-running PHP workers such as queue processes. Then run the version check again and confirm the numbers.

Shared hosting and managed servers

If you are on shared hosting, you cannot patch anything yourself. Ask your provider whether PHP has been updated, and do not accept “we turned off one setting” as an answer. That setting only covers one of the two bugs (see below). Docker users should pull the new official image tags and rebuild. Old images and the RC1 tags do not contain the fix.

If you can’t update today

Sometimes a patch has to wait: a legacy app that breaks on a newer PHP, a package that has not reached your repository yet. PHP’s own advisory is clear that there is no complete workaround, only measures that block the known attack paths. Treat the following as a bridge, not a fix.

Set these in the server configuration, not in the application:

# PHP-FPM pool file (for example /etc/php/8.3/fpm/pool.d/www.conf)
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M

# Apache with mod_php
php_admin_flag session.upload_progress.enabled off
php_admin_value memory_limit -1
php_admin_value post_max_size 8M

# php.ini
session.upload_progress.enabled = 0
memory_limit = -1
post_max_size = 8M
  • If your site accepts uploads, use post_max_size = 32M and keep upload_max_filesize below it.
  • On PHP 8.5, also turn fatal_error_backtraces off the same way.
  • Setting these with ini_set() or a .user.ini file does not work. Then restart every PHP process.
  • Yes, memory_limit = -1 looks reckless, and it is, because one request can now eat the whole server’s memory. Cap memory with a container or cgroup limit instead, and avoid RLIMIT_AS or RLIMIT_DATA, which PHP says can trigger the flaw.

On Plesk, the per-domain place for these is the PHP Settings page of each subscription, where additional directives can be added. Plesk’s own memory limit field can override pool values, so verify the result with the test file from Step 1.

Block uploads at the web server (only if you do not need them)

The known attacks arrive as multipart form requests, the format browsers use for file uploads. If a site does not accept uploads at all, researchers at Sansec suggest rejecting them. This is not part of PHP’s own advice, and it will break media uploads, contact forms with attachments and some checkout pages, so it is not suitable for most WordPress sites.

# nginx, inside the server block
if ($http_content_type ~* "multipart/form-data") { return 403; }

# Apache, inside the <Directory> block of your web root (needs mod_rewrite)
RewriteEngine On
RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]

Send a test upload afterwards to make sure the rule is really doing something. Placed in the global Apache config instead of a Directory block, it can be silently ignored.

A firewall buys time, nothing more

Cloudflare switched on managed rules for all plans, including Free, on October 21, and Akamai and Fastly have rules for these CVEs too. That is useful while you patch, but it only covers attacks that are already known and only traffic passing through the provider. If your origin server can be reached directly, the rules do nothing. Cloudflare itself reports attackers already changing payloads to get around the rules. Put the firewall in front, then patch anyway.

Check whether you were hit

The attacks were seen in the wild starting October 17, four days before the public disclosure (Amazon’s threat intelligence team recorded the first attempt), so start your log review there, not on October 21. Because the bugs trigger inside PHP before your application runs, normal application logs often show nothing.

One trace that CrowdStrike points to is an out-of-memory error reported with no PHP file attached. Search for it in your error logs and adjust the paths for your server:

grep -E "Allowed memory size of [0-9]+ bytes exhausted .* in Unknown on line 0" \
  /var/log/apache2/error.log /var/log/php*-fpm.log

A match is a reason to investigate, not proof of a break-in. Then look for the usual signs of a compromised web server:

  • New PHP files in upload, cache or temp folders. On WordPress, find wp-content/uploads -name "*.php" -mtime -10 is a quick start. There should normally be none.
  • Unknown administrator accounts (wp user list --role=administrator with WP-CLI) and new cron jobs.
  • Web server processes starting shells or tools such as sh, curl or wget, or making outbound connections to hosts you do not recognise.
  • Unusually large or garbled PHPSESSID cookies in responses, which can mean server memory is leaking.

One of the two bugs can leak server memory, which may contain passwords, session tokens and API keys. If a server was exposed before it was patched, rotating database passwords, API keys and WordPress salts afterwards is cheap insurance.

What the two bugs actually are

CVE-2026-109618 is an out-of-memory problem. A crafted request can make PHP run out of memory while it is still setting up the request, and the cleanup code then trusts data that was never properly built. The GitHub advisory confirms code execution on Apache with mod_php.

CVE-2026-109619 is a stale-state problem. When a request ends abruptly, PHP can leave behind references to memory it already freed, and the next request on the same worker uses them. The confirmed paths include session upload progress and, on PHP 8.5, fatal error backtraces. The GitHub advisory confirms it on both mod_php and PHP-FPM.

This is why the advice you may have read, “just turn off session.upload_progress”, is not enough. That setting only addresses one of the two bugs, and PHP’s own advisory says only the update is a complete solution.

A quick checklist

  1. List every PHP version that serves web traffic on your servers and containers.
  2. Back up, then update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (or the vendor’s patched package).
  3. Restart PHP-FPM, Apache or nginx and any PHP workers, then re-check the version.
  4. Move any site still on PHP 8.1 or older to a supported version.
  5. If you cannot patch yet, apply the temporary settings, keep a firewall in front, and patch as soon as a package arrives.
  6. Review logs back to October 17 and rotate secrets on anything that was exposed.

Keep an eye on PHP’s announcements for the next few days. An unconfirmed claim circulated on the oss-security list that the 8.5.12 fix may be incomplete, and PHP has not commented on it publicly. If a follow-up release appears, update again.

Official sources

Last updated: October 23, 2026. Details are still changing, so check the php.net advisory for the latest.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top