DEV Community

Cover image for A 14-Year-Old PHP Bug Lets Strangers Run Code on Your Server. Here's How to Stop It
Emmanuel R for CobuildX AI

Posted on Originally published at cobuildx.ai

A 14-Year-Old PHP Bug Lets Strangers Run Code on Your Server. Here's How to Stop It

The code nobody touched for years just became everyone's problem. A bug hiding in PHP since 2012 lets strangers run code on web servers, and the NSA and India's CERT-In have both raised the alarm. Here's how PHP2Shell (CVE-2026-109618 and CVE-2026-109619) works, how it was found, and exactly what to do about it.

On 21 October 2026, PHP shipped an emergency release for two critical bugs in the PHP engine itself: CVE-2026-109618 and CVE-2026-109619, nicknamed PHP2Shell. Both let an attacker run code on a web server without logging in [1][2].

Governments moved fast. The NSA told organizations that can't update right away to take their PHP systems offline [3]. The same day, India's national cybersecurity agency, CERT-In, issued a critical-severity advisory (CIVN-2026-0510) warning that vulnerable PHP servers could be exposed to remote code execution [10][11]. CISA in the US issued its own alert [6].

The unsettling part is where the bugs came from: code that has sat in PHP since 2012, and that attackers started probing before a patch existed. This post explains what the two bugs are, how they work, how they were found, and what to do about them. If you run PHP anywhere, including inside WordPress, Drupal, Magento or an internal tool, it applies to you.

The Two Bugs at a Glance

CVE-2026-109618 CVE-2026-109619
Name in the advisory RCE via OOM corruption RCE via stale global state
Bug type Memory corruption when PHP runs out of memory Use-after-free across two requests
Severity CVSS v4.0 9.2, critical CVSS v4.0 9.2, critical
Login needed? No No
Confirmed exploit Apache mod_php mod_php and PHP-FPM
Advisory GHSA-w8hw-w9fx-j79w GHSA-6wmf-4rv5-p3rp

In both cases, a successful attack runs code as the web server's PHP user. From there an attacker can read files, steal database passwords and keys from config files, or plant a backdoor [1][2].

How the Bugs Work

How PHP2Shell works. CVE-2026-109618: a crafted multipart request makes PHP run out of memory while the request is half set up; PHP jumps out early, and the clean-up step then uses the half-built data. CVE-2026-109619: request A ends in a fatal error, so clean-up is skipped and the worker keeps a pointer to freed memory; request B on the same worker uses that pointer before normal setup clears it

CVE-2026-109618: running out of memory at the wrong moment

When PHP runs out of memory, it doesn't return an error step by step. It jumps straight out of whatever it was doing, using a C function called longjmp(). If that happens while a request is still being set up, some of the request's data is left half-built. Later, when PHP cleans up the request, it treats that half-built data as valid, and memory gets corrupted [1].

An attacker can cause this on purpose. Weaknesses in how PHP parses multipart form uploads let a crafted request use up memory while still looking like it's within the configured limits. On Apache with mod_php, this happens before any application code runs, so the app itself doesn't need to have a bug [1][4].

CVE-2026-109619: one request's leftovers, used by the next

A PHP worker handles many requests in a row, and it keeps some global state between them: error, session and extension data. Normally that state is reset at the end of each request. But a fatal error, such as running out of memory or hitting the execution time limit, can skip that reset. The worker is left holding a pointer to memory that's already been freed [2].

If the next request on the same worker touches that state before it's reset, PHP uses freed memory. The advisory confirms two ways to reach it. One is session upload progress, a feature that's on by default and works even if the app never calls session_start(). The other is PHP 8.5's new fatal error backtraces setting, also on by default. Besides running code, this bug can leak memory from other users' requests, including passwords and tokens [2].

How It Was Found

Both bugs were found in August 2026 by researchers auditing PHP's C source with the help of AI models. Researcher Ryan Austin at Calif found one during an audit run under OpenAI's "Patch the Planet" project. Wiz found the same flaws independently, using AI models alongside its own tooling [5]. The PHP advisories credit reporters from both efforts and others [1][2].

The weakness wasn't entirely new. In 2024 a PHP core developer spotted the out-of-memory problem behind CVE-2026-109618 during an audit, but the team couldn't agree on a fix, and a working exploit was thought unlikely, so it wasn't prioritised [5]. The affected code dates back to PHP 5.4, released in 2012.

Timeline

Timeline: 2012, PHP 5.4 ships with the affected code. 2024, the out-of-memory issue is spotted but not fixed. August 2026, Calif and Wiz report the bugs. 17 October, first exploit attempt seen by Amazon. 20 October, details leak and PHP announces a release. 21 October, patches and alerts from the NSA, CISA and CERT-In

The release was meant to be a normal coordinated disclosure, but it didn't stay quiet. Amazon's honeypots logged an exploit attempt on 17 October, four days early. On 20 October news leaked on Hacker News, watchTowr posted about it, and a proof-of-concept video was published by mistake, so PHP announced the release a day ahead. The fixes and the two GitHub advisories went out on 21 October, the same day as alerts from CISA, the NSA, India's CERT-In and others [5][6][10].

