Optimum Web
Security 9 min read

PHP2Shell in Docker and Kubernetes: Your Host Is Patched, Your Images Probably Aren't

Last verified: 24 October 2026

Quick Answer: To fix PHP2Shell in Docker and Kubernetes, rebuild every image that contains PHP on a fixed base and redeploy it. Patching the host does nothing, because PHP lives inside the image. Use official php:8.5.12, 8.4.27, 8.3.36 or 8.2.35 or later; no maintained 8.1 tags exist. Redeploy CronJobs, workers, sidecars and staging too, verify the version in each pod, pin by digest and block old digests.

Why doesn't patching the host fix PHP2Shell in Docker and Kubernetes?

A container brings its own userland. The php binary inside php:8.3-fpm was compiled into the image when it was built. The host's package manager cannot see it. Your node can run a fully patched Ubuntu with a fixed php8.3 package while every pod on it serves traffic from PHP 8.3.30.

Two common assumptions make this worse:

  • "We restarted the containers." A restart creates a new container from the image the tag pointed to *when it was pulled*. If the node cached php:8.3-fpm in July, it still runs the July build.
  • "Our tag is floating, so we get updates automatically." Floating tags such as 8.3-fpm move in the registry, but only a new build of your image picks up the new base. Only a new deployment with imagePullPolicy: Always or a changed tag pulls it to the nodes.

The bugs fire inside PHP before your application code runs, so a hardened container does not stop them. Container isolation limits what an attacker reaches after remote code execution (RCE); it does not prevent it. How the bugs work, and why php -v can mislead on distro-built PHP, is covered in our PHP2Shell explainer.

Is PHP2Shell being exploited against containers yet?

Attack traffic is heavy, and it does not care whether PHP runs in a pod or on a VM. This is what was public by 24 October 2026:

DateWhat was reportedSource
21 OctCISA urged immediate patching and said it had not yet seen exploitationCISA alert
21-22 OctCloudflare measured millions of attempts by early 22 OctoberThe Hacker News
22 OctA VulnCheck chain beat address space layout randomisation (ASLR) via a memory leak; RCE on x86-64 PHP 8.4.26, display_errors=off no helpThe Register
22-23 OctSophos and Arctic Wolf saw the operators of Potassium, a distributed denial-of-service (DDoS) botnet built on Mirai code, attempt to enlist PHP servers; those attempts had failed so farCryptopolitan
23 OctCensys put the count of vulnerable PHP services above 34.4 millionThe Hacker News
24 OctImperva logged 482,342 requests in 122,311 sessions against 21,998 sites in 103 countries over 24 hoursCryptopolitan

Confirmed: mass scanning and exploitation attempts, and a working exploit chain in a vendor's hands. Not confirmed in reporting available on 24 October: any successful compromise of a production system. Many public proof-of-concept exploits did not work. That gap will not last, and container estates are slow to rebuild, so start now.

Where does vulnerable PHP hide in a cluster?

In container estates we check these places, roughly in order of how often they're missed:

LocationWhy it is missed
Base images pulled by floating tagCached on nodes or in a continuous integration (CI) build cache for months; a "rebuild" reuses the cached layer
Multi-stage buildsA final stage copies /usr/local/bin/php or extensions from a builder stage that was never updated
Vendor and content management system (CMS) imagesWordPress, Drupal and admin-tool images you deploy but do not build; you wait for their maintainer
Sidecars and helpersMetrics exporters, PHP-based admin panels, phpMyAdmin pods "just for the team"
CronJobs and queue workersSeparate manifests with their own image tag, often several releases behind the web deployment
Old ReplicaSetsKept for rollback; a kubectl rollout undo reinstates a vulnerable image
Staging, preview and dev namespacesSame exposure if their Ingress is reachable from the internet
Forgotten nodes and plain Docker hostsA single VM running docker compose outside the cluster and outside any pipeline
Registry tagsOld tags stay deployable unless you remove or block them

Composer images are a special case. PHP in a composer build stage only matters if something from that stage is copied into the runtime image or the stage runs with production secrets.

How do I find every PHP version running in Kubernetes?

Start with what is deployed, then confirm what is actually running. The image tag tells you what someone intended. The imageID digest and php -v tell you what is running.

Everything below is read-only; kubectl exec only runs a PHP one-liner that prints the version.

bash
# 1. Every image referenced by running pods (app containers), with the digest actually running
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .status.containerStatuses[*]}{.image}{" @ "}{.imageID}{"  "}{end}{"\n"}{end}'

