READ
Results
📝
PHP2Shell Explained: How Two PHP Bugs Knocked Websites Offline Worldwide (CVE-2026-109618 & CVE-2026-109619)
Security · 13 min
↩
📝
How I Built a Coding Agent from Scratch — AI Agent Architecture Explained
AI & LLMs · 16 min
↩
📝
How I Built Mini-GPT from Scratch
AI & LLMs · 14 min
↩
📝
The Problem with Modern Incident Response (And How AI Fixes It)
AI & LLMs · 14 min
↩
📝
What Happens When a User Writes Something in the Chatbox?
AI & LLMs · 18 min
↩
📝
From Basic RAG to an Agentic Ecosystem: Multi-Agent, GraphRAG & Generative UI
AI & LLMs · 14 min
↩
📝
How I Built an Enterprise AI Assistant Using RAG and Mistral LLM
AI & LLMs · 12 min
↩
📝
Agentic AI in Low-Code Platforms: Is the Future Closer Than We Think?
AI & LLMs · 8 min
↩
📝
Unlocking the Future of Low-Code: What's New in Appian 25.2
Appian · 15 min
↩
📝
What’s New in Appian 25.3: A Deep Dive into the Future of Low-Code
Appian · 14 min
↩
📝
10 Must-Know Data Modeling Best Practices for Appian Developers
Appian · 18 min
↩
📝
Performance Optimization in Appian: Top 10 Proven Tips That Actually Work
Appian · 10 min
↩
📝
Mastering Web API Design in Appian: Best Practices with Validations & Real-World Tips
Appian · 16 min
↩
📝
How Generative AI Is Transforming Business in the BFS Sector
AI & LLMs · 7 min
↩
👤
About Gopal
Page
↩
📧
Subscribe to Newsletter
Action
↩
🌗
Switch Theme
Action — try Chalk or Dusk
↩
⚡ SecurityAdvanced●November 1, 2026●13 min read

PHP2Shell Explained: How Two PHP Bugs Knocked Websites Offline Worldwide (CVE-2026-109618 & CVE-2026-109619)

In October 2026, two bugs deep inside PHP — the language behind most of the web — pushed governments, hospitals and businesses to pull their websites offline. Here's what PHP2Shell actually is, how the memory bugs work in plain English, why it caused so much chaos, and exactly how to check and patch your servers.

AuthorGopal Kumar
PublishedNov 1, 2026
Read time13 min
DifficultyAdvanced
Fig. 0 — PHP2Shell Explained: How Two PHP Bugs Knocked Websites Offline Worldwide (CVE-2026-109618 & CVE-2026-109619)
01

On the morning of October 21, 2026, something strange happened across Europe. Government tax portals went dark. Hospital websites showed maintenance pages. University and transport sites in the Netherlands, Finland, France, Germany and the Czech Republic quietly disappeared. Nobody had hacked them. Their own administrators had pulled the plug.

The reason was two bugs in PHP — not in WordPress, not in some plugin, but in the PHP language runtime itself. Security folks nicknamed the pair PHP2Shell, and they're tracked as CVE-2026-109618 and CVE-2026-109619. Both scored 9.2 out of 10 on CVSS. Together they let an attacker run code on a server without logging in.

I spent the last week reading advisories from the PHP project, CISA, the UK's NCSC, CERT-In, Red Hat and a handful of security vendors, trying to understand what actually broke. Most coverage I found was either "patch now!!" headlines or dense C-level write-ups. This post is the middle ground: what happened, how the bugs work (explained so you don't need to know C), why a two-line advisory turned into a global scramble, and what you should do about it.

