What PHP2Shell is
PHP2Shell is two separate bugs in how the PHP engine cleans up after a request that fails partway through. They were found by researchers at Calif, Wiz Research and OpenAI. Calif first reported one to the PHP project on 11 August 2026 (Sansec's timeline), details leaked on 20 October, and php.net released the fixes on 21 October at 14:00 UTC.
php.net says every PHP installation that serves web requests is at risk: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. It considers the risk highest for mod_php, where it demonstrated an exploit that runs before any application code does. The bugs are in PHP, not in WordPress, Magento or any plugin, so updating the application does not fix them.
| CVE | php.net advisory | What goes wrong | Severity |
|---|---|---|---|
| CVE-2026-109618 | GHSA-w8hw-w9fx-j79w, "RCE via OOM corruption" | When PHP runs out of memory while a request is still being set up, the cleanup can treat half-built internal data as valid. The advisory's examples include multipart/form-data requests built to exhaust memory_limit. | CVSS v4.0 9.2, critical |
| CVE-2026-109619 | GHSA-6wmf-4rv5-p3rp, "RCE via stale global state" | A fatal error can leave a PHP worker holding pointers into memory that a finished request owned, and the next request on that worker uses them. Confirmed through session upload progress, and through fatal_error_backtraces on PHP 8.5. | CVSS v4.0 9.2, critical |
Sansec reports that one of the paths also returns freed worker memory to the visitor inside the PHPSESSID cookie, and that this memory can hold passwords, session tokens, API keys and TLS private keys from other requests, even on a site that never starts a session. That is why the last step on this page, for every kind of hosting, is rotating secrets.
Which PHP versions fix it
php.net supports PHP 8.2 and later. Older branches are fixed only by vendors, and a vendor's fixed build usually keeps the version number it had, so on those branches the version alone does not tell you whether you are patched.
| PHP branch | Fixed in | Where the fix comes from |
|---|---|---|
| 8.5 | 8.5.12 | php.net, 21 October 2026 |
| 8.4 | 8.4.27 | php.net, 21 October 2026 |
| 8.3 | 8.3.36 | php.net, 21 October 2026 |
| 8.2 | 8.2.35 | php.net, 21 October 2026 |
| 8.1, 8.0 and 7.4 | No php.net release | Vendor builds that keep the version number (distributions, Plesk, cPanel, CloudLinux, Ondřej Surý, paid extended support), or the PHP security team's source backports for people who build PHP themselves |
| 7.3 and older | No php.net fix or backport | watchTowr says none is available and to upgrade. CloudLinux has rebuilt its packages back to 5.2, Ondřej Surý's repository covers 5.6, and Plesk points to paid TuxCare support for 5.6, 7.2 and 7.3 |
Is it being exploited? What the sources said by 23 October 2026
- Cloudflare says it saw attack traffic aimed at its customers after the details leaked and before the fix was released, and that its WAF rules were live from 03:02 UTC on 21 October.
- watchTowr says it has observed exploitation in the wild since 21 October through its honeypot network (FAQ updated 22 October).
- CISA had not observed exploitation when it published its alert on 21 October. Neither CVE was in CISA's Known Exploited Vulnerabilities catalogue as of 22 October, according to watchTowr.
- Sansec's update of 22 October: the public proof-of-concept exploits do not work against a default PHP installation, most need the target application to also have a local file inclusion bug, and mass exploitation is less likely than it first expected. It still says to update PHP.
- CISA says that anyone who cannot update immediately, or who still runs PHP 8.1 or earlier, should take PHP-based systems offline until they are patched or updated.
Shared hosting: check which PHP your site runs
In cPanel, open MultiPHP Manager: it lists each of your domains with the PHP version it uses, or "Inherited" if it follows the server default. On hosts running CloudLinux, the cPanel page is Select PHP Version, where you choose from the versions the host allows. Other panels show it in the site's PHP settings.
If the site is on 8.1 or older, move it to 8.2 or newer once you have checked that the site works on it, on a staging copy if the host offers one. php.net supports 8.2 and later and ships every fix for them; anything older depends on the host's vendor rebuilding each fix in turn.
The version number cannot tell you whether you are patched. Hosting panels ship backported builds: CloudLinux's fixed alt-php83 is 8.3.35-6, below php.net's 8.3.36, and Plesk's fixed 8.1, 8.0 and 7.4 keep their old numbers with a new build date. cPanel fixed every EasyApache 4 PHP from 7.4 to 8.5 on 21 October, and CloudLinux fixed its alt-php and ea-php builds back to 5.2, so on a host that has run its updates even an older branch can be patched. Whether it has run them is the first question to ask.
Shared hosting: a temporary .htaccess block
Every known trigger is a multipart/form-data request, the format browsers use to upload files (Sansec). Apache and LiteSpeed read rules from a .htaccess file in the site's folder, and a rewrite rule there refuses those requests before PHP reads them. Add these lines at the top of the .htaccess file in the site's document root (usually public_html):
RewriteEngine On
RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]This is the rule Sansec published for an Apache VirtualHost, placed where a hosting account can reach it. It answers every multipart request with a 403, so file uploads stop working: the WordPress media library, product images, attachments on contact forms. Some forms that upload nothing still submit as multipart, and Sansec notes that some stores send cart and checkout forms that way, so test every form that matters after adding it.
To confirm it is active, try an upload in the site's admin: it should now fail with a 403 Forbidden error. If the upload still works, the host is not applying the rule, and only the host can block these requests.
It is a stopgap, not a fix: it blocks the known way in, not the bugs. It does nothing on a host that runs nginx alone, because nginx ignores .htaccess. Remove it once the host confirms PHP is fixed.
Shared hosting: what php.ini tricks and Cloudflare cannot do
php.net's stopgap for the confirmed CVE-2026-109619 session paths is turning off session.upload_progress, and it says where: in php.ini, a PHP-FPM pool, or server configuration that applies before PHP parses the request. It says not to rely on ini_set() or on a .user.ini file. Those two are the routes a plugin or a file in your own folder takes, so the setting has to be made by the host; on CloudLinux, the host sets it for every alt-php version in /etc/cl.selector/global_php.ini. Some panels let a site owner change PHP options, and whether a given screen applies early enough depends on how the host runs PHP, so ask rather than assume. The memory_limit change php.net suggests for CVE-2026-109618 is in the same position: it belongs where applications cannot override it.
If the site is behind Cloudflare, its WAF has carried PHP2Shell rules since 03:02 UTC on 21 October, on by default on every plan with the WAF, the free plan included. Pro, Business and Enterprise customers should check that the Managed Ruleset is deployed in block mode. Cloudflare also says attackers were changing their payloads to get past the rules, and to patch PHP anyway.
A WAF in front of a site only sees the traffic that goes through it. Anyone who learns the server's own IP address can send requests straight to it, and on shared hosting that address serves every site on the server. Cloudflare helps only if the server accepts web traffic from Cloudflare alone, and on shared hosting that is the host's firewall, not a setting a site owner controls. Treat it as a layer, not as the fix. The same goes for Imunify360 on CloudLinux hosts: CloudLinux says to keep its WAF on with ruleset 8.84 or later, and that this reduces the risk without replacing the update.
Shared hosting: what to ask your host
Send these in one message. A host that has patched can answer all of them in a sentence each.
- "Has the PHP that runs my site been updated for CVE-2026-109618 and CVE-2026-109619, and on what date?"
- "Please confirm it from the package changelog, not the version number." Fixed panel builds often keep their version, so "you are on 8.3.35" does not answer the question.
- "Were the PHP workers restarted after the update?" A worker started before the update keeps running the old code. CloudLinux notes that on LiteSpeed a web server restart does not always stop running lsphp processes.
- "Is every PHP version offered in my panel fixed, including the old ones, and which have no fix?" CloudLinux, for example, has no fixed build of alt-php44 or alt-php51.
- "Is the control panel's own PHP patched?" cPanel and Plesk run their own interfaces on their own PHP, and on cPanel that includes Roundcube webmail. cPanel fixed it in cPanel & WHM 138.0.18, 136.0.50, 134.0.66 and 110.0.153; Plesk in Obsidian 18.0.80 Update 11 and 18.0.81 Update 4.
- "If it is not patched yet, when will it be, and what protects the server until then?" A multipart block, WAF rules and the php.ini settings are all partial. CISA's advice for a system that cannot be patched is to take it offline.
After PHP is patched: rotate what the site holds
Because one path can leak memory from other requests (Sansec), assume anything the site held in memory may have been read. Rotate after the host confirms the fix, not before: a secret changed on an unpatched server can leak the same way. The same applies on a VPS.
- Every admin and editor password for the site, and the hosting control panel, FTP and SFTP passwords.
- The database password, then the copy of it in the site's configuration (wp-config.php on WordPress).
- The keys that sign sessions and cookies: on WordPress, new keys and salts in wp-config.php, which also signs everyone out.
- API keys and tokens stored in the site or its plugins: payment gateways, mail services, storage and analytics.
- The TLS certificate's private key, if you manage it. Where the host issues certificates for you, ask whether it is reissuing them.
- Then look for what an intruder would leave: admin accounts you did not create and PHP files you did not upload. CISA's hunting advice for whoever runs the server is to watch for PHP worker processes launching shells or downloading tools.
On a VPS or dedicated server: update, restart, verify
On a server you run, the fix is the same everywhere: install the fixed PHP from wherever your PHP came from, restart every process that runs PHP (PHP-FPM, Apache if it loads mod_php, LiteSpeed's lsphp), and check the package changelog for the two CVE numbers. The changelog is the proof, because distribution and panel builds backport the fix without taking php.net's version number. One server can carry PHP from several sources at once: a Plesk or cPanel server usually has the panel's builds beside the distribution's, and each needs updating.
| Release | Source package | Fixed in | Advisory |
|---|---|---|---|
| Ubuntu 24.04 LTS | php8.3 | 8.3.6-0ubuntu0.24.04.13 | USN-8948-1, 21 October |
| Ubuntu 22.04 LTS | php8.1 | 8.1.2-1ubuntu2.28 | USN-8948-1, 21 October |
| Debian 12 (bookworm) | php8.2 | 8.2.35-1~deb12u1 | Debian security tracker, 21 October |
| Debian 13 (trixie) | php8.4 | 8.4.27-1~deb13u1 | Debian security tracker, 21 October |
| Rocky Linux 9 | php (8.0, the default stream) | 8.0.30-8.el9_8.1 | RLSA-2026:87151, 22 October |
| Rocky Linux 9 | php:8.2 module stream | 8.2.35-1.module+el9.8.0+40395+2f706e64 | RLSA-2026:87072, 22 October |
| Rocky Linux 9 | php:8.3 module stream | 8.3.36-1.module+el9.8.0+40396+4e680bb7 | RLSA-2026:87079, 22 October |
| AlmaLinux 9 | php:8.3 module stream | 8.3.36-1.module_el9.8.0+330+fdc30075 | ALSA-2026:87079, 21 October |
A release or stream missing from the table had no advisory naming either CVE in OSV on 23 October. That is not the same as unaffected: check its changelog. Ubuntu's numbers sit far below php.net's because Ubuntu backports the fix into the version each release shipped; Debian's carry php.net's own 8.2.35 and 8.4.27.
PHP from the distribution (Ubuntu, Debian, Rocky Linux, AlmaLinux)
- Why
- The php8.x packages on Debian and Ubuntu, or the php packages and module streams on Rocky Linux and AlmaLinux. The fixed versions are in the table above.
- Apply
- Debian and Ubuntu
sudo apt update && sudo apt upgrade sudo systemctl restart php8.3-fpm # your branch; once per installed branch sudo systemctl restart apache2 # only if Apache loads mod_phpRocky Linux and AlmaLinuxsudo dnf update 'php*' sudo systemctl restart php-fpm - Verify
- Debian and Ubuntu
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.3-common/changelog.Debian.gzSansec's check, with your branch in place of 8.3. Both CVE numbers should print.Rocky Linux and AlmaLinuxrpm -q --changelog php-common | grep -E 'CVE-2026-10961[89]'
PHP from Ondřej Surý's repository (deb.sury.org, ppa:ondrej/php)
- Why
- The same package names as Debian and Ubuntu use, built separately. For 8.2 and later it publishes php.net's releases: its 8.2.35, 8.3.36, 8.4.27 and 8.5.12 builds for Ubuntu 24.04 and 22.04 went up on 21 October, beside rebuilt 8.1.34 and 8.0.30 that keep their numbers. Sansec lists its fixed older branches as 5.6, 7.4, 8.0 and 8.1.
- Apply
- Debian and Ubuntu
sudo apt update && sudo apt upgrade sudo systemctl restart php8.3-fpm # your branch; once per installed branch - Verify
- Debian and Ubuntu
php8.3 -v | head -1For 8.2 and later the first line should name 8.2.35, 8.3.36, 8.4.27, 8.5.12 or later. For 8.1 and older, check the changelog as for the distribution's packages.
Plesk
- Why
- Plesk builds its own PHP under /opt/plesk/php. Fixed on 21 October: 8.2.35, 8.3.36, 8.4.27 and 8.5.12, plus the end-of-life 7.4.33, 8.0.30 and 8.1.34 rebuilt that day with the same numbers. The panel's own PHP (sw-engine) is fixed in Plesk Obsidian 18.0.80 Update 11 and 18.0.81 Update 4.
- Apply
- Plesk for Linux
plesk installer updateOr Tools & Settings > Updates > Update Components in the panel. Plesk restarts the service of a component it updates. If apt stops with NO_PUBKEY 5B6AB2160B50DB96 on Ubuntu or Debian, Plesk has a separate article for that error. - Verify
- Plesk for Linux
plesk version /opt/plesk/php/8.3/bin/php -vRun the second line for each installed branch. A supported branch should report its fixed version. 8.1, 8.0 and 7.4 keep their number, so look for a build date of 21 October 2026 or later ("built: Oct 21 2026") in the first line.
cPanel (EasyApache 4) and CloudLinux
- Why
- cPanel's EasyApache 4 25.91 (21 October) fixed ea-php74 through ea-php85, and cPanel & WHM 138.0.18, 136.0.50, 134.0.66 and 110.0.153 fixed the PHP the panel itself runs. On CloudLinux, fixed ea-php and alt-php builds reach back to 5.2 (none for alt-php44 or alt-php51); CloudLinux says an update run before about 01:00 UTC on 22 October found nothing, so run it again. These builds keep a version below php.net's, so check the changelog.
- Apply
- CloudLinux, with CloudLinux's own commands
yum clean all && yum update 'alt-php*' 'ea-php*' -y /scripts/restartsrv_httpd /scripts/restartsrv_apache_php_fpmOn a cPanel server without CloudLinux, update the ea-php packages the same way (yum update 'ea-php*'), and cPanel & WHM itself from WHM. Under LiteSpeed, also run /usr/local/lsws/bin/lswsctrl restart and pkill lsphp: a web server restart does not always stop running lsphp processes. - Verify
- cPanel and CloudLinux
for p in $(rpm -qa 'ea-php*-php-cli' 'alt-php*-common' --qf '%{NAME}\n'); do echo "$p $(rpm -q --changelog $p | grep -cE 'CVE-2026-(109618|109619)')"; doneCloudLinux's check. Every installed package must print 1 or more.
PHP 8.1 and older: a vendor build or an upgrade
php.net no longer releases 8.1 or older. The PHP security team published minimal source backports for 7.4, 8.0 and 8.1 for people who build PHP themselves, and several vendors have rebuilt their packages. Otherwise the answer is to move to 8.2 or newer.
- Ubuntu 22.04: its own php8.1 is fixed in 8.1.2-1ubuntu2.28 (USN-8948-1).
- Ondřej Surý's repository: 5.6, 7.4, 8.0 and 8.1 (Sansec).
- Remi's repository: 7.4, 8.0 and 8.1 according to Sansec on 22 October. php.net had warned on 21 October that it would have no updates that day and urged its users to switch to distribution packages or build from source.
- Plesk: 7.4.33, 8.0.30 and 8.1.34 rebuilt on 21 October. For 5.6, 7.2 and 7.3, Plesk recommends paid TuxCare Extended Lifecycle Support.
- cPanel: ea-php74, ea-php80 and ea-php81 carry the backport (EasyApache 4 25.91, with 25.92 adding it to their change logs). CloudLinux: alt-php and ea-php builds for every branch from 5.2, except alt-php44 and alt-php51.
- Extended support, per Sansec's list: TuxCare ELS for PHP, ZendPHP LTS and Freexian ELTS for Debian 9 to 11 (all paid), and Ubuntu Pro for Ubuntu 14.04 to 20.04.
- The official Docker php image gets no fix for 8.1 or older (Sansec).
- If none of these applies and the server cannot be upgraded, CISA's advice is to take it offline until it is.
If you run the server and cannot update today
These are partial. php.net says only an update is a complete solution, and that the measures below reduce exposure to specific attack paths without making an installation safe. On shared hosting they are the host's to apply.
- Block multipart/form-data at the web server (Sansec). In nginx, in every server block that passes requests to PHP: if ($http_content_type ~* "multipart/form-data") { return 403; }, then nginx -t && systemctl reload nginx. In Apache, the three rewrite lines from the .htaccess section, in every VirtualHost that serves PHP. Uploads stop working until you remove it.
- Turn off session upload progress, which blocks the confirmed CVE-2026-109619 session paths only (php.net): session.upload_progress.enabled = 0 in php.ini for mod_php, or the pool lines below for PHP-FPM, then restart every worker. Not with ini_set() and not with .user.ini.
- Remove the controllable memory limit (php.net, for CVE-2026-109618): memory_limit = -1, set where applications cannot override it, and check that no pool, directory or application puts a finite limit back. One request can then use all of the server's memory, so watch it and restore the limit after updating (Sansec).
- Keep post_max_size low: 8M where the application takes no uploads, 32M where it does, with upload_max_filesize below it (php.net).
- On PHP 8.5, set fatal_error_backtraces = 0 and restart every worker. This blocks the confirmed backtrace path only (php.net).
- Do not cap memory with RLIMIT_AS or RLIMIT_DATA: php.net says they can cause the very allocation failure the bug needs.
; every PHP-FPM pool, for example /etc/php/8.3/fpm/pool.d/www.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[memory_limit] = -1Then reload the pool (systemctl reload php8.3-fpm, with your branch) and confirm the values from a request that PHP-FPM serves, not from php -i on the command line, which does not read the pool's settings (Sansec).
What SecAI does and does not do about PHP2Shell
SecAI is a Linux server security agent, so it can only help on a server where you have root. This is exactly what it covers for these two CVEs today.
- It matches installed packages against OSV.dev every twelve hours on Ubuntu, Debian, Alpine, Rocky Linux and AlmaLinux, and keeps each OSV answer for up to 24 hours, so a new advisory can take a day and a half to become a finding. The distributions' advisories for both CVEs were in OSV by 22 October for Ubuntu 22.04 and 24.04, Debian 12 and 13, Rocky Linux 9 and AlmaLinux 9. A SecAI finding for either CVE had not yet been confirmed on a real server when this page was written, so do not wait for one: use the changelog checks above.
- It does not check the PHP that Plesk, cPanel or CloudLinux install (the plesk-php, ea-php and alt-php packages). A server whose sites run on panel PHP gets no PHP2Shell finding from SecAI, patched or not. Use the changelog checks above.
- A build from Ondřej Surý's repository uses the distribution's package names, and SecAI compares it against the distribution's own fixed version, which says nothing about whether that build is fixed. Check Surý builds by their version, as above.
- It does not update PHP, restart PHP workers or change PHP settings. Its install action only installs a few security tools, such as fail2ban.
- It cannot help on shared hosting: the agent needs root on the server, which a hosting account does not have.
- After the fact, it reports a PHP file in a web root that matches a web-shell signature or was recently modified, and a process running from /tmp, /var/tmp or /dev/shm, each with its evidence. It has no detection for the signal CISA names, a PHP worker process launching a shell.
Every detection SecAI makesPHP advisories per distributionWeb shell detection