PHP2Shell: What CVE-2026-109618 and CVE-2026-109619 Mean, and What to Do Now
A plain-language guide to the PHP2Shell vulnerabilities: what broke, which PHP versions and setups are affected, how to patch, and what to do if you can't patch today.
On 21 October 2026 the PHP project shipped emergency releases for two critical vulnerabilities, CVE-2026-109618 and CVE-2026-109619. Between them they have been nicknamed PHP2Shell, and the name is accurate: an unauthenticated attacker can send a request to a web server running a vulnerable PHP and end up executing code on it.
Most of my own work is in TypeScript, but PHP is rarely far away. A marketing site on WordPress, a WooCommerce shop, an old admin panel nobody has logged into for a year, a client's shared hosting account — plenty of teams whose product runs on Node or Python still have PHP answering requests somewhere. This post is the checklist I would want if that were my server.
Everything here reflects what was public as of 26 October 2026. This is a fast-moving story, so the advisories linked at the end are the source of truth.
What actually broke
Both bugs sit in PHP's own request handling, not in any framework or application code. That is what makes them serious: it does not matter how carefully your Laravel app or WordPress theme was written.
CVE-2026-109618 — memory corruption on out-of-memory. When a request goes over memory_limit, PHP aborts it by jumping straight out of whatever it was doing. If that happens partway through setting up the request, some internal data is left half-initialised, and the cleanup code that runs afterwards trusts it anyway. An attacker who can push a request over the limit at the right moment can turn that into code execution.
CVE-2026-109619 — stale state between requests. PHP workers are reused: one process handles many requests in a row. This bug lets internal state from one request outlive the memory it points to, so the next request on the same worker can act on freed memory. A fatal error is one way to trigger it.
Both are rated 9.2 on CVSS v4.0. Neither needs a login.
Why it went out early
The issues were found by researchers at Calif and reported privately, with coordinated disclosure planned for 22 October. Details leaked on 20 October, so PHP moved the release forward and published fixes on 21 October. watchTowr reported seeing exploitation attempts in its honeypot network from 21 October. As of 22 October, CISA had not yet added either CVE to its Known Exploited Vulnerabilities catalogue.
The practical reading: treat this as being exploited, and do not wait for an official confirmation.
Am I affected?
Almost certainly, if you serve web requests with PHP and have not updated since 21 October. The affected setups include:
- PHP-FPM — the usual pairing with nginx
- Apache mod_php — which the PHP team considers the highest risk, because an attack can land before your application code even runs
- php-cgi
- FrankenPHP and LiteSpeed
- Third-party products that embed PHP, such as appliances and control panels
The fixed releases are:
- 8.5.12 for the 8.5 branch
- 8.4.27 for 8.4
- 8.3.36 for 8.3
- 8.2.35 for 8.2
PHP 8.1, 8.0 and 7.4 are end-of-life and get no official release. The PHP team published minimal source backports for those three, and Linux distributions and LTS vendors had patches in advance. PHP 7.3 and older, including PHP 5, have no fix at all. If something you run is on one of those, the fix is an upgrade.
Checking the version that actually serves requests
This trips people up, so it is worth being careful. php -v tells you the version of the command-line PHP. The PHP behind your web server can be a different binary with a different version.
# The CLI — not necessarily what serves your site
php -v
# The FPM binary, which is what nginx talks to
php-fpm -v # may be named php-fpm8.3, php8.3-fpm, etc.
# On Debian or Ubuntu, see every installed PHP package
dpkg -l | grep -E '^ii\s+php[0-9.]*-(fpm|cgi|common)'The most reliable answer comes from the web server itself: a temporary page that calls phpinfo() shows the version and the Server API (FPM, Apache 2.0 Handler, CGI) for that exact site. Delete it as soon as you have read it.
The fix, in order
1. Update PHP. Use your distribution's package manager, or the fixed release above for your branch. The PHP team is explicit that no configuration change fully mitigates these bugs. Patching is the fix.
2. Restart every PHP worker. This is the step that gets skipped. Installing a new package does not replace PHP-FPM workers that are already running, so until they restart you are still running the old code.
sudo systemctl restart php8.3-fpm # use your own service name
sudo systemctl restart apache2 # if you use mod_php3. Rebuild containers — don't just restart them. If PHP runs in Docker, the vulnerable version is baked into the image. Pull the updated base image, rebuild, and redeploy. Restarting a container from the old image changes nothing.
4. Find the PHP you forgot about. Inventory everything internet-facing: staging servers, old client sites, a phpMyAdmin you set up once, a hosting control panel. The forgotten box is the one that gets compromised. For appliances and products that embed PHP, the Dutch NCSC is keeping a list of vendor statuses in its php2shell repository.
5. If you are on managed hosting, check your host's status page. Some hosts applied mitigations at their edge before the patch was out. That is welcome, but confirm they have also updated PHP itself.
If you cannot patch today
These temporary mitigations come from the CERT-EU advisory. The advisory is clear that they "do not make an installation safe" — they buy time, nothing more.
If you can, take the site off the public internet until it is patched. Behind a VPN or an IP allow-list is far safer than any setting below.
For CVE-2026-109619:
- Set
session.upload_progress.enabled=0inphp.inior the FPM pool configuration, then restart every worker. Setting it withini_set()or.user.iniis not enough. - On PHP 8.5, also set
fatal_error_backtraces=0and restart. - Fix fatal errors and timeouts that an outside visitor can trigger, rather than hiding them.
For CVE-2026-109618:
- Set
post_max_sizeto8M, or32Mif you need uploads, and keepupload_max_filesizebelow it. - Enforce request size limits at the web server or proxy as well, including for requests that arrive without a
Content-Lengthheader. - Use a container or cgroup memory limit rather than relying on PHP's own.
On 23 October PHP also disclosed a third, separate issue, CVE-2026-115560. PHP considers it an unlikely risk because it only affects applications configured in an unusual way, and it will be fixed in the next regular release. The same session.upload_progress.enabled=0 setting is the interim mitigation.
Checking whether you were hit
Patching closes the door; it does not tell you whether someone came in first. If an internet-facing server was unpatched after 20 October, look for:
- PHP or web-server processes starting shells,
curl,wgetorbase64— normal PHP almost never does this - New or modified
.phpfiles in upload or cache directories - Unfamiliar cron jobs, SSH keys or system users
- Outbound connections from the web server to addresses you do not recognise
If you find any of these, treat the machine as compromised: rebuild it from a known-good image and rotate every credential it could reach — database passwords, API keys, cloud access keys.
The pattern worth remembering
Last December it was React's turn. React2Shell (CVE-2025-55182) was an unauthenticated remote-code-execution bug in React Server Components, and it reached Next.js App Router sites the same way PHP2Shell reaches PHP sites: through the platform, before a single line of the application's own code had a say.
The lesson from both is the same and it is not glamorous. Know which runtime versions you are running in production, be able to update them within hours rather than weeks, and keep a list of every server you are responsible for — including the ones you would rather forget.