Some organizations didn't wait. Government portals in the Netherlands, Finland and France, many Wikimedia services and the Internet Archive went offline for a time as a precaution. Wiz compared the bug to Heartbleed, the 2014 OpenSSL flaw [5].

Am I Affected?

  • Versions: everything from PHP 5.4 up to, but not including, 8.2.35, 8.3.36, 8.4.27 and 8.5.12 [1][2].
  • Server setups: any PHP that serves web requests: Apache mod_php (highest risk), PHP-FPM, php-cgi, FrankenPHP and LiteSpeed [4].
  • Old versions: PHP 8.1 and older are end of life. The PHP team released source patches for 7.4, 8.0 and 8.1, but no official builds. Older versions get nothing [5].
  • Hidden PHP: PHP is often bundled inside other products, such as control panels, appliances and vendor software. Cisco, for example, published its own advisory for affected products [6][7].

To check a server, look at the version and the two settings that matter most:

php -v
php -i | grep -E 'session.upload_progress.enabled|fatal_error_backtraces|memory_limit'
Enter fullscreen mode Exit fullscreen mode

Run this with the same PHP the web server uses. The command-line PHP can be a different version from PHP-FPM or mod_php, so also check your web server's phpinfo() page or package list.

What to Do Now

For security teams, this goes beyond applying an update. The work falls into four steps: patch, mitigate, contain and investigate. Start by finding every PHP install, internet-facing ones first [6].

1. Patch

Upgrade to a fixed release: 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or your Linux distribution's patched package. Then restart every PHP worker: PHP-FPM, Apache and any long-running PHP processes. Until they restart, the old code is still running [1][2].

2. Mitigate, if you can't patch today

The PHP advisories list temporary settings. Set them in php.ini, the PHP-FPM pool or the web server config. Setting them with ini_set() or .user.ini is too late to help [1][2][4].

; CVE-2026-109619: closes the confirmed session path.
; Worth keeping off after patching if you don't use upload progress bars.
session.upload_progress.enabled = 0

; CVE-2026-109619, PHP 8.5 only: closes the confirmed backtrace path.
fatal_error_backtraces = 0

; CVE-2026-109618: avoids the out-of-memory jump. Not a complete fix,
; so pair it with tight request limits below and at your proxy.
memory_limit = -1
post_max_size = 8M
upload_max_filesize = 2M
Enter fullscreen mode Exit fullscreen mode
  • Limit requests before they reach PHP. At nginx, Apache or your load balancer, cap request size, the number of form fields and multipart parts, and headers per part. If your app doesn't take file uploads or multipart forms, block them entirely [1].
  • Put a web application firewall in front if you have one [6].
  • Don't rely on RLIMIT_AS or RLIMIT_DATA. These operating system memory limits can trigger the very bug you're trying to avoid [1].
  • Hiding errors doesn't help. Turning off display_errors or using @ doesn't stop the stale state from being used [4].

These settings only close the paths researchers have confirmed so far. Both advisories say updating is the only complete fix [1][2].

3. Contain

If an internet-facing system can't be patched yet, or runs PHP 8.1 or older, take it offline until it's patched or upgraded. This is what the NSA and CISA both advise [3][6]. Several governments, Wikimedia and the Internet Archive did exactly this on 21 October [5].

4. Investigate

Exploit attempts started before the patch, so a patched server may already have been hit. Look for:

  • PHP worker processes starting a shell (sh, bash) or tools like curl and wget [6].
  • Signs of persistence: new or changed .php files in web directories (especially upload folders), new cron jobs or new user accounts.
  • Unexpected outbound connections from web servers.
  • Bursts of large or unusual multipart POST requests in your access logs.

Clean logs don't prove a server is safe [2]. If you find signs of compromise, treat it as an incident: rotate the database passwords and API keys stored on that server, not just the PHP version.

The Bigger Lesson

Legacy code isn't harmless just because it's familiar. The code behind PHP2Shell had run on millions of servers for 14 years, and it was trusted because nothing had gone wrong yet.

Neither bug was in anyone's PHP code. Both were memory-safety bugs in the PHP engine, which is written in C: memory used after it was freed, and data used before it was fully set up. These are the same classes of bug the NSA and CISA have pointed to for years when they urge a move to memory-safe languages for new code [8][9].

Two other points stand out. AI-assisted audits found bugs that had sat in widely used code for over a decade, and attackers have the same tools. And PHP runs a large share of the web, yet the PHP Foundation has only 13 employees and relies on donations [5]. If your business depends on PHP, supporting the foundation is part of keeping it safe.

For now, the steps are simple: find every PHP server, update it, restart it, and check it wasn't hit first.

References

Advisories

Analysis and background

CobuildX helps teams modernise legacy systems with MigrateX.


Originally published on the CobuildX blog.

Top comments (0)