TL;DR — If you run PHP anywhere, upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (or your distro's backported package) and restart PHP-FPM / Apache. Updating WordPress, Drupal or Magento alone does not fix this. PHP 8.1 and older get no official patch.

01 —1. What Is PHP2Shell?

PHP2Shell is the name for two related memory-safety bugs in PHP's core engine. Both are about what happens when a request dies badly — when PHP runs out of memory or hits a fatal error halfway through doing something.

  • CVE-2026-109618 — the out-of-memory bug. When a script hits its memory limit (for example, while processing a big file upload), PHP aborts immediately. If that happens while some internal data is only half built, the cleanup code later treats that half-built data as if it were valid. That's memory corruption.
  • CVE-2026-109619 — the stale state bug. After a fatal error, some internal "global" state belonging to the dead request survives, even though the memory behind it has already been freed. The next request handled by the same worker process can then use that stale state. That's a classic use-after-free.

On their own, each bug is a crash waiting to happen. In the hands of a skilled attacker, they become unauthenticated remote code execution (RCE) — the worst category of vulnerability there is. The attacker's request hits the PHP engine before your application code even runs, so your login page, your CMS permissions and your security plugin never get a say.

02 —2. Why a PHP Bug Is a Global Problem

It's easy to think of PHP as "that old language". The numbers say otherwise. PHP is commonly estimated to run on roughly 70% of websites whose server-side language is known. WordPress alone powers a huge slice of the internet, and it sits on PHP. So do Drupal, Joomla, Magento, MediaWiki (the software behind Wikipedia), Laravel apps, and a long tail of internal enterprise tools that nobody has touched in years.

When the bug is in a plugin, only sites using that plugin are at risk. When the bug is in the language runtime, the blast radius is basically "everything running PHP". That's the key difference that made PHP2Shell feel like Log4Shell all over again — and yes, the name is a deliberate nod to it.

03 —3. The Timeline: Four Days Too Late

Here's how things played out, pieced together from vendor reports and news coverage:

Fig. 1 — How PHP2Shell Unfolded (2026)
Timeline of the PHP2Shell vulnerability from discovery in August 2026 to recovery in late October 2026 AUG Bugs found in code audit (AI-assisted) OCT 17 First exploit attempts seen (honeypots) OCT 20 Details leak publicly OCT 21 Patches ship + mass scanning sites go offline LATE OCT Services return after patching Attackers were active 4 days before the fix existed
  • August 2026 — The bugs were reportedly found during a security research effort involving Calif, Wiz and OpenAI's "Patch the Planet" project, with AI-assisted source code auditing playing a big part. They were reported privately to the PHP security team.
  • October 17 — According to threat intelligence reports citing Amazon's honeypot network, the first exploitation attempts appeared in the wild. That's four days before any patch existed.
  • October 20 — Details and CVE IDs leaked through pseudonymous posts on community forums, including Hacker News. The coordinated disclosure plan was effectively over.
  • October 21 — The PHP project shipped emergency releases. Cloudflare and others reported millions of automated scans and exploit attempts within hours. National agencies (CISA in the US, NCSC in the UK, CERT-In in India, and several European CERTs) issued urgent alerts.
  • October 21–22 — The "worldwide outage" phase: organizations that couldn't patch immediately took public-facing PHP systems offline.
  • Late October — Services came back gradually as teams verified patches or mitigations.

That gap between the first attacks and the fix is what turned a serious bug into a crisis. Normally, defenders get a head start. This time, attackers did.

04 —4. Under the Hood: How PHP Handles a Request

To understand the bugs, you need one mental model of how PHP runs in production. Bear with me — this is the part that makes everything else click.

In most setups (Nginx + PHP-FPM, Apache + mod_php, LiteSpeed, FrankenPHP), PHP doesn't start a fresh process for every visitor. That would be far too slow. Instead, there's a pool of long-lived worker processes. Each worker handles a request, cleans up, and then picks up the next request. One worker might serve hundreds or thousands of requests before it's recycled.

Every request goes through roughly three phases:

  1. Startup — set up per-request state: superglobals like $_POST and $_FILES, session data, memory pools.
  2. Execution — run your PHP script.
  3. Shutdown — free everything that belonged to the request, so the worker is clean for the next one.

The whole model depends on one promise: whatever happens in request A stays in request A. PHP2Shell breaks that promise.

The emergency exit: longjmp()

PHP's engine is written in C. When something goes really wrong — out of memory, a fatal error — PHP can't politely unwind like an exception in JavaScript or Python. Instead it uses a C feature called setjmp/longjmp. Think of it as a teleporter: setjmp marks a safe spot, and longjmp instantly jumps back to it, skipping everything in between.

Here's a simplified illustration of the pattern (this is not the actual PHP source, just the shape of the problem):

/* Simplified illustration only — not real PHP source */

if (setjmp(bailout_point) == 0) {
    // Normal path
    upload = allocate_upload_struct();   // memory reserved...
    parse_multipart_body(upload);        // ...OOM can strike HERE
    upload->initialized = 1;             // never reached if we bail out
} else {
    // We teleported back here after a fatal error / OOM
}

// Shutdown: assumes every struct is in a sane state
free_upload(upload);   // operates on half-built data

The teleport is fast and simple, but it's also dangerous. Any code between the safe spot and the crash simply never finishes. If that code was in the middle of building a data structure, you now have a half-built structure lying around — and the cleanup code doesn't know that.

Fig. 2 — One PHP Worker, Two Requests, Two Bugs
A PHP worker handling request A which crashes mid-setup, leaving corrupted and stale state that request B then uses PHP-FPM WORKER PROCESS (long-lived, reused) REQUEST A (attacker) Startup allocate state Parse upload half-initialized data 💥 OOM / fatal longjmp() bail out Cleanup trusts junk data CVE-2026-109618 cleanup frees/uses memory that was never set up Global pointer still points at freed memory the worker survives, and so does the stale state REQUEST B (next request, same worker) Reads stale pointer → attacker-controlled memory CVE-2026-109619 — use-after-free → remote code execution

05 —5. Bug #1 Explained: CVE-2026-109618 (Out-of-Memory Corruption)

Imagine a restaurant kitchen. A chef starts plating a dish, gets halfway, and the fire alarm goes off. Everyone runs out. When the cleaning crew comes in later, they see a plate and assume it's a finished dish, so they handle it like one — and things go wrong because half the "dish" is raw ingredients and the other half is missing.

That's CVE-2026-109618. An attacker sends a request designed to push PHP past its memory_limit at exactly the wrong moment — for example, during multipart upload parsing. PHP bails out via longjmp(). Later, the request cleanup code walks over the half-initialized data, treats garbage values as real pointers and lengths, and corrupts memory.

Memory corruption on its own usually causes a crash. But a skilled attacker who can control what the garbage looks like can steer that corruption into overwriting something useful — and eventually into running their own code.

06 —6. Bug #2 Explained: CVE-2026-109619 (The Poisoned Worker)

This one is sneakier, and in my opinion the more interesting of the two.

Remember that workers are reused. After a fatal error, PHP is supposed to wipe all request-specific state. CVE-2026-109619 is a case where some global state survives the crash while the memory it points to has already been freed. The pointer is like a hotel key card for a room that's been checked out. The room might now belong to someone else entirely.

Now the next request arrives at that same worker. PHP reads the stale global state and follows the pointer into freed memory. If the attacker managed to place their own data in that memory (a technique called heap grooming), PHP is now reading attacker-controlled values and acting on them. That's how a use-after-free turns into code execution.

There's an extra nasty side effect: because the worker serves other people's requests too, researchers warned the stale state could also leak memory from other users' requests — session tokens, database credentials, API keys. Even without full code execution, that's a serious breach.

Why session upload progress kept coming up

Several advisories pointed to session.upload_progress.enabled as a useful mitigation. This PHP feature tracks file upload progress inside the session, which means it touches session state and upload parsing at the same time — two of the exact areas involved in the bugs. It's enabled by default, and most apps never use it. Turning it off removes an easy path for attackers to shape memory before triggering the crash.

07 —7. Why It Caused Havoc (Not Just Headlines)

Plenty of critical CVEs come and go without anyone outside security noticing. PHP2Shell was different, for a few reasons that stacked on top of each other:

  • Exploitation before patches. With attacks spotted on October 17 and a leak on October 20, defenders started the race already behind.
  • No authentication required. Any public PHP endpoint was a possible entry point. Login walls, CMS roles and security plugins didn't help.
  • "Patch" really meant "patch + restart + rebuild". Updating the PHP package on disk does nothing while old worker processes keep running vulnerable code in memory. And in Docker or Kubernetes, updating the host OS doesn't touch the PHP inside your container images — those had to be rebuilt and redeployed.
  • Package mirrors buckled. With millions of servers trying to update at the same moment, package repositories and mirrors (Ubuntu's Launchpad among them) were reported to be badly congested. Some teams literally couldn't download the fix fast enough.
  • Forgotten PHP everywhere. Admin panels, old marketing microsites, vendor appliances with PHP bundled inside. Many organizations discovered PHP servers they didn't know they had.
  • End-of-life versions. PHP 8.1 and older got no official fix. For teams stuck on old versions, the only safe choices were an emergency upgrade or going offline.
  • AI on both sides. AI-assisted auditing helped find the bugs in the first place. On the other side, Palo Alto Networks' Unit 42 reported attackers using automated, agent-style tooling to go from exploit to post-exploitation (harvesting password hashes, attempting privilege escalation) within hours.

Put all of that together and the "outage" makes sense. Government IT teams weren't being dramatic. When you can't patch in the next hour, and attackers are scanning everything, turning the website off is the responsible move.

08 —8. Am I Affected? A Quick Checklist

You're potentially affected if you run PHP in any of these ways — Apache mod_php, PHP-FPM, php-cgi, LiteSpeed or FrankenPHP — on a version older than the fixed releases below.

PHP Branch Fixed Version Status
8.5 8.5.12+ Patched
8.4 8.4.27+ Patched
8.3 8.3.36+ Patched
8.2 8.2.35+ Patched
8.1 and older None End-of-life — upgrade or isolate

Linux distributions like Red Hat, Debian and Ubuntu often backport security fixes into older-looking version numbers. So if your distro package says something like 8.2.x with a distro suffix, check your vendor's advisory for CVE-2026-109618 / 109619 rather than trusting the version number alone.

To check what you're running:

# CLI version (note: may differ from what your web server uses!)
php -v

# What PHP-FPM is actually running
php-fpm -v        # or php-fpm8.3 -v, depending on your distro

# Inside a container
docker exec -it <container> php -v

# Is upload progress tracking on? (it is by default)
php -i | grep session.upload_progress.enabled

On WordPress you can also go to Tools → Site Health → Info → Server to see the PHP version your site is really using. That's the one that matters, not the one your terminal shows.

09 —9. How to Fix It (and What Only Helps)

Fig. 3 — Layers of Defense (only one is a real fix)
Nested defense layers: WAF at the edge, rate limits, PHP configuration hardening, and the patched PHP runtime at the core EDGE — WAF virtual patch (Cloudflare, AWS, Azure…) Blocks known exploit shapes. Bypassable by mutated payloads. RATE LIMITS on upload / heavy endpoints Slows down automated memory-exhaustion probing. PHP CONFIG — shrink the attack surface upload_progress off · small upload limits · low pm.max_requests ✅ PATCHED PHP RUNTIME 8.5.12 · 8.4.27 · 8.3.36 · 8.2.35 + restart every worker

The real fix: upgrade and restart

  1. Inventory everything. Every server, container image, VM and appliance that ships PHP. This is the step most teams underestimated.
  2. Upgrade PHP to a fixed version, or install your distro's patched package.
  3. Restart every PHP process. Old workers keep vulnerable code in memory until they die.
  4. Rebuild container images from an updated base image and redeploy. Patching the host is not enough.
  5. Check for compromise. If you were exposed between October 17 and your patch date, look for new admin accounts, unfamiliar PHP files in upload folders, unexpected cron jobs and outbound connections. Rotate database passwords and API keys that live in PHP config files.
# Debian / Ubuntu example
sudo apt update && sudo apt install --only-upgrade php8.3 php8.3-fpm
sudo systemctl restart php8.3-fpm
sudo systemctl restart nginx   # or apache2

# RHEL / Rocky / Alma example
sudo dnf update php php-fpm
sudo systemctl restart php-fpm httpd

Mitigations while you schedule the upgrade

These buy time. None of them close the hole, and every advisory I read was clear on that.

; php.ini — reduce the attack surface
session.upload_progress.enabled = Off   ; most apps don't need it
upload_max_filesize = 8M                ; as small as your app allows
post_max_size = 8M

; php-fpm pool (www.conf) — recycle workers more often
; limits how long a "poisoned" worker can live
pm.max_requests = 200
  • WAF rules. Cloudflare pushed emergency rules to all plans, including free, before the official disclosure. AWS and Azure WAF followed. These block known exploit patterns, but attackers mutate payloads to slip past them.
  • Rate limiting on upload endpoints and anything memory-heavy, to slow down automated probing.
  • Take it offline if it's internet-facing, unpatchable (EOL PHP), and not critical. That's exactly what many governments did.

10 —10. Lessons for Every Engineer — Even If You Don't Write PHP

I don't run PHP on this blog (it's Next.js), but PHP2Shell still changed how I think about a few things:

  • Error paths are attack surface. Both bugs live in the code that runs when things go wrong. We test the happy path obsessively and the crash path almost never. Attackers know that.
  • Long-lived workers need strict isolation. Any system that reuses processes, connections or containers between users — Node servers, Python workers, connection pools — has the same basic risk: state leaking from one request to the next.
  • Memory safety is not an academic debate. This is the kind of bug that memory-safe languages like Rust, Go or managed runtimes rule out by design. It's no surprise new infrastructure is moving that way.
  • You can't patch what you don't know you run. An up-to-date inventory (or SBOM) of runtimes and base images is what separated the teams that patched in hours from the teams that went dark for days.
  • Restarting is part of patching. "We installed the update" and "we're no longer vulnerable" are not the same sentence.
  • AI has shortened the clock. AI-assisted auditing found these bugs, and AI-assisted tooling helped weaponize them. The time between disclosure and mass exploitation is now measured in hours. Your patching process has to keep up.
  • Edge protection earns its keep. Having a CDN/WAF in front of your origin gave many sites a few critical hours of cover. It's not a fix, but it's a very good seatbelt.

11 —11. PHP2Shell FAQ

Is WordPress vulnerable to PHP2Shell?

WordPress itself doesn't contain the bug, but any WordPress site running on an unpatched PHP version is exposed. Updating WordPress core, themes or security plugins does not help. You need to update PHP on the server (or ask your host to).

Do I really need to restart PHP-FPM after updating?

Yes. Running workers keep the old, vulnerable code in memory. Until you restart PHP-FPM (or Apache, if you use mod_php), you're still running the vulnerable version even though the new one is installed.

Does a WAF like Cloudflare fully protect me?

No. WAF rules block known exploit patterns and buy time, but they can be bypassed with modified payloads. They're a mitigation, not a fix.

I'm on PHP 8.1 or older. What now?

There's no official upstream patch for end-of-life versions. Your options are to upgrade to a supported branch (8.2 or newer), use a commercial extended-support vendor that has shipped a fix, or take the service off the internet until you can upgrade.

Is my Node.js, Python or Next.js site affected?

Not directly. PHP2Shell is a PHP runtime bug. But check whether anything else in your stack — an admin panel, a legacy CMS, a phpMyAdmin instance, a vendor appliance — quietly runs PHP.

What do CVE-2026-109618 and CVE-2026-109619 mean?

They're the official identifiers for the two bugs. 109618 is the out-of-memory corruption bug; 109619 is the stale-state use-after-free bug. Both are rated 9.2 (critical) on CVSS.

12 —Final Thoughts

The scariest part of PHP2Shell wasn't the bug itself. Critical memory bugs are found in big C codebases every year. What made this one different was the speed: attackers were active before a patch existed, details leaked a day early, and AI-assisted tooling turned a proof of concept into mass exploitation within hours.

The organizations that came through it cleanly weren't the ones with the fanciest security tools. They were the ones who knew exactly where PHP ran, could rebuild and redeploy in minutes, and had something at the edge to buy them time. That's not a PHP lesson. That's an engineering lesson.

If you haven't checked your servers yet, do it today. Run php -v, check what your web server actually uses, upgrade, and restart.

Let's build smart. Let's build together.

— Gopal

Keep
Reading

More from the archive
© 2026 Ai TechSavvy. All rights reserved.Crafted by Gopal Kumar