On 21 October 2026 PHP shipped an emergency release for two bugs in the language itself: CVE-2026-109618 and CVE-2026-109619, together nicknamed PHP2Shell. Both are rated 9.2 (Critical) and both can give an attacker remote code execution with no login - on a server whose application code has no bug at all. Wiz, one of the teams that reported them, says every PHP version released in the last decade is affected by at least one of the two, going back to PHP 5.4 from 2012.
The other reason this one made the evening news is how it was found. Three separate reports reached the PHP security team in August and September, and frontier AI models are credited with a large share of the finding, the exploiting and the checking. This article covers the bug itself, how it was found, how it was fixed, what you need to do, and whether AI is the real danger here.
If you only read one line: update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (or your distribution's patched package), then restart every PHP process - Apache, PHP-FPM, queue workers. PHP's own advisory says there is no complete workaround: "updating is the only safe choice."
What PHP2Shell is, in plain English
A PHP server does not start a fresh process for every visitor. It keeps a pool of worker processes alive and hands each new request to one of them. Every request gets its own memory, and PHP is supposed to clean that memory up completely before the worker takes the next request.
Both bugs are failures of that clean-up, triggered at moments PHP did not expect to be interrupted:
- CVE-2026-109618 - "RCE via OOM corruption". If a request runs out of memory
(hits
memory_limit) at the wrong moment, PHP abandons it while some internal data is only half set up. The clean-up code then reads that half-built data as if it were valid. - CVE-2026-109619 - "RCE via stale global state". If a fatal error or an out-of-memory error interrupts a request at the wrong point, some of PHP's global state keeps pointing at memory that belonged to that request. When the same worker picks up the next request, it can use that freed memory before it is reset - a classic use-after-free, across two requests.
Corrupted or reused memory is the raw material for remote code execution: an attacker who can shape what ends up in that memory can steer the process. That is what "2Shell" in the name means - from an HTTP request to a shell on your server.
Why your application does not need a bug
This is the part that makes PHP2Shell unusual. Most PHP security news is about a plugin, a framework or somebody's code. These two bugs are in the interpreter, and the dangerous paths run before your code is reached:
- Multipart parsing happens during request startup. When a request arrives with a
file-upload style body, PHP parses it to fill
$_POSTand$_FILESbefore your script starts. The advisory for CVE-2026-109618 describes three ways an attacker can push that parsing into an out-of-memory error while staying inside limits that look safe: a body with no usableContent-Lengththat keeps streaming, a part with an unlimited number of headers, and empty file fields that do not count towardsmax_file_uploadsbut still allocate memory. - Session upload progress runs without
session_start(). Thesession.upload_progressfeature is enabled by default and does its work during upload parsing. The CVE-2026-109619 advisory names it as one of the confirmed paths. - Settings applied per request can trip it. On Apache with mod_php,
.htaccessandphp_valuedirectives are applied before the request is fully initialised. A warning raised there can read the stale state from the previous request - and the advisory notes that hiding warnings witherror_reporting,@ordisplay_errorsdoes not help.
Wiz says it got remote code execution against the stock WordPress, Drupal and Joomla container images
and against a plain "Hello World" page. It also showed a second outcome that needs no code execution
at all: leaking a worker's memory back to the attacker inside an oversized PHPSESSID
cookie. Worker memory can hold database passwords, API keys and other visitors' data.
Which setups are most exposed
- Apache with mod_php is the worst case. It runs request shutdown even when startup failed, so a single unauthenticated request can trigger CVE-2026-109618 before any application code runs. PHP says full RCE was demonstrated here with default settings.
- PHP-FPM is less exposed to the startup path, because a child that fails to start exits. The CVE-2026-109618 advisory says no complete FPM exploit was demonstrated; but the PHP Foundation confirms RCE on FPM through application-level code paths, and the CVE-2026-109619 advisory says the session-upload-progress RCE was validated against both mod_php and FPM.
- FrankenPHP and LiteSpeed (LSAPI) are judged likely susceptible from source review, not validated.
How it was found
The timeline below comes from the PHP Foundation's post and Wiz's write-up, which agree on the dates they share.
- 2024: during PHP's first external security audit (by Quarkslab, organised by OSTIF and funded by Germany's Sovereign Tech Agency), a PHP core developer spotted the first signs of the out-of-memory problem while fixing a different report. It was not fixed at the time; reporting since disclosure says the team could not agree on a fix and nobody had shown it was exploitable.
- 30 July 2026: security firm Calif starts an audit of PHP under OpenAI's "Patch the Planet" programme.
- 11 August: Calif's Ryan Austin reports an unconditional remote code execution with the out-of-memory root cause. Coverage citing the Wall Street Journal and New York Times says he used OpenAI's GPT-5.6 Sol.
- 19-24 August: Wiz Research, working independently, finds its first PHP bug on 19 August, confirms unauthenticated RCE against Drupal the next day and WordPress the day after, and reports to PHP on 24 August. Wiz says it used its own Wiz Atlas tool together with Google's Gemini 4 Argon and GPT-5.6 Sol, and that the work was done by a single researcher in about a week.
- 18 September: OpenAI reports further attack vectors.
- 21 October: patched releases and public disclosure.
What the AI actually did matters more than the headline. In Wiz's account the models contributed to finding the bugs, writing exploits, adapting those exploits to different PHP versions, configurations and memory layouts, detecting attacks and validating the patch - analysing their own failures and trying again. Humans chained the bugs together and did the per-target debugging. On the receiving end, the PHP security team also uses AI now: to validate incoming reports and group the ones with the same root cause. Their post is explicit that "a person makes every call and works on every patch."
If you want to see what this kind of AI-driven bug hunting looks like in practice - a model driving a disassembler or a debugger on its own - I wrote up the tooling in AI reverse engineering: Ghidra MCP, IDA MCP and REA.
How the disclosure went wrong
Coordinated disclosure means the fix and the announcement land at the same moment, so defenders and attackers start level. PHP2Shell did not get that. In UTC, per the PHP Foundation, Patchstack and the reporting that followed:
- 17 October: Amazon's MadPot honeypots recorded an exploitation attempt from an anonymising network - four days before disclosure. Amazon says it is unclear how the attacker knew, and advises investigating from at least 17 October, not from the release date.
- 20 October, ~15:00: the CVE IDs leaked on forums and social media, with a wrong release time and a wrong CVSS score of 10.0. No technical details leaked.
- 20 October, 19:30: PHP published its pre-announcement early and brought the release forward a day, to 21 October at 14:00.
- 20 October, ~20:00: Calif mistakenly published a video of a working proof of concept, which the PHP Foundation says accelerated attackers' own proof-of-concept work.
- 21 October, 07:41: container vendor Chainguard published patched images more than six hours before the coordinated release - letting anyone compare old and new code.
- 21 October, 07:43: Cloudflare's CTO said it was seeing early attacks and had deployed WAF rules.
- 21 October, ~13:34-14:00: advisories and releases went out.
Some operators chose to go dark rather than wait. Reported examples include the Dutch government portal Overheid.nl, Finnish public-facing government services, French Interior Ministry websites, parts of Wikimedia's supporting infrastructure, and the Internet Archive briefly.
Is it being exploited? The honest answer on 22 October is "reports conflict". CISA said on 21 October it had not observed exploitation. watchTowr said its honeypots saw exploitation by the same day. Patchstack reported that hosting partners had reported active incidents that morning, while its own telemetry after release showed probing rather than confirmed breaches. Plan as if exploitation is happening.
How it was fixed
The fixes go after the root cause rather than the individual attack paths:
- Out-of-memory no longer jumps out of the middle of anything. PHP used a
longjmp()to abandon a request the instant an allocation failed - wherever it happened to be. The patch removes that. Hitting the limit now raises a delayed "soft" out-of-memory error at the next safe point in the virtual machine, where no half-built state is in flight, plus an immediate hard exit of the process (_exit()) at a higher threshold. Either way, nothing reads half-initialised data afterwards. - Request state is reset before request memory is freed. The CVE-2026-109619 fix makes sure error, session, object and extension state that points into a request's memory is cleared before that memory is released, so the next request on the worker has nothing stale to find.
Fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older are end of life and get no binaries, but because of the severity the PHP team published minimal source backports for 7.4, 8.0 and 8.1 for people who build from source. Linux distributions and long-term-support vendors received the patches in advance, so your distribution's package may already be fixed under its own version number.
What to do now
1. Find out which PHP actually serves your site
php -v # the command-line PHP - NOT necessarily what your web server runs
php-fpm8.3 -v # each FPM version installed (Debian/Ubuntu naming)
That first command catches people out. The php on your shell path and the PHP behind
your website are often different builds, and many servers have several versions installed side by side.
The reliable check is from the web side: a phpinfo() page you delete straight afterwards,
or the version shown in WordPress → Tools → Site Health → Info → Server. On cPanel,
MultiPHP Manager lists the version per domain; Plesk shows it in each domain's PHP settings. Update
every installed version, not just the one your site uses.
2. Update, then restart everything
A new package on disk does nothing while old workers are still running the old code in memory. Restart Apache, every PHP-FPM pool and any long-running PHP process (queue workers, Laravel Octane, RoadRunner, Swoole). Rebuild and redeploy container images - a running container does not patch itself.
3. If you genuinely cannot update today
These come from PHP's own announcement. They block the known paths and nothing more; PHP is clear that they do not make a server safe:
; php.ini, FPM pool or server config - not ini_set(), not .user.ini
memory_limit = -1 ; removes the controllable OOM trigger
post_max_size = 8M ; 32M only if you need uploads
upload_max_filesize = 6M ; example - keep it below post_max_size
session.upload_progress.enabled = 0 ; blocks the confirmed session paths
fatal_error_backtraces = 0 ; PHP 8.5 only
Apply them where PHP reads them before the request starts, and restart every worker. Put request-size
limits in front of PHP too, at the web server or proxy, because the limits have to cover bodies with no
usable Content-Length. And note the warning in the advisory: do not use
RLIMIT_AS or RLIMIT_DATA as a substitute memory cap - they can cause exactly
the allocation failure you are trying to avoid. If the server is high-value and none of this is possible,
CISA's advice is to take it offline until it is patched.
4. Look back, starting from 17 October
The signs Wiz, Patchstack and CISA list: unusual multipart requests followed by memory-limit errors,
bursts of HTTP 500s, oversized or malformed PHPSESSID cookies, unexplained PHP worker
restarts, and - the one that matters most - a PHP process starting a shell or downloading tools. None of
these proves anything on its own. If you see them, patch anyway, treat the server as possibly
compromised, and rotate the secrets it holds, because a memory leak does not leave a broken page behind.
5. "My site isn't PHP" - check the server anyway
This site runs on ASP.NET Core and Next.js, so PHP2Shell does not touch its code. That is exactly the trap for anyone on shared or panel-managed hosting. A Plesk or cPanel server usually has PHP installed whether or not your site uses it - for webmail such as Roundcube, for phpMyAdmin, and for the other customers on the same machine. Ask your host which PHP builds are on your server and when they were patched. If you are deciding between a managed CMS and something custom partly on maintenance grounds, this is the kind of event that belongs in that decision - see WordPress vs a custom website.
Is AI the danger here?
Every headline asked it, so here is my reading of the evidence - and it is a reading, not a fact.
AI did not create these bugs. They were written by people, some of them over a decade ago, and a person had already seen the first sign of one in 2024. What stopped anyone fixing it was not a lack of intelligence; it was disagreement, missing proof of exploitability and too few people. The PHP Foundation says so itself: the delay "was a structural failure, caused by both the security team's decision process and its lack of resources."
What AI changed is the cost. Wiz's account is one researcher, about a week, three of the biggest PHP applications exploited, with the models porting the exploit across versions. Turning "this looks like it might be exploitable" into a working attack used to be the expensive part. Here it was the fast part. The same economics apply to attackers - and they apply to the gap between a patch and an exploit. Security firm Sansec predicted "mass attacks to start within 15 minutes of the release"; Chainguard's early images handed out the patch diff hours ahead. When a model can read a diff and work out the bug, publishing a fix is publishing a map.
The same tools are the defence. All three reports came from defenders using AI, and PHP's own team uses AI to cope with the volume - it says it has handled more than 100 invalid, duplicate or unreproducible reports since July alongside 15 real CVEs. The capability is symmetrical. What is not symmetrical is funding: The Guardian pointed out that the foundation maintaining a language behind a huge share of the web had 13 full- and part-time staff, while the AI work credited with the discovery sits on billions in investment.
So no, AI is not the threat on its own. The threat is the time between "a fix exists" and "your server runs it", and AI is shrinking every other clock in this story. Updates that used to be fine within a week now need to happen within hours, which mostly means automatic security updates, an inventory of everything that runs PHP, and a host that tells you when it has patched. If your process today is "someone remembers to check", PHP2Shell is the reason to change it.
The short version
Two memory-safety bugs in PHP's request clean-up, present since 2012, let an unauthenticated request corrupt or reuse worker memory and take over the process - before your code runs. AI-assisted research found and weaponised them in days; a leaky disclosure gave attackers a head start; PHP fixed the root cause by no longer abandoning requests mid-operation and by resetting state properly. Update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, restart every worker, and check for signs of trouble back to 17 October.





Join the conversation
No comments yet — be the first to share what you think.