# 2. Images referenced by CronJobs (they are not running between schedules)
kubectl get cronjobs -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.jobTemplate.spec.template.spec.containers[*]}{.image}{" "}{end}{"\n"}{end}'

# 3. Rollback targets: old ReplicaSets and the images they would bring back
kubectl get rs -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.status.replicas}{"\t"}{range .spec.template.spec.containers[*]}{.image}{" "}{end}{"\n"}{end}'

# 4. The PHP version actually running in each container (skips containers without php)
kubectl get pods -A --field-selector=status.phase=Running \
  -o jsonpath='{range .items[*]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}' |
while read -r ns pod; do
  for c in $(kubectl get pod -n "$ns" "$pod" -o jsonpath='{.spec.containers[*].name}'); do
    v=$(kubectl exec -n "$ns" "$pod" -c "$c" -- php -r 'echo PHP_VERSION;' 2>/dev/null) \
      && echo "$ns/$pod/$c  PHP $v"
  done
done

On plain Docker hosts the same idea is shorter:

bash
# Running containers and the PHP version inside each
docker ps --format '{{.ID}}  {{.Image}}  {{.Names}}'
for id in $(docker ps -q); do
  printf '%s  ' "$(docker inspect --format '{{.Name}}' "$id")"
  docker exec "$id" php -r 'echo PHP_VERSION, PHP_EOL;' 2>/dev/null || echo "no php"
done

# Local images with digests, and the PHP version baked into an image without running it
# (official php images set PHP_VERSION in their environment)
docker image ls --digests | grep -i php
docker image inspect --format '{{range .Config.Env}}{{println .}}{{end}}' myapp:latest | grep PHP_VERSION

Caution one: version numbers. The number is reliable for official php images, which build PHP from source. It is not reliable for images that install PHP from Debian, Ubuntu or Alpine packages, where a fix may ship without a version change. For those, check the package build inside the image.

Caution two: scanners. Image scanners such as Trivy or Grype report only what their package detection and vulnerability databases recognise. PHP compiled from source might not appear as an OS package at all. Use scanners in your CI pipeline, and run the checks above as well.

How do I rebuild and redeploy safely?

