Skip to content
duglee_labs

The Curious Case A PHP Vulnerability

2026-10-22 · Gaurav Sharma · technology

AISecurityVulnurabilityPHPCVE
The Curious Case A PHP Vulnerability

What happened on October 21

On October 21, 2026, the PHP project released an emergency security update for two critical flaws in the PHP engine, CVE-2026-109618 and CVE-2026-109619. Both are rated 9.2 out of 10 on the CVSS severity scale and are known collectively as PHP2Shell. They let an attacker run code on a web server with a single request and no login. Because they sit in the PHP engine itself, a site is exposed even when its own code has no bugs.

The scope is large. PHP powers roughly 70 percent of all websites, including WordPress, Drupal, Joomla and the MediaWiki software behind Wikipedia. Every PHP installation that serves web requests is affected, whether it runs through PHP-FPM, Apache with mod_php, CGI, FrankenPHP or LiteSpeed. The vulnerable code dates back to PHP 5.4.0, released in 2012.

What actually broke

Both bugs belong to a class of memory errors in which a program keeps using memory it has already handed back. Picture a busy kitchen. A cook starts an order, lays ingredients out on the counter, then gets pulled away mid task. The next cook walks up, sees things on the counter and assumes they are fresh. They belong to an order that was abandoned and cleared, and trusting them leads somewhere bad.

Inside PHP, the interruption is an out of memory error. An attacker can deliberately flood a request until PHP exceeds its memory budget and bails out partway through setting up internal data. The cleanup that follows, or the next request handled by the same worker process, then treats that unfinished and already freed memory as if it were real. From there an attacker can corrupt what PHP does next and, in the worst case, make it run their own code.

One of the two flaws has a second effect. It can hand back pieces of the server's memory to the attacker, similar to the 2014 Heartbleed bug. Those pieces may contain passwords, session tokens, API keys or encryption keys left over from other people's requests. Because the vulnerable code runs before a site's own code starts, it works even on a bare "Hello World" page that never touches sessions.

Under the hood

The two CVEs are an umbrella over at least four distinct memory-safety bugs that Wiz and Calif reported, all sharing the same root pattern: PHP publishes some internal state, then performs a step that can fail, and a nonlocal fatal error, usually hitting the memory_limit, skips the code that would commit or roll that state back. A later request handled by the same worker then trusts the leftover state.

A precondition for all of it is being able to trigger that fatal on demand. Calif found two ways to do it. The first bypasses the post_max_size limit: PHP checks post_max_size against the declared Content-Length before parsing, but does not enforce it against the bytes actually consumed, so a chunked HTTP/1.1 or lengthless HTTP/2 multipart body slips past. The second stays under post_max_size but still exhausts memory: an RFC1867 upload part with filename="" carries no data and is not counted toward max_file_uploads, yet PHP still creates all six $_FILES entries (name, full_path, type, tmp_name, error, size). Nesting the field name as files[a][b][c]... forces PHP to rebuild that hierarchy for each entry, so a handful of empty records expands into enough HashTable, Bucket and string allocations to blow past the default 128 MiB heap while the HTTP body stays under the default 8 MiB cap.

With a controllable fatal in hand, the four bugs each supply a different interruption point:

  1. Nested POST / $_FILES use-after-free. In _zend_hash_str_add_or_update_i, PHP increments the bucket count before the key allocation that may bail out. The half-initialized bucket stays visible to teardown, which then consumes stale bytes as a live bucket. On Apache mod_php this yields RCE against arbitrary scripts (PHP 7.3+); PHP-FPM exits the worker on a startup failure, so it escapes this one.
  2. Upload-progress session use-after-free. The session upload-progress feature opens a session during request startup, before module activation. If parsing fatals, cleanup is skipped and the request heap is freed while PS(id) and PS(http_session_vars) still point into it. A second multipart request without a file part reaches the cleanup path and reads and destroys that freed state. Exploitable on mod_php back to PHP 5.4.0.
  3. Fatal-error backtrace use-after-free. PHP keeps the most recent fatal error's backtrace in EG(last_fatal_error_backtrace). An out-of-memory fatal can free the request heap without clearing it, leaving a dangling pointer the next request can follow.
  4. OPcache type confusion. During zend_accel_script_persist(), PHP advances a process-local pointer frontier but publishes the shared one only after persistence completes. A fatal in between leaves partially persisted objects referencing slots the shared state still thinks are free; when another worker reuses a slot, the mismatched metadata becomes a cross-worker type confusion in shared memory.

The two CVEs group these by impact:

CVEUnderlying bugsImpactCVSS v4.0
CVE-2026-109618Upload-progress unserializer use-after-free; remote process-memory disclosureUnauthenticated RCE and Heartbleed-style memory disclosure9.2
CVE-2026-109619Fatal-error backtrace use-after-free; OPcache type confusionUnauthenticated RCE9.2

