Profile
Back to NewsBack
Dev.to 10 min
Reader Mode
PHP2Shell, explained: the two critical PHP bugs every team needs to patch

PHP2Shell, explained: the two critical PHP bugs every team needs to patch

15 hours ago

On October 21, 2026, PHP shipped an emergency security release for two critical vulnerabilities in the PHP runtime itself. The pair is being called PHP2Shell:

  • CVE-2026-109618
  • CVE-2026-109619

Both have a 9.2 Critical CVSS score. Both can expose vulnerable PHP services to unauthenticated remote code execution and memory disclosure. And neither one is fixed by updating Laravel, WordPress, Symfony, a Composer package, or your application code.

Salman bro

This is the important part:

The vulnerable thing is the PHP interpreter that runs your application.

So yes, the meme is funny. The patch window is not.

First: should you be worried?

You should move this to the top of the queue if you run PHP on an internet-facing service and your runtime is any version from PHP 5.4 through PHP 8.5.11.

That includes more than the obvious php-fpm server in a VM:

  • PHP-FPM behind Nginx or Apache
  • Apache mod_php
  • WordPress, Drupal, Magento, Moodle, and other PHP-based products
  • Docker images built from php:* base images
  • Kubernetes workloads
  • queue workers and scheduled jobs
  • application images deployed to Cloud Run, ECS, App Engine Flexible, or similar platforms
  • vendor software that silently bundles its own PHP runtime

This is why security incidents around language runtimes are awkward: everyone knows where the repository is, but fewer teams can answer which PHP binary is actually serving production traffic right now?

What actually happened?

PHP receives a request, allocates memory, builds internal structures, runs your code, cleans everything up, and does it again for the next request. Most of the time, none of this is visible to an application developer. It should not be visible.

PHP2Shell lives in the parts of that lifecycle you only notice when something has already gone wrong: out-of-memory handling, cleanup, freed memory, and reused workers.

Think of a restaurant kitchen.

Normally, an order comes in, a tray is prepared, food is served, the tray is cleaned, and the next order starts with an empty tray. These vulnerabilities are different flavours of the kitchen being interrupted mid-cleanup—and the next order being served from a tray the system thought was safe, but was not.

That is a deliberately simplified analogy. The practical consequence is not: “your page crashes.” The reported impact is much worse: an unauthenticated attacker may be able to manipulate the runtime into leaking memory or executing code in the PHP process.

The two CVEs, in human language

CVE The technical shape The plain-English version Why defenders care
CVE-2026-109618 During an out-of-memory condition, PHP can be interrupted while request data is only partially initialized. Subsequent cleanup can treat that invalid state as valid. The runtime gets interrupted while setting up the request, then later cleans up a half-built object as if it were complete. The failure path can become an attacker-controlled path.
CVE-2026-109619 Request-owned state can remain reachable after its backing memory has been freed. A later request on the same worker can use stale state before normal initialization resets it. One request leaves behind a poisonous sticky note; the next request on the same worker reads it before the desk is reset. Long-lived, reused PHP workers make cross-request state especially relevant.

The research and vendor advisories use precise memory-management language because the details matter for exploitability. You do not need to reproduce that research to respond safely. The operational conclusion is already clear: update the runtime, restart/redeploy every worker that might still have the old binary or old in-memory state, and verify the version that is actually live.

Why “we use a framework” is not the answer

Framework updates are valuable. They just solve a different class of problem.

A typical framework vulnerability might be triggered by a route, a package feature, an insecure deserialization path, or an app-level configuration choice. With PHP2Shell, the issue is underneath that stack:

Your application
↓
Laravel / WordPress / Symfony / custom code
↓
Composer dependencies
↓
PHP runtime   ← the vulnerable layer
↓
OS or container image

You can have pristine dependencies, a clean SAST report, and still be running an affected PHP interpreter. That is why the right opening question is not “Which repo uses PHP?” It is “Which live workloads execute PHP?”

Which versions are affected—and what fixes them?

The disclosed affected range is PHP 5.4 through PHP 8.5.11.

The initial fixed upstream releases are:

Supported branch Fixed release
PHP 8.5 8.5.12
PHP 8.4 8.4.27
PHP 8.3 8.3.36
PHP 8.2 8.2.35

PHP 8.1 and older are end of life and are not receiving an upstream fix. If you are on one of those branches, the decision is not “which patch do we wait for?” It is “which supported PHP branch can we upgrade to safely and quickly?”

One nuance for distro users: a Linux distribution can backport a security patch while retaining a different-looking upstream version string. Follow your distribution’s security advisory and package changelog, then verify the installed package. For custom builds and official PHP images, move to a fixed upstream release.

The container trap: patching the host may change nothing

This is the moment where many incident checklists quietly fail.

Imagine a host running the latest system PHP package. Great. But your production app is deployed as this:

FROM php:8.3-fpm
COPY . /var/www/html

The PHP process serving requests came from the image, not from the host package manager. Updating the host does not alter the PHP binary already baked into that image.

The real fix path is:

update base image or PHP package
→ rebuild image
→ push a new immutable digest
→ redeploy every workload
→ verify the running container

