Runtime-Level PHP Vulnerabilities: Why PHP2Shell Wasn't the First, and What They Have in Common
Direct answer: PHP2Shell wasn't PHP's first runtime-level vulnerability. CVE-2019-11043 (PHP-FPM on nginx), the 2021 php-src backdoor attempt and CVE-2024-4577 (php-cgi on Windows) all hit PHP itself rather than application code. They share three patterns: memory-safety bugs in C, attacks that land before your application code runs, and state or configuration shared across requests. Each time, the fix came quickly. The damage happened on servers that patched late or ran unsupported versions.
Most PHP security advice is about application code: SQL injection, XSS, unsafe uploads. That's still where most incidents happen. But every few years a bug in the runtime itself reminds everyone that a perfectly written Laravel app is only as safe as the PHP binary running it. This post is part of my PHP in 2026 series.
Four incidents, side by side#
| Incident | Year | Where the bug was | Fix and response |
|---|---|---|---|
| CVE-2019-11043 | 2019 | PHP-FPM, triggered by a common nginx configuration | Reported in September, patched in 7.1.33 / 7.2.24 / 7.3.11 in October. Exploited in the wild within days of the public exploit. |
| php-src backdoor attempt | 2021 | The project's own git server | Malicious commits caught and reverted within hours. The repository moved to GitHub the next day. No release ever contained the code. |
| CVE-2024-4577 | 2024 | php-cgi on Windows, via character-encoding conversion | Reported May 7, patched June 6 in 8.3.8 / 8.2.20 / 8.1.29. Ransomware campaigns followed within days. |
| PHP2Shell (CVE-2026-109618/9) | 2026 | Core engine: out-of-memory handling and stale request state | Coordinated embargo, leak, and four branches patched on October 21. Listed as actively exploited five days later. |
2019: CVE-2019-11043, PHP-FPM and nginx#
A widely copied nginx configuration used fastcgi_split_path_info with a regular expression that a newline character (%0a) could break. With a crafted URL, PHP-FPM computed an empty PATH_INFO and a negative length, writing before the start of a buffer. That buffer underflow was turned into remote code execution.
What made it dangerous: the vulnerable config came from tutorials and hosting panels, so it was everywhere, and the attack worked before any application code ran. Nginx's own guidance was blunt: the bug was in PHP-FPM, so the only real fix was upgrading. Adding try_files $fastcgi_script_name =404; was the stopgap.
2021: the php-src backdoor attempt#
On March 28, 2021, two commits appeared in php-src on the project's self-hosted git.php.net server, written to look like they came from Rasmus Lerdorf and Nikita Popov. They added code that would execute PHP sent in a specially named HTTP header, disguised as a typo fix. Core developers spotted and reverted them within hours. The attacker pushed again, and that commit was reverted too.
The project's response is the part worth remembering. Maintainers decided that running their own git infrastructure was an unnecessary risk, and on March 29 the GitHub mirror became the canonical repository, with commit access tied to GitHub accounts and two-factor authentication. No PHP release ever shipped the backdoor. It's one of the clearest examples of an open-source project catching a supply-chain attack in time, because people actually read the commits.
2024: CVE-2024-4577, php-cgi on Windows#
Orange Tsai of DEVCORE found that Windows' "Best-Fit" character conversion could turn certain characters in a URL into a hyphen. That let attackers smuggle command-line arguments into php-cgi, bypassing the fix for a 2012 bug of the same kind (CVE-2012-1823). Any Windows server running PHP in CGI mode was exposed, including XAMPP's default setup.
The fix shipped on June 6, 2024, about a month after private disclosure. Within days, the TellYouThePass ransomware group was exploiting unpatched servers. PHP 8.0 and older, already end-of-life, got no fix.
A related lesson: bugs underneath PHP#
Also in 2024, a buffer overflow in glibc's iconv() (CVE-2024-2961) was shown to be exploitable through PHP's php://filter stream chains. The bug wasn't in PHP, but PHP provided a path to it. Patching PHP isn't enough. The operating system and its libraries are part of your runtime too, which is one more reason to rebuild container images regularly rather than only when PHP releases.
What these vulnerabilities have in common#
1. Memory safety in C#
PHP's engine is written in C. Buffer underflows, use-after-free and half-initialised memory are the classic C failure modes, and CVE-2019-11043, PHP2Shell and the glibc bug are all exactly that. Your PHP code is memory-safe; the interpreter underneath it doesn't get that guarantee for free.
2. The attack lands before your code runs#
CVE-2019-11043, CVE-2024-4577 and the worst PHP2Shell case on mod_php all exploited the layer between the web server and PHP: request parsing, path handling, command-line arguments, upload processing. Application-level defences such as validation, CSRF protection and framework middleware never get a chance to run.
3. Configuration and shared state#
Each bug needed a specific setup: an nginx regex, CGI mode on Windows, per-directory INI settings, session upload progress. And PHP2Shell's stale-state bug lived in memory reused between requests in the same worker. That's increasingly relevant as more teams move to long-running runtimes, which I compare in PHP runtimes explained.
4. The fix is fast; patching is slow#
In every case PHP shipped a fix within weeks, and with PHP2Shell, within hours. The exploitation that followed hit servers that hadn't updated, and especially servers on end-of-life versions that never got a fix at all.
How PHP's security process has matured#
- Infrastructure: after 2021 the canonical source moved to GitHub, with security advisories published as GitHub Security Advisories (GHSA) alongside CVEs.
- Funding: since 2021, the PHP Foundation has paid core developers to work on the engine full-time, including security fixes and reviews.
- Coordination: PHP2Shell showed a full embargo process: early patches to distributions and LTS vendors, honest advisories, and a release pulled forward when details leaked.
- Predictable support: each branch gets two years of active support plus two years of security fixes, published on php.net/supported-versions.
Hardening that limits the damage next time#
You can't prevent the next runtime bug, but you can make it far less damaging:
- Stay on a supported version. Today that's PHP 8.2 or later, and 8.2 drops out on December 31, 2026. See the upgrade playbook.
- Make patching boring. One base image, reproducible builds, and a deploy you trust. That's what let us patch everything within 24 hours during Dashain.
- Run PHP as an unprivileged user with a read-only filesystem for application code.
- Block outbound network access from PHP servers unless it's needed. Most exploit chains download a second stage.
- Prevent PHP from spawning shells (
sh,curl,wget) and alert when it tries. - Avoid legacy setups: no
php-cgiexposed to the web, no per-directory INI overrides you don't need, and minimal enabled extensions. - Put a WAF in front. On disclosure day it buys you hours while you patch.
For the application-level basics, see security basics every startup should get right.
So, is PHP secure?#
As secure as any widely deployed runtime written in C, which means it has bugs, gets them fixed quickly, and is safe in practice when you stay on supported versions and patch promptly. Node.js, Python and Ruby have had serious runtime CVEs too. The difference between a footnote and an incident is almost always how long the vulnerable version stayed in production.
Key takeaways#
- PHP2Shell is the latest in a line of runtime-level bugs: CVE-2019-11043, the 2021 backdoor attempt and CVE-2024-4577.
- Common patterns: C memory-safety bugs, attacks that land before application code runs, and configuration or state shared between requests.
- PHP's fixes have arrived quickly every time. Exploitation hit late patchers and end-of-life versions.
- The best defence is a supported version, fast reproducible rebuilds, least-privilege workers, and no unnecessary legacy setups.