On 21 October 2026, the PHP team announced two serious security bugs in PHP itself. They are CVE-2026-109618 and CVE-2026-109619, together nicknamed PHP2Shell. Both score 9.2 out of 10. An attacker on the internet may be able to run code on your server without logging in. Your own code can be perfectly safe and you can still be hit, because the bugs are inside PHP.
Attacks have already started. This guide explains what the bugs are, how to check whether you’re affected, and what to do for every PHP version from 5.6 to the newest.
Do this now
- Find out which PHP version your website really runs (see Step 1).
- Update PHP to 8.2.35, 8.3.36, 8.4.27 or 8.5.12, or newer, and restart it. On 8.1 or older, see the options in Step 2.
- Can’t update today? Set the stop-gap settings in this section, and keep old servers off the open internet.
- Check your logs for odd multipart requests, repeated 500 errors and PHP workers restarting for no reason.
- Change your secrets (database passwords, API keys, session secrets) if the server was exposed.
The short version
- Affected: nearly every PHP version in use. The vulnerable code goes back to PHP 5.4.0. The official advisories list the supported branches, 8.2 to 8.5.
- Fixed in: PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12.
- No official fix for PHP 8.1 and older. For 7.4, 8.0 and 8.1, the PHP team only published small source patches. Some vendors that sell extended support may have their own fix.
- Attacks have started. Cloudflare saw attack traffic before the official release, and watchTowr reported exploitation attempts on release day. Exploit tools and write-ups are public.
- What to do: update PHP, restart it, then check the version your website really runs.
- If you can’t update today: use the stop-gap settings further down. They lower the risk, but they don’t remove it.
Who found it, and what happened
Several groups worked on this, and each found or reported different parts of it:
- Calif found multiple ways to run code starting on 1 August 2026, and reported the first bug to PHP on 11 August. Calif also built PHP2Shell, an exploit tool that chains the two bugs into one attack that needs no login. It showed this working against WordPress and Magento, and against Drupal, Joomla, MediaWiki, Roundcube, phpMyAdmin, Nextcloud, Laravel, Symfony and WHMCS.
- Wiz Research found the bugs on its own. By its own timeline, it found the first one on 19 August and had working attacks against Drupal and WordPress by 21 August. It reported to the PHP security team on 24 August and published a detailed write-up.
- OpenAI worked on this through its Patch the Planet program, and its Codex Security team is credited with finding the flaws. It says its models helped, and that it led a group of maintainers, researchers and security companies that prepared fixes and protections before disclosure. It also released a version checker, covered below.
- Google says its Gemini models helped find the bugs and test the fixes, and published advice for people running PHP on Google Cloud.
- The PHP security team wrote and released the fixes, working with Linux vendors who received the patches before the announcement.
Wiz says the bugs had been in PHP for more than ten years. Researchers say AI tools made it much faster to turn them into working exploits.
The plan to announce everything together did not hold. Details leaked on 20 October, and PHP moved the release forward to 21 October. Cloudflare switched on its default protection at 03:02 UTC that day and reported attack traffic before the release. watchTowr reported active exploitation attempts of both bugs after the 14:00 UTC release. At the time of writing, no successful break-ins had been publicly reported, and Sansec expects mass exploitation now that the fixes and write-ups are public.
How the bugs work
Picture a PHP worker as a shop assistant who serves one customer at a time. After each customer, the assistant clears the desk and throws away the notes. In these bugs, a request ends badly, and the assistant leaves some notes on the desk. The notes point to things that were already thrown away.
The next customer’s request is handled by the same worker, and it uses those old notes. If an attacker controls what is sitting in that memory, PHP can be tricked into treating the attacker’s data as its own. That can end with the attacker’s code running on your server.
The two CVEs are two different ways of leaving notes behind.
CVE-2026-109618: “RCE via OOM corruption”
When PHP runs out of memory, it stops what it is doing and jumps out. If this happens very early, before the request is fully set up, the cleanup step later reads half-built data and treats it as valid.
An attacker can cause this without going over your normal limits. A multipart upload can carry a huge number of headers, or many empty file fields with nested names. These take up far more memory inside PHP than they do on the network. So a request that looks small can still use up the whole memory limit.
With Apache’s mod_php, this can happen before any of your code runs. The PHP team confirmed a remote attack over HTTP, using the default input and memory limits.
CVE-2026-109619: “RCE via stale global state”
Here, a request ends badly (a fatal error, a timeout or running out of memory) and leaves behind information that points to memory that has been freed. A later request on the same worker reads that old information before PHP resets it.
The PHP team confirmed two ways this can happen:
- PHP 8.5 fatal error backtraces. When
fatal_error_backtracesis on, PHP doesn’t reset the stored backtrace properly after a fatal error. - Session upload progress. If PHP runs out of memory while reading a request, session data can be left pointing at freed memory. This feature is on by default, and your app doesn’t need to call
session_start()for it to matter.
Wiz splits the attacks into four parts, and groups them a little differently from the PHP advisories: a use-after-free in upload progress, a leak of worker memory through the PHPSESSID cookie, a use-after-free in fatal error backtraces, and a type mix-up in OPcache. By combining the leak with the memory damage, Wiz got code running without a login. You don’t need to follow every detail. A patched PHP closes all of them.
How serious is it?
The score is 9.2 out of 10 on both CVEs because anyone on the internet can try the attack, no account is needed, and the result can be a full takeover of the web server’s account.
The attacker can also read the live memory of a PHP worker without running code first. That memory can hold passwords, tokens, API keys and certificates from other requests.
The score also says the attack is hard to pull off (“high complexity”). Triggering the bug is easier than turning it into working code, and the memory layout depends on your PHP build. More workers lower the odds on each try, but they don’t make the problem go away. With public exploit tools around, don’t rely on the difficulty to keep you safe.
Which servers are affected
Any setup that serves web requests from PHP is in scope: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The risk differs:
- Apache with mod_php: the highest risk. The PHP team showed an attack that runs before any application code starts.
- PHP-FPM: the session upload progress attack has been confirmed against it. The out-of-memory bug is harder to reach, because the worker exits when startup fails, and nobody has shown a complete attack yet. FPM can also be reached when the web server passes
PHP_VALUEorPHP_ADMIN_VALUEsettings that cause a warning very early. Pool settings and.user.inidon’t have that problem. - Nginx: nginx doesn’t run PHP itself. It hands requests to PHP-FPM, so a site on nginx with PHP-FPM is affected through PHP-FPM, as described above. The fix is to update PHP, not nginx. Check your nginx config for
fastcgi_param PHP_VALUEorPHP_ADMIN_VALUElines, because those settings are one way to reach the bug. Nginx’sclient_max_body_sizeis also one of the stop-gap limits below. - FrankenPHP: likely open to the out-of-memory bug, because it runs the end-of-request cleanup even when startup fails. This is based on reading the code, not a full test.
- LiteSpeed (LSAPI): works much like PHP-FPM. Attacks on specific apps are likely, but none has been confirmed. “Not confirmed yet” doesn’t mean safe.
- Docker and cloud platforms: if you build your own image, rebuilding it is your job. Google Cloud says its own systems aren’t affected, but PHP apps running on it might be.
The software on top doesn’t matter. WordPress, Drupal, Joomla, Magento, Shopware and WooCommerce sites are exposed through PHP itself, however clean their own code is.
Step 1: find out which PHP version you really run
The PHP you use on the command line and the PHP behind your website can be two different programs. Check the one that serves the site.
# command line only
php -v
# PHP-FPM (the name may include the version, e.g. php-fpm8.3)
php-fpm -v
# shows whether mod_php is loaded in Apache
apachectl -M | grep -i php
On shared hosting or a managed platform, look in the control panel or ask your host.
A quick check from outside
OpenAI released a small open-source tool called php2shell-checker. It reads the PHP version your site shows in its HTTP headers, or from php -v on a server, and answers OK, VULNERABLE or UNKNOWN. To run it, use node phpcheck.js "https://your-site.com", or node phpcheck.js --local on the server.
It only compares version numbers. It can’t see vendor backports, and many servers hide the version. UNKNOWN doesn’t mean you’re patched, so use it as a first look only.
If your Linux vendor patches PHP for you
On Debian, Ubuntu, Red Hat and similar systems, a package can be patched even when its version number looks old. Check the changelog:
# Debian / Ubuntu
apt changelog php8.3-fpm | grep -i "CVE-2026-1096"
# Red Hat family
rpm -q --changelog php-fpm | grep -i "CVE-2026-1096"
If you see both CVE numbers, the package has the fix. If you don’t, it isn’t patched yet.
Step 2: apply the fix for your version
| Your PHP | What to do |
|---|---|
| 8.5 | Update to 8.5.12 or newer. |
| 8.4 | Update to 8.4.27 or newer. |
| 8.3 | Update to 8.3.36 or newer. |
| 8.2 | Update to 8.2.35 or newer, and plan an upgrade. 8.2 gets its last security fix on 31 December 2026. |
| 8.1, 8.0, 7.4 | No official release. Upgrade, use a vendor backport, or build the PHP team’s backport (details below). |
| 7.3, 7.2, 7.1, 7.0, 5.6 and older | No fix from the PHP project, and no backport branch. Upgrade, buy extended support, or keep the server off the open internet. |
PHP 8.2 to 8.5: update PHP
On a normal Linux server, update PHP from your package source, then restart it:
# Debian / Ubuntu (change the version number)
sudo apt update && sudo apt install --only-upgrade 'php8.3*'
# Red Hat / Alma / Rocky / Fedora
sudo dnf update 'php*'
# then restart PHP (or: sudo systemctl restart apache2)
sudo systemctl restart php8.3-fpm
New packages can take a little while to reach your repository after 21 October. If you still see the old version, wait a bit or ask your vendor. Many Linux vendors got the patches before the announcement.
For Docker, pull the newest PHP image for your branch and rebuild your own image. On shared hosting, ask your host to confirm in writing that the update is done.
PHP 8.1, 8.0 and 7.4: three options
-
Upgrade (best). Move to 8.3, 8.4 or 8.5. It’s also the only way to keep getting security fixes.
-
Use a vendor backport. Some Linux distributions and long-term support vendors patch old PHP themselves. Ask whether their package fixes CVE-2026-109618 and CVE-2026-109619, and check the changelog as shown above. Ask for it in writing if you’re paying for support.
-
Build the PHP team’s backport. The PHP security team published small fixes for people who build from source or have no vendor support:
Build it with the same options you use now. You can see them with
php -i | grep "Configure Command". In short: clone the branch, run./buildconf, run./configurewith your options, then runmakeandmake install. Test it on a staging server first.
PHP 7.3, 7.2, 7.1, 7.0, 5.6 and older
The PHP project has published nothing for these versions, and the bugs go back to PHP 5.4. You have three choices:
- Move to a supported PHP. This is the real fix, and it’s a project, not a quick patch. A common route is 5.6 to 7.4, then on to 8.3 or newer, fixing old code at each step. PHPCompatibility, PHPStan and Rector can find most of the problems before you test by hand. Our guide to modernizing a legacy PHP application shows how to do it in small steps. If you want help, see our application modernization service.
- Buy extended support. Some vendors look after old PHP versions for a fee. Ask them to show that both CVEs are fixed in your exact version.
- Keep the server off the open internet. If you can’t do either yet, put it behind a VPN, an IP allow-list or a login at the proxy, and use the stop-gap settings below. This buys you a few weeks. It doesn’t solve the problem.
If you’ve taken over an old server from someone else, our application handover checklist helps you find out what is really running on it.
If you can’t update today: stop-gap settings
The PHP team says only an update fully fixes this. The settings below close some of the attack routes. Set them where applications can’t change them (in php.ini, an FPM pool, or with php_admin_value), and restart every PHP worker afterwards.
Against CVE-2026-109618 (running out of memory):
- Set
memory_limit = -1. This takes away the memory failure an attacker aims for. It isn’t complete, because a failure deeper in the system can still reach the bug. It also means one runaway script can use all the memory on the server. So cap memory per worker with a container or cgroup limit that only stops that worker. Don’t useRLIMIT_ASorRLIMIT_DATA, because they can cause the very failure you’re trying to avoid. - Lower
post_max_sizeto 8M if you have no file uploads, or 32M if you do. Keepupload_max_filesizebelow it. - Set size limits at the web server or proxy too, including for requests that don’t say how big they are. In nginx that’s
client_max_body_size. In Apache it’sLimitRequestBody. Also limit the number of fields, parts, headers and nesting levels. - If your site doesn’t need uploads or multipart forms, block
multipart/form-dataat the web server. Every known trigger uses this format. It breaks file uploads, so test your forms and checkout afterwards.
Against CVE-2026-109619 (leftover state):
- Set
session.upload_progress.enabled = 0. Put it inphp.inior the pool config, not inini_set()or.user.ini, because those are applied too late. The PHP team suggests leaving it off after patching if you don’t use it. - On PHP 8.5, set
fatal_error_backtraces = 0. - Remove invalid or old PHP settings. Warnings that appear before a request starts are one way the bug is reached. Hiding them with
error_reportingor@doesn’t help. - Fix any fatal errors and timeouts that a visitor can trigger, for example in upload and import code.
- If your speed allows it, set
pm.max_requests = 1in PHP-FPM. Each worker then handles one request and is replaced. - Imperva also suggests turning OPcache off (
opcache.enable = 0) after testing, because one of the four bugs is in OPcache. Expect your site to get slower.
An example for an FPM pool:
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 2M
php_admin_flag[session.upload_progress.enabled] = off
Add a WAF as a second layer. On 21 October, Cloudflare turned on default protection for all plans, including Free. Paid plans should check that the Managed Ruleset is in block mode. Imperva says its Cloud WAF customers are covered out of the box, and Google Cloud has Cloud Armor guidance. Attackers have already been changing their payloads to get past the rules, so a WAF lowers the risk but doesn’t replace the update.
After you patch
- Restart PHP (or Apache) so every worker loads the new version. Then check the version the website serves, not just the command line.
- Go back to a normal memory limit if you set
memory_limit = -1as a stop-gap. The patched PHP handles running out of memory safely, and its normal memory limiting still applies. The fix also adds a hard limit that ends the process at once, so check thatmemory_limitandpost_max_sizesuit your app. - Look for signs of an attack. Watch for odd multipart requests followed by memory-limit errors or repeated 500 errors, very large or broken
PHPSESSIDcookies, and PHP workers restarting for no reason. Your app logs may show little, because this happens below the app. A clean log doesn’t prove you’re safe. - Look for changes you didn’t make: new files in web folders, new admin users, unknown scheduled jobs.
- Change your secrets if the server ran an affected version on the public internet. That means database passwords, API keys, certificates and session secrets.
- Switch off what you don’t use, such as session upload progress.
If you’d like a second pair of eyes on an old server, our security audit covers PHP versions, exposed services and the quickest fixes.
Need help with patching or upgrading?
We patch and upgrade PHP servers and applications every week, from a quick update to a full move off PHP 5.6 or 7.x. We can check how exposed you are, put stop-gap protection in place today, and plan the safe route to a supported version.
- Talk to us about patching or upgrading
- Technical & security audit: find out where you’re exposed and what to fix first
- Application modernization: move an old PHP app to a version that gets security fixes
Timeline
- 1 August 2026: Calif finds multiple ways to run code in PHP.
- 11 August 2026: Calif reports the first bug to PHP.
- 19 August 2026: Wiz Research finds the first bug on its own.
- 20 and 21 August 2026: Wiz confirms working attacks against Drupal (20 August) and WordPress (21 August).
- 24 August 2026: Wiz reports the bugs to the PHP security team.
- 21 September 2026: PHP confirms the reports and starts a coordinated fix.
- 20 October 2026: Details leak early, and PHP moves the release forward to 21 October.
- 21 October 2026, 03:02 UTC: Cloudflare turns on default protection for all plans and reports attack traffic before the release.
- 21 October 2026, 14:00 UTC: The patched releases, advisories and CVE numbers go public.
- 21 October 2026, 14:19 UTC: watchTowr reports active exploitation attempts of both bugs.
Conclusion
These two PHP bugs are serious, attacks have started, and the only full fix is a patched PHP: 8.2.35, 8.3.36, 8.4.27 or 8.5.12, or newer. PHP 8.1 and older get no update from the PHP project. For those, use a vendor backport, build the PHP team’s patch for 7.4, 8.0 or 8.1, or upgrade. Upgrading is the fix that lasts.
Start today. Check which PHP your website really runs, patch it or apply the stop-gap settings, then check your logs and change your secrets if the server was exposed.
Sources
- PHP security advisory, 21 October 2026
- GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618)
- GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619)
- Wiz Research: PHP2Shell
- OpenAI: Patch the Planet, coordinated disclosure of critical vulnerabilities
- OpenAI php2shell-checker
- Google Cloud: Responding to core PHP vulnerabilities
- Cloudflare: protections against php2shell
- Sansec: PHP2Shell research
- Imperva: PHP2Shell vulnerability updates
- The Hacker News: PHP emergency patches for two critical RCE flaws
- PHP supported versions
Frequently asked questions
Are these bugs already being attacked?
Yes, attempts have started. Cloudflare said it saw attack traffic against its customers before the official release, and the security firm watchTowr reported active exploitation attempts on the day of release. An exploit tool and technical write-ups are also public. At the time of writing, no successful break-ins had been publicly reported. Treat any unpatched, internet-facing PHP server as a target.
Is my site already hacked if it runs an affected PHP version?
Not necessarily. An affected version means the door is open. It doesn't mean someone has walked in. Patch first. Then check your logs for odd multipart requests followed by memory errors, repeated 500 errors, very large PHPSESSID cookies, and PHP workers restarting for no reason.
I don't have file uploads on my site. Am I safe?
No. The attacks use the way PHP reads multipart requests and session upload progress. Both happen before your own code runs. Wiz says even a plain Hello World page on an affected version can be attacked. Having no upload form helps a little with the stop-gap settings, but it doesn't make you safe.
Does a firewall or WAF protect me?
It helps, but only as a second layer. Cloudflare switched on default rules for all plans, including Free, on 21 October. Imperva says its Cloud WAF customers are covered out of the box, and Google Cloud has Cloud Armor guidance. Attackers have already been changing their payloads to get around the rules. The PHP team and Wiz both say that updating PHP is the only complete fix.
My PHP version number looks old, but my server says it's patched. Can that be true?
Yes. Linux vendors often copy a security fix into an older package and keep the version number the same. So don't go by the number alone. Check the package changelog for CVE-2026-109618 and CVE-2026-109619, or ask your vendor to confirm in writing.
Is there a fix for PHP 5.6, 7.0, 7.1, 7.2 or 7.3?
Not from the PHP project. These versions lost support years ago, and there is no patch or backport branch for them. You can move to a supported version, buy extended support from a vendor that covers your version, or keep the server off the public internet until you can move.
Can I just set memory_limit to -1 and be done?
No. The PHP advisory lists it as a partial fix for one of the two bugs. It also has a cost: a runaway script can now use all the memory on the server. If you use it, set it where applications can't change it, cap memory per worker with a container or cgroup, and still plan the update.
Will updating WordPress, Drupal, Magento or Joomla fix this?
No. The bug is in PHP, not in the CMS or the shop software. Calif showed code execution against the latest WordPress and Magento, and against Drupal, Joomla and several other PHP apps. Keep your CMS up to date anyway, but you also have to update PHP on the server.
Should I move to PHP 8.4 or 8.5 instead of patching 8.2 or 8.3?
If you can, yes. PHP 8.2 gets its last security fix on 31 December 2026, so patching it buys you only a couple of months. PHP 8.3 is supported until the end of 2027, 8.4 until the end of 2028 and 8.5 until the end of 2029. Test your app on the newer version first.
Do I need to change passwords and keys after patching?
If your server ran an affected version on the public internet, it's a smart precaution. The memory leak can expose passwords, tokens, API keys and certificates from other requests on the same worker. Changing database passwords, API keys and session secrets takes an hour. Finding out later that they were stolen takes much longer.
Is there a tool that tells me if my site is vulnerable?
OpenAI published php2shell-checker on GitHub. It reads the PHP version from your site's HTTP headers, or from php -v on the server, and answers OK, VULNERABLE or UNKNOWN. It only compares version numbers. It can't see vendor backports or settings, and many servers hide their version, so UNKNOWN doesn't mean you're patched. Use it as a first look, then check the version your web server really runs.