The same rule applies to queue workers, cron jobs, admin jobs, and one-off worker deployments. They often have a separate Dockerfile, release pipeline, or autoscaling pool.

“The build succeeded” is not the final check. “The running workload uses the patched runtime” is.

A calm, practical response plan

1. Find every runtime

Start by checking what a machine or container is running:

php -v
php --ini

For container fleets, find images that contain PHP, then inspect the deployed digest in your orchestration platform. Search your CI definitions too: the old base image may be pinned in a reusable workflow rather than in the application repository.

Do not forget:

  • background queues
  • scheduled jobs
  • worker pools
  • maintenance scripts
  • staging and pre-production environments that hold production-like data
  • marketplace or vendor products with bundled PHP

2. Patch the runtime

Update to the fixed release for your supported branch—or to a vendor-confirmed package that contains the backported fix. For source builds, use the fixed upstream release. For containers, rebuild from a fixed base image or package set.

3. Restart or redeploy the processes

Installing a package is only half a fix if PHP-FPM or Apache is still running an old process in memory. Restart the relevant service. In container platforms, deploy fresh tasks/pods/instances from the patched image.

This matters especially for a bug involving request-owned state and reused workers: stale, running processes are not where you want to gamble on “probably fine.”

4. Verify the live environment

Check the runtime version from a newly started process, not just from your laptop or CI log:

php -v

For containers, also verify the new image digest is the one your deployment actually pulled. An autoscaler can keep old replicas alive longer than expected; an image tag can be reused; a rollout can partially fail. Those are normal operational problems. They are also how a completed patch turns into an incomplete remediation.

5. Monitor and investigate, without turning this into panic theatre

The FBI’s public guidance recommends prioritizing internet-facing systems, watching for suspicious PHP processes launching command shells or downloading files, and preserving relevant logs if compromise indicators appear. Qualys also reported that Cloudflare had observed attack traffic, which is a good reason to treat external exposure as urgent rather than theoretical.

Useful places to review:

  • web-server access and error logs
  • PHP-FPM / Apache error logs
  • process-execution telemetry from affected hosts and containers
  • WAF and load-balancer events
  • application error-rate and restart telemetry
  • image-scanner results after the rebuild

Unexpected 5xx spikes, repeated process crashes, large unusual request bodies, unexpected shell processes, or unexplained downloads deserve incident-response attention. They are signals to investigate—not proof by themselves that the CVE was exploited.

“Can a WAF save us?”

A WAF can buy time. It cannot patch a vulnerable interpreter.

Cloud guidance for this incident recommends using managed WAF protections and being careful about oversized request bodies while patching. That is reasonable defence in depth, especially for public applications. Roll new rules out in preview/canary mode where feasible, look for application-specific false positives, then enforce them.

But do not let a temporary rule become a reason to defer the update. A rule protects only the traffic it sees, only the shapes it recognizes, and only the applications placed behind it. The runtime update is the remediation.

The posts people are sharing on X

These are real posts published after the disclosure. They are included here because they add useful operational perspective—not because social media is a source of truth. The PHP security advisory and your platform/vendor guidance should drive the actual patch plan.

FBI Cyber Division: treat exposed installations as urgent

Qualys: patch, restart workers, and verify the runtime

Datadog: check versions across the organization

The things teams will get wrong this week

“We updated WordPress, so we are safe.”

Maybe you are safer from a WordPress vulnerability. That is not the same as replacing the PHP binary that runs WordPress.

“We upgraded the server package.”

Excellent—unless the service runs inside a container, an immutable image, a separate worker pool, or a vendor distribution with its own runtime.

“Our scanner says the repository is clean.”

Source scanning does not prove the running infrastructure has the right runtime. Scan the image, check the deployed digest, and verify the live process.

“We are on PHP 8.1, but it is stable.”

Stable is not supported. There is no upstream security release for PHP 8.1 or older here. Upgrade planning just became incident response work.

“We put a WAF rule in front of it.”

Good temporary mitigation. Still not a patch.

Your incident checklist

  • [ ] Identify every production, worker, and scheduled workload that executes PHP.
  • [ ] Confirm whether its PHP runtime is in the affected range.
  • [ ] Update to PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35, or a vendor-confirmed patched build.
  • [ ] Upgrade PHP 8.1 and older to a supported branch.
  • [ ] Rebuild custom images without relying on a stale cache.
  • [ ] Redeploy every workload and restart long-lived PHP processes.
  • [ ] Verify the live runtime version and image digest.
  • [ ] Apply temporary WAF/edge controls while rollout completes.
  • [ ] Review security telemetry and preserve evidence if compromise indicators appear.
  • [ ] Record which environments are verified and which still need work.

The takeaway

PHP is a widely deployed runtime having a serious emergency patch day.

The less funny takeaway is that runtime upgrades cannot live forever in a low-priority maintenance bucket. The moment a core vulnerability lands, the teams that respond well are the ones that already know:

  1. where their runtimes are,
  2. how their images are built,
  3. how to roll out a change everywhere, and
  4. how to prove that the change reached production.

Patch it. Redeploy it. Verify it.

References


Have you checked your worker images yet, or only the web servers?

Chat with me