⚡ TL;DR
php2shell (CVE-2026-109618/109619) made pre-auth RCE a PHP-wide problem. Run this 7-step grep and checklist routine to audit AI-generated PHP before deploy.
php2shell: Auditing AI-Generated PHP for RCE Vulnerabilities
On 21 October 2026, the PHP project shipped emergency security releases for two core-engine bugs the industry now calls php2shell. AI tooling found them, they enable unauthenticated remote code execution, and they touch every PHP release of the past decade. Patching the engine closes that hole. It doesn’t do anything about the AI-generated PHP sitting in your repo, quietly interpolating request data into shell commands. Here’s the audit routine you’d run before your next deploy, plus the context that explains why this class of bug got a name of its own.
- Two memory-safety bugs, CVE-2026-109618 and CVE-2026-109619, enable unauthenticated RCE on PHP releases dating back to 5.4; CVSS tops out at 9.2 (Wiz, October 2026).
- PHP powers roughly 70% of all websites, and a plain “Hello World” app on a vulnerable version was exploitable because the bugs live below the application layer (Wiz, October 2026).
- As of 4 November 2026, NCSC-NL tracked 948 PHP-dependent products with 215 still vulnerable and 93 under investigation (NCSC-NL, November 2026).
- Cloudflare deployed 7 WAF managed rules against known exploit attempts by default, even on its Free plan (Cloudflare, October 2026).
Contents
- What Is the php2shell Vulnerability, Really?
- Why Does AI-Generated PHP Amplify RCE Risk?
- Which Constructs Should You Grep For First?
- What Does Vulnerable vs. Fixed PHP Look Like?
- Can Cloudflare-Style WAF Protections Buy You Time?
- What Goes on Your Pre-Deploy Checklist?
- FAQ
- The Verdict
What Is the php2shell Vulnerability, Really?
php2shell is the shorthand name for two critical vulnerabilities in the PHP core itself, disclosed and patched on 21 October 2026: CVE-2026-109618 and CVE-2026-109619 (NCSC-NL, November 2026). Both are memory-safety flaws. One chain corrupts memory during request cleanup after an out-of-memory error; the other reuses stale request-owned state on a later request handled by the same worker (Wiz, October 2026). A related session-upload-progress issue rounds out the family.
What makes this nastier than a typical CVE: exploitation sits below your application code. Wiz demonstrated pre-authentication RCE on WordPress, Drupal, and Joomla, plus application-agnostic memory disclosure that can leak passwords, tokens, API keys, and certificates. That’s why IEEE Spectrum treated php2shell as a landmark moment for AI in cybersecurity rather than another patch Tuesday.
The AI angle is real, just not the one most people assume. Wiz Research used AI tooling to find and weaponize the bugs; their writeup claims a single researcher did in one week what a world-class team would have needed months to do (Wiz, October 2026). Datadog Security Labs published parallel detection and remediation guidance the same day, and Akamai connected exploitation campaigns to the disclosure within days.
The fixed releases are 8.5.12, 8.4.27, 8.3.36, and 8.2.35, with minimal source backports for PHP 7.4, 8.0, and 8.1 (NCSC-NL, November 2026). Per Wiz, applying the update remains the only complete remediation. Everything else on this page is defense in depth.
Why Does AI-Generated PHP Amplify RCE Risk?
Once the engine is patched, the application layer is your remaining RCE surface, and it has grown fast. Most teams now accept substantial PHP diffs written by coding assistants, and assistants have a specific failure mode: they optimize for “make it work now.”
- Need a thumbnail?
exec("convert $file ...")works on the first try, so it ships. - Models lean on training data full of decade-old snippets that use
eval()andunserialize()without a second thought. - Output is confident and syntactically clean, which lowers review scrutiny per line.
- Volume compounds it: more generated code means more shell sinks landing per sprint, with less human attention on each one.
I’m not claiming every assistant output is a landmine. The base-rate argument is enough: when code production accelerates and per-line review shrinks, dangerous constructs reach main more often. PHP command injection is one of the oldest bug classes in the language precisely because the shell-execution functions are so convenient. The audit routine below exists to restore the scrutiny that volume removed.
Which Constructs Should You Grep For First?
Start with the sinks. These are the constructs that execute something or pull in something executable.
| Construct | Why it’s dangerous | Grep pattern |
|---|---|---|
exec, system, shell_exec, passthru, proc_open, popen | Runs a shell command | \b(exec|system|shell_exec|passthru|proc_open|popen)\s*\( |
| Backticks | Alias for shell_exec | Backtick characters with interpolation |
eval, assert | Executes a string as PHP | \b(eval|assert)\s*\( |
unserialize on request data | Object injection via POP gadget chains | \bunserialize\s*\( |
include/require with variables | Local or remote file inclusion | \b(include|require)(_once)?\s*\(?\s*\$ |
preg_replace with /e | Code execution on PHP < 7 | preg_replace.*['"].*/e |
create_function, call_user_func with input | Dynamic function invocation | \b(create_function|call_user_func(_array)?)\s*\( |
Copy-paste battery for a quick first pass:
# 1. Shell execution sinks
grep -rnE '\b(exec|system|shell_exec|passthru|proc_open|popen)\s*\(' app/ src/
# 2. eval and friends
grep -rnE '\b(eval|assert)\s*\(' app/ src/
# 3. unserialize fed straight from request data
grep -rnE 'unserialize\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)' app/ src/
# 4. Variable include/require (file inclusion candidates)
grep -rnE '\b(include|require)(_once)?\s*\(?\s*\$' app/ src/
# 5. Backtick execution
grep -rn '`' app/ src/A hit is a sink, not proof of exploitability. Each one needs a single question answered: can attacker-controlled request data reach the argument, directly or through a config value the attacker influences? If yes, it’s a finding. If it’s fed only from a hardcoded constant, log it and move on.
What Does Vulnerable vs. Fixed PHP Look Like?
Here’s the pattern I’d expect an assistant to produce when you ask for “resize uploaded images.” It works. That’s the problem.
// Vulnerable: request data interpolated straight into a shell command.
$format = $_GET['format'] ?? 'png';
$file = $_GET['file'] ?? 'upload.png';
exec("convert uploads/{$file} -resize 800x600 uploads/thumb.{$format}", $out, $code);
if ($code !== 0) {
throw new RuntimeException("Conversion failed for {$file}");
}A request like ?file=x.png;cat .env makes the shell run two commands. The attacker reads your secrets, drops a webshell next to the uploads directory, and moves laterally from there.
// Fixed: no shell, allowlisted inputs, basename-truncated path.
$format = in_array($_GET['format'] ?? 'png', ['png', 'jpg', 'webp'], true)
? $_GET['format']
: 'png';
$file = basename($_GET['file'] ?? 'upload.png');
if (!is_file(__DIR__ . '/uploads/' . $file)) {
throw new InvalidArgumentException('Unknown file');
}
// First choice: skip the shell entirely with a library.
$imagick = new Imagick(__DIR__ . '/uploads/' . $file);
$imagick->resizeImage(800, 600, Imagick::FILTER_LANCZOS, 1);
$imagick->writeImage(__DIR__ . "/uploads/thumb.{$format}");If a binary call is genuinely unavoidable, wrap every dynamic argument in escapeshellarg(), truncate paths with basename(), and allowlist the values before they get anywhere near exec(). The fixed version’s hierarchy matters: removing the shell beats escaping the shell. Escaping is one upstream refactor away from disappearing.
Can Cloudflare-Style WAF Protections Buy You Time?
Partially. Cloudflare deployed default protections against the two php2shell CVEs ahead of public disclosure, with 7 managed rules covering known exploit attempts available even on the Free plan (Cloudflare, October 2026). AWS shipped parallel managed rules for PHP workloads, and Datadog Security Labs published detection queries alongside the CDN vendors.
The detection signals published at disclosure are worth wiring into your own alerting: unusual multipart requests followed by memory-limit errors, repeated HTTP 500s, oddly large or malformed PHPSESSID cookies, and unexplained PHP worker churn (Wiz, October 2026).
Limits matter, though. php2shell exploits primitives below the application layer, so generic WAF signatures don’t reliably match every variant, and the vendor guidance is explicit that rules are interim mitigation. A WAF also does nothing for application-level command injection in generated code, though it will catch the crudest ;cat .env payloads. Enable the rules as a seatbelt while you patch. Never instead of patching.
What Goes on Your Pre-Deploy Checklist?
- Confirm production runs 8.5.12, 8.4.27, 8.3.36, or 8.2.35, or a backported 7.4/8.0/8.1 build (NCSC-NL, November 2026).
- Set
session.upload_progress.enabled=0until you’re past the next bugfix release (NCSC-NL, November 2026). - Run the grep battery above and ticket every sink with a “can request data reach this?” verdict.
- Review AI-generated PRs line by line for shell calls,
eval,unserializeon request data, and variable includes. - Turn on your WAF’s php2shell managed rules and confirm they’re active, not just available.
- Alert on the multipart-plus-memory-limit and worker-churn signals (Wiz, October 2026).
- Audit third-party PHP images and appliances separately. NCSC-NL tracked 948 products, and 215 were still vulnerable as of 4 November 2026.
FAQ
Is php2shell a single CVE?
No. It’s two core PHP bugs, CVE-2026-109618 and CVE-2026-109619, plus a related session-upload-progress issue. All are memory-safety flaws enabling unauthenticated RCE or memory disclosure across PHP 5.4 through 8.5.
Did AI write the vulnerable code?
Not in this case. The bugs live in the PHP engine, not in anyone’s generated application code. AI tooling found and weaponized them. Separately, AI assistants do reintroduce classic application-level injection patterns, which the grep routine catches.
Can I skip patching if my WAF rules are enabled?
No. Vendors treat WAF rules as interim mitigation, and applying the 21 October 2026 PHP security release is the only complete remediation. Managed rules can miss exploit variants; the patch eliminates all known ones at the engine level.
Which PHP versions are safe?
8.5.12, 8.4.27, 8.3.36, or 8.2.35, all released 21 October 2026. If you’re pinned to 7.4, 8.0, or 8.1, use the minimal source backports NCSC-NL’s repository links to, and keep interim mitigations active.
How often should I rerun the audit?
Every deploy, honestly, for AI-generated diffs, since each PR is new attack surface. Run the full-codebase grep battery quarterly, or whenever a named class like php2shell shows what the ecosystem is actively being exploited for.
The Verdict
- Patch first. The engine fix is the only complete remediation for php2shell, and unpatched PHP 5.4–8.5 hosts are pre-auth RCE with a CVSS of 9.2.
- Assume generated code contains sinks. The grep battery takes minutes, and every hit it surfaces needs a one-question triage.
- Prefer removing the shell over escaping it.
Imagickbeatsexec("convert ...")on every axis, including the one where refactors stop breaking your escaping. - Layer the WAF, don’t lean on it. Cloudflare’s 7 default rules and the published detection signals buy you time, not safety.
- Audit the edges too. NCSC-NL’s tracking shows the slowest-moving part of this vulnerability class is third-party appliances and images, not your own app.
If this reads like familiar ground, it should. The core lesson matches what I argued across my three-part series on securing AI agents: why the prompt-injection analogy holds, how defense in depth and input validation actually work in code, and what production hardening and monitoring look like when the checklist is real. AI accelerates both sides of security. The audit routine is how you keep your side of the acceleration.

Written by Mihir Pipermitwala
Senior Full Stack Consultant & Mobile Engineering Specialist building user-focused applications in healthcare with Flutter, Dart and React Native. I write about what I actually build and test — every tutorial here comes from real project work.
More about Mihir →