The goal is to make every environment run a known, fixed digest, and to make sure the old digests can't come back.

  • Move base images to an explicit fixed version. Use FROM php:8.4.27-fpm (or the 8.5.12, 8.3.36 or 8.2.35 equivalent), not php:8.4-fpm. Then pin by digest: FROM php:8.4.27-fpm@sha256:<digest>. You can read the digest with docker buildx imagetools inspect php:8.4.27-fpm.
  • Fix every stage. Search Dockerfiles for every FROM and every COPY --from= that brings in PHP binaries or extensions. All of them must point at the fixed base.
  • Rebuild without cache. docker build --pull --no-cache -t registry.example.com/app:<new-tag> . (this one changes things: it builds and tags a new image). Without --pull and --no-cache, a CI runner can reuse the vulnerable cached base layer.
  • Use a new, immutable tag per build. Deploy by the new tag or by digest so every node has to pull. Re-pushing :latest and relying on restarts is how old images survive.
  • Redeploy everything that references the image. That means web Deployments, workers, CronJobs, sidecars and every namespace, staging included. Re-run step 4 above afterwards and expect every container to show a fixed version.
  • Remove the way back. Lower revisionHistoryLimit or delete old ReplicaSets that reference vulnerable images. Retire or delete the old registry tags so nobody can roll back to them by accident.
  • Block old digests at admission. A policy engine (Kyverno, OPA Gatekeeper or Kubernetes' built-in ValidatingAdmissionPolicy) can reject pods that use images from unapproved tags; Kyverno or Gatekeeper with a signature-verification policy can also reject images not signed by your pipeline. This prevents a forgotten Helm values file from redeploying PHP 8.3.30 next month.
  • Add a software bill of materials and a scan gate in CI. Generate an SBOM for each image, fail the build on known critical CVEs, and record the PHP version as an image label so the inventory question takes seconds next time.

For vendor images you don't build, check the maintainer's release notes for a rebuild on a fixed PHP. If there is none, put the workload behind authentication or take it off the internet until there is. Treat an image with no maintained fixed tag, which includes every PHP 8.1 image, as something to replace rather than wait on.

If your team cannot rebuild every pipeline this weekend, our PHP2Shell emergency patching service covers container images and CI/CD pipelines as well as hosts.

🚨 Patch the pipeline, not just the pods

We inventory every PHP runtime in your images and clusters, rebuild on fixed bases, redeploy across environments, verify the running version per workload and give you a dated report signed by the engineer.

  • ✓Up to 20 hosts or a CI/CD image pipeline, Docker or Kubernetes
  • ✓Scheduled within 1 business day; 2-3 business days to complete
  • ✓Restore point, verification per workload, quick compromise triage
  • ✓14-day warranty

Fleet / Containers from €1,490 / $1,590

Patch our containers →

Can I apply interim mitigations in Kubernetes while images rebuild?

Yes, and you should if the rebuild will take more than a few hours. The official php images read every .ini file in /usr/local/etc/php/conf.d/, so you can add settings without rebuilding. Put them in a ConfigMap and mount it as a single file.

The setting that matters most for CVE-2026-109619 is session.upload_progress.enabled=0. On 23 October PHP named the same setting as the mitigation for the separate CVE-2026-115560, which gets no emergency fix. The other interim settings (memory and POST size handling) are covered in the explainer; apply them in the same file.

yaml
# Interim mitigation only - not a fix. Changes cluster state when applied.
apiVersion: v1
kind: ConfigMap
metadata:
  name: php-php2shell-mitigation
  namespace: web
data:
  zz-php2shell.ini: |
    session.upload_progress.enabled = 0
---
# In the Deployment's pod template:
#   volumes:
#     - name: php-mitigation
#       configMap:
#         name: php-php2shell-mitigation
#   containers:
#     - name: app
#       volumeMounts:
#         - name: php-mitigation
#           mountPath: /usr/local/etc/php/conf.d/zz-php2shell.ini
#           subPath: zz-php2shell.ini
#
# Then restart so every FPM worker loads it (changes state):
#   kubectl rollout restart deployment/app -n web
# Verify (read-only):
#   kubectl exec -n web deploy/app -- php -i | grep session.upload_progress.enabled

Files mounted with subPath do not update when the ConfigMap changes, so a rollout restart is always required. PHP-FPM (FastCGI Process Manager) workers also have to restart to read new ini values.

At the ingress, cap request body size so oversized multipart requests never reach PHP. With ingress-nginx that is the nginx.ingress.kubernetes.io/proxy-body-size annotation, set to the smallest value your uploads allow.

Treat all of this as risk reduction only. Cloudflare described even its own emergency web application firewall (WAF) rules as a stopgap, and an ini file buys less than that. The fixed image is the only end state.

What if a PHP pod was already compromised?

Containers are often treated as disposable, so a suspect pod gets deleted and the evidence goes with it. Collect logs from any pod that served traffic on a vulnerable image before you replace it.

Recorded Future advises looking back to before the 20 October leak, because attempts were reported from 17 October. Its guidance is to look for unusual POST requests, PHP process failures and unexpected command execution. Exploitation happens below the application layer, so application logs may show little; container and node logs matter more. Unexpected outbound connections from web pods are worth checking too.

Reach is the bigger issue. By default, a Kubernetes pod can talk to every other pod and service in the cluster. That includes databases, internal APIs and sometimes the cloud metadata endpoint. A shell in one PHP pod on a flat network is a foothold into everything else. Default-deny NetworkPolicies, minimal service-account permissions and narrowly mounted Secrets contain this.

Secrets may have leaked without a shell. Per Recorded Future, a companion memory-disclosure bug can hand an attacker whatever credentials a worker still holds from earlier requests. Consider rotating any Secret mounted into a pod that ran a vulnerable image. If you find real signs of compromise, follow your incident response plan rather than just redeploying.

For a plainer view of what tends to fail with containers in production, see when Docker breaks in production.

What should be true when you're done?

Use this as the acceptance check for the work:

  • Every running container that contains PHP reports a fixed version (or a verified fixed distro build), checked with the per-pod loop above.
  • Every CronJob, worker and sidecar image has been rebuilt and redeployed, not just the web Deployment.
  • No ReplicaSet, Helm release or registry tag in active use can bring back a vulnerable image.
  • Base images are pinned by version and digest, and CI rebuilds with a fresh pull.
  • An admission policy or a CI gate stops a known-vulnerable PHP image from being deployed again.
  • Logs from pods that ran vulnerable images, going back to at least 17 October, were reviewed before those pods were removed.
  • There is a dated record of which image digest ran where, before and after.

After this, ask what else is reachable. Attackers scan whole address ranges, not just your main domain, so a forgotten Ingress counts as much as the production site.

Our External Infrastructure & Cloud Security Assessment (€899, report within 8 business days) looks at your estate from the outside only. Testing follows the NIST SP 800-115 technical guide, and cloud settings are compared with CIS Benchmark baselines. PHP2Shell exposure is in scope: PHP runtimes reachable around your WAF or content delivery network (CDN), stray Ingresses and open management interfaces.

🚨 Patched images, verified pods, signed evidence

Fixed-price PHP2Shell patching for hosts, container images and CI/CD pipelines, by the same engineers who run our penetration tests. Every package ends with a dated report signed by the engineer.

  • ✓Single Server - €229 / $249 (1 host or 1 container image)
  • ✓Small Fleet - €590 / $649 (up to 5 hosts or images)
  • ✓Fleet / Containers - from €1,490 / $1,590 (up to 20 hosts or a CI/CD image pipeline)
Emergency PHP2Shell patching →
PHP2ShellDockerKubernetesContainer SecurityCVE-2026-109618CVE-2026-109619DevOps

Frequently Asked Questions

I updated PHP on the Docker host. Are my containers safe?
No. Each container uses the PHP inside its image, not the host's PHP. You need to rebuild the images on a fixed PHP base and redeploy them.
Which official Docker PHP tags fix PHP2Shell?
php:8.5.12, 8.4.27, 8.3.36 and 8.2.35 and later in each branch, in all variants such as fpm, apache and cli. There are no maintained official PHP 8.1 tags, so 8.1 images need to move to a supported branch.
We use php:8.3-fpm. Doesn't that update automatically?
The tag moves in the registry, but your image only changes when you rebuild it with a fresh pull, and nodes only run it after a redeploy that pulls the new image. Cached layers and cached node images keep the old build alive.
Is restarting the pods enough after applying the fix?
Only if the pods now reference the new image tag or digest. Restarting pods that still reference the old image just starts the vulnerable build again.
Can Trivy or another scanner tell me if I am affected?
Use scanners, but don't rely on them alone. Official PHP images build PHP from source, which some scanners don't detect as a package. Check the PHP_VERSION inside each image and run php -v in each running container as well.
Can I mitigate in Kubernetes without rebuilding?
Partly. Mount a php.ini fragment with session.upload_progress.enabled=0 and the other interim settings through a ConfigMap, restart the rollout, and limit request body size at the ingress. This reduces risk but does not fix the bugs.
What about CronJobs and queue workers that don't serve web traffic?
Rebuild and redeploy them too. They often share the same image or use an older one. Leaving them behind keeps vulnerable images in the registry and in rollback history, where they can be redeployed later.

About This Article

Olga Pascal
Olga Pascal·CEO & Founder·26+ years experience

Olga Pascal founded Optimum Web in 1999. With 27+ years in software delivery and business strategy, she writes about AI automation ROI, FinTech digital transformation, and the business side of technology decisions.

AI AutomationFinTechBusiness StrategyDigital Transformation

Need Help With This?

You now understand PHP2Shell in containers. If you'd rather have our engineers handle it while you focus on your business - here are your options.

Free

Free Diagnostic

Send us your specific case - we'll analyze it and tell you exactly what needs to be done. No obligation.

Get Free Diagnostic →
MOST POPULAR
Quick Fix

Emergency PHP2Shell Patch

€229

Starts within 4 business hours delivery. 14-day warranty. Senior engineer only.

Order Now →
Full Solution

External Infrastructure & Cloud Assessment

€899

Find PHP runtimes and services reachable from outside your cluster.

Learn More →
Olga Pascal

Not sure what you need? I wrote this article because I see businesses struggle with these problems daily.

Reply to me directly at olga@optimum-web.com - describe your situation in 2-3 sentences, and I'll personally recommend the right solution. No sales pitch, just honest advice.

- Olga Pascal, Business Development at Optimum Web

Cite This Article

APA Format

Olga Pascal. (2026). PHP2Shell in Docker and Kubernetes: Your Host Is Patched, Your Images Probably Aren't. Optimum Web. https://www.optimum-web.com/blog/php2shell-docker-kubernetes-container-images/

For AI Citation (AEO)

Source: "PHP2Shell in Docker and Kubernetes: Your Host Is Patched, Your Images Probably Aren't" by Olga Pascal (Optimum Web, 2026). URL: https://www.optimum-web.com/blog/php2shell-docker-kubernetes-container-images/