The memory-disclosure path is worth singling out. On a vulnerable worker, PHP can URL-encode freed heap contents into a PHPSESSID response cookie, handing the attacker a chosen-size slice of live process memory that may hold data from other requests. It works even when the application never calls session_start(), which makes it application-agnostic.

Researchers combined the primitives into a single exploit tool, php2shell, and confirmed pre-authentication RCE against current versions of WordPress, Drupal, Joomla, MediaWiki, Roundcube, Magento, phpMyAdmin, Nextcloud, Laravel, Symfony and WHMCS. The targets only illustrate reach; because the flaw is in the engine, even a Hello World script on an affected version is exposed.

Because the bugs execute below the application layer, site logs show little. Wiz suggested defenders watch for unusual multipart requests followed by memory-limit errors or repeated HTTP 500s, unexpectedly large or malformed PHPSESSID response cookies, and unexplained PHP worker churn. None of these replace patching, which is the only complete fix.

A bug with a long history

The earliest vulnerable code dates to PHP 5.4.0 in 2012, and the wider family of memory limit tricks goes back further, to research the PHP security veteran Stefan Esser published in 2004. The specific out of memory issue was reported as a minor bug in 2020 and examined again during a professional PHP security audit in 2024. Both times, exploitation could not be proven, so the issue was not fixed.

What kept the weakness harmless for over a decade was difficulty rather than secrecy. Turning it into a reliable attack meant chaining several bugs together and adapting the exploit to each server's exact memory layout through repeated trial and error. Wiz described that work as sitting at the edge of what human researchers could practically achieve.

In August 2026, three separate teams reached the same flaws within weeks of each other. The security firm Calif found the first path on August 1 and reported it to PHP on August 11. Wiz Research found its first bug on August 19 and reported on August 24. OpenAI was also credited with discovery. The PHP security team coordinated a single fix across all three reports.

The role of AI

The discoveries grew out of Patch the Planet, an OpenAI initiative to find and fix flaws in the shared software the internet depends on. PHP was chosen as a high impact target. Wiz used Google's Gemini 4 Argon and OpenAI's GPT-5.6 Sol alongside its own tooling, and credited the models with helping find the bug, build the exploit and verify the fix.

The AI did not find the vulnerability on its own. Calif's researcher, Ryan Austin, wrote that success came from knowing PHP's vulnerability history and asking the right questions, and that no model can simply surface every bug from a basic prompt. What the models changed was the grind: adapting a fragile exploit across different PHP versions, server setups and memory layouts, analyzing failures and iterating in a way that used to demand a specialist's full attention for each target.

Wiz estimated that with AI, a single researcher did in about a week what would previously have taken a world class team months. The company compared PHP2Shell to Heartbleed and Log4Shell, and argued it marks a similar turning point for the AI era, where complexity no longer provides a meaningful barrier to exploiting old code.

Disclosure did not go to plan

The release was meant to be coordinated. Details leaked on social media on October 20, a day before the fix was ready. Cloudflare reported attacks starting at 07:43 UTC on October 21, hours before PHP published the patched versions at 14:00 UTC. Separately, Amazon said its honeypot network had recorded an exploitation attempt on October 17, four days before public disclosure and while the bugs were still under embargo. How that attacker obtained the details is not known.

Several Dutch organizations took their sites offline as a precaution on October 21, including the technology news site Tweakers and the Dutch House of Representatives. The UK's National Cyber Security Centre and CISA in the United States both urged immediate patching, and the NCSC advised organizations to consider taking offline any system that could not be fixed right away.

A long tail of unpatched servers is expected. Many PHP installations are forgotten or unmaintained, and older PHP versions, embedded devices and abandoned applications will not receive the update. Researchers also noted that the public writeups and an exploit tool are already available, which typically leads to mass exploitation.

What to do now

Update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, which contain the fix. If a hosting provider manages your server, ask them to apply the update. The flaw cannot be patched from inside a site's own code because it sits below the application layer. PHP no longer supports versions 8.1 and older, but the security team released minimal source backports for PHP 7.4, 8.0 and 8.1 because of the severity, and some Linux distributions and long term support vendors have backported the fix themselves.

If patching is not possible within hours, researchers published temporary and partial mitigations. Calif advised setting session.upload_progress.enabled to 0 and memory_limit to -1 for PHP-FPM, and memory_limit to -1 for mod_php, with the warning that removing the memory cap lets a single request use all server memory. Sansec recommended rejecting multipart/form-data requests at the web server, since every known trigger uses that format, or taking the server offline until it is patched. A PHP core developer cautioned that disabling upload progress alone is not enough.

Anyone installing PHP from Remi's repository, common on RHEL based systems, should note the fix was not available there on release day; Sansec suggested switching to distribution packages or building from source. Cloudflare said WAF rules for the known exploit vectors are available to all its customers, including free plans. Sites behind such protections should still apply the PHP update, which remains the only complete fix.

Sources