Skip to content
Noviqent.
Home
Solutions Integrations Services Software Insights About Log in Contact us

Cybersecurity

PHP2Shell (CVE-2026-109618 & CVE-2026-109619): how to patch PHP on Linux, Docker, Kubernetes, AWS, Azure and GCP

22 October 2026

On 21 October 2026 the PHP project released emergency fixes for two critical vulnerabilities in the PHP core, nicknamed PHP2Shell. Both can lead to unauthenticated remote code execution. Both are triggered while PHP is still parsing the incoming HTTP request, before your framework, router or login check gets a chance to run. Exploitation attempts were seen before public disclosure, and broad internet-wide scanning followed within hours. If you run PHP on anything reachable from the internet, treat this as a patch-today issue.

We patched our own estate the same day. This guide sets out what is affected, the fixed versions, and the exact steps for plain Linux servers, Docker, Kubernetes, AWS, Azure and Google Cloud, including the gotchas we hit along the way.

The CVEs at a glance

CVESeverityWhat goes wrong
CVE-2026-109618Critical, CVSS v4.0 9.2When a memory allocation fails during request parsing, PHP's cleanup code processes partially initialised request data. An attacker can trigger this with an oversized POST field and corrupt memory.
CVE-2026-109619Critical, CVSS v4.0 9.2A use-after-free in which a PHP worker reuses stale global state from a previous request. It is reachable through the session upload-progress feature, and on PHP 8.5 also through fatal-error backtraces. It can leak memory and lead to code execution as the web worker user.

Any PHP runtime that serves HTTP requests is in scope, including PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed.

Fixed versions

BranchVulnerableFixed inUpstream support
8.58.5.11 and earlier8.5.12Active support
8.48.4.26 and earlier8.4.27Active support
8.38.3.35 and earlier8.3.36Security fixes only
8.28.2.34 and earlier8.2.35Security fixes until 31 Dec 2026
8.1, 8.0, 7.x, 5.xAffectedNo php.net releaseEnd of life

For 7.4, 8.0 and 8.1, the PHP project published source-level backports only. Some distributions, control panels and extended-support vendors ship patched packages for those branches, but the durable fix is to move to a supported branch.

Watch out for distribution version numbers. Debian, Ubuntu and RHEL often backport security fixes without changing the upstream version. A package reporting 8.3.6 on Ubuntu 24.04 can be fixed, and one reporting a newer number can still be vulnerable. Always check your vendor's security notice for the fixed package release.

Step 1: find every PHP runtime you run

The most common mistake is patching the PHP you can see and missing the PHP that actually serves traffic. In practice that means three things:

  • The CLI is not the web server. php -v tells you about the command-line binary. PHP-FPM or mod_php can be a different version from a different package.
  • Old versions linger. Servers that have been upgraded over the years often still have php7.4 or php8.1 installed and wired into a vhost or an FPM pool. When we audited our own servers we found exactly this: an old PHP 7.4 still installed long after nothing needed it.
  • Containers bundle their own PHP. Patching the host does nothing for PHP inside an image.
# Every PHP package installed (Debian/Ubuntu)
dpkg -l | grep -E '^ii\s+(lib)?php[0-9.]*' | awk '{print $2, $3}'

# Every PHP package installed (RHEL/Alma/Rocky/Amazon Linux)
rpm -qa | grep -i '^php' | sort

# Which PHP-FPM services are running
systemctl list-units --type=service | grep -i fpm

# Which PHP handler Apache is actually using
apache2ctl -M 2>/dev/null | grep -i php
grep -rh "SetHandler\|php.*-fpm" /etc/apache2/ 2>/dev/null | sort -u

# The version FPM reports (adjust the binary name to your version)
php-fpm8.3 -v

For certainty about what the web server loads, drop a temporary phpinfo() page behind your server's IP allow-list, read the version and session.upload_progress.enabled, then delete it right away.

Linux servers

Debian and Ubuntu

sudo apt update
apt list --upgradable 2>/dev/null | grep -i php
sudo apt install --only-upgrade 'php*'

# Restart every PHP service and the web server so no old workers survive
sudo systemctl restart php8.3-fpm        # repeat for each installed version
sudo systemctl restart apache2           # or nginx

# Confirm the fixed package release against your vendor's notice
apt policy php8.3-fpm

On Ubuntu 20.04 and older, the fixes come through Ubuntu Pro ESM. If you use a third-party PHP repository (a PPA, Sury or Remi), check that the repository has actually published the fixed build. Not all of them did on day one.

RHEL, AlmaLinux, Rocky and CentOS Stream

sudo dnf clean metadata
sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm httpd     # or nginx
rpm -q php-fpm

On RHEL 9 the php:8.2 module stream moves to 8.2.35 (RHSA-2026:87072). AlmaLinux has published patched packages for every PHP stream on versions 8, 9 and 10.

Alpine

apk update
apk upgrade 'php83*'     # or php84 / php85
apk info -v | grep '^php8'

Remove what you don't need

If an old PHP version is installed but not serving anything, purge it instead of patching it. That is one less runtime to track the next time this happens. On Debian and Ubuntu, for example, run sudo apt purge 'php7.4*' && sudo apt autoremove. Then make sure Apache's default handler and every vhost point at the patched version.

Docker

The official php images on Docker Hub have been rebuilt with 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Your containers keep running the old PHP until you rebuild your image on the new base and replace the running containers.

# 1. Pin the fixed version in your Dockerfile
FROM php:8.3.36-fpm-bookworm

# 2. Rebuild with --pull so Docker doesn't reuse a cached old base layer
docker compose build --pull --no-cache
docker compose up -d --force-recreate

# 3. Verify every running container
for c in $(docker ps -q); do
  printf '%s  ' "$(docker inspect -f '{{.Name}}' "$c")"
  docker exec "$c" php -r 'echo PHP_VERSION, PHP_EOL;' 2>/dev/null || echo "(no php)"
done
  • Floating tags such as php:8.3-fpm only move when you re-pull. Pinned tags and digests such as php:8.3.30-fpm or @sha256:… never move. You must edit them.
  • Images built on a distribution base (for example FROM debian plus apt install php) need the distribution's fixed packages, not the Docker Hub tag.
  • Rebuild every image that bundles PHP, including Nginx Unit, FrankenPHP, Laravel Octane and third-party application images. Check each vendor's release notes for theirs.

Kubernetes

Kubernetes doesn't patch anything for you. You ship a new image and roll it out. First, find every workload running PHP:

kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{" "}{end}{"\n"}{end}' \
  | grep -iE 'php|laravel|wordpress|drupal|magento|symfony'

Rebuild and push a patched image as in the Docker section, then update the workload. An immutable tag or digest is better than :latest, so the rollout is explicit and auditable:

kubectl -n web set image deployment/app php=registry.example.com/app:2026-10-22-php8.3.36
kubectl -n web rollout status deployment/app

# Prove every pod runs the fixed version
for p in $(kubectl -n web get pods -l app=app -o name); do
  echo -n "$p  "; kubectl -n web exec "$p" -c php -- php -r 'echo PHP_VERSION, PHP_EOL;'
done

If you can't roll out straight away, mount the interim mitigations (below) as a php.ini fragment from a ConfigMap into /usr/local/etc/php/conf.d/, then restart the deployment. You can also cap request bodies at the ingress, for example with the nginx.ingress.kubernetes.io/proxy-body-size annotation on ingress-nginx. Add an admission policy (Kyverno, Gatekeeper or your registry's scanner) to block vulnerable PHP images from being deployed again.

AWS

  • EC2 on Amazon Linux 2023. AL2023 pins package repositories to a release version, so a plain dnf upgrade may not see new packages. Check against the latest release, then upgrade:
    sudo dnf check-update --releasever=latest 'php*'
    sudo dnf upgrade --releasever=latest 'php*'
    sudo systemctl restart php-fpm httpd
    Compare the result with the Amazon Linux Security Center (ALAS) advisory. If the fixed package hasn't been published yet, apply the interim mitigations now, and patch as soon as it is.
  • EC2 at scale. Use Systems Manager Patch Manager, or Run Command to patch a tagged fleet in one go:
    aws ssm send-command \
      --document-name "AWS-RunShellScript" \
      --targets "Key=tag:Role,Values=web" \
      --parameters 'commands=["sudo dnf -y upgrade --releasever=latest php*","sudo systemctl restart php-fpm httpd"]'
  • ECS and Fargate. Push the rebuilt image to ECR, register a new task definition revision, then run aws ecs update-service --cluster prod --service app --force-new-deployment.
  • EKS. Follow the Kubernetes steps above.
  • Elastic Beanstalk. Update the environment to the newest PHP platform version that includes the fix (Configuration → Platform, or managed platform updates). For Docker-based environments, rebuild the image.
  • Lambda with Bref. Move to a Bref layer or image built on a fixed PHP release, then redeploy.
  • Detection. Amazon Inspector can flag vulnerable EC2 instances and ECR images once its vulnerability database includes the CVEs. AWS WAF can enforce body-size limits in front of ALB and CloudFront.

Microsoft Azure

  • App Service (built-in PHP on Linux). Microsoft maintains the PHP runtime images and rolls out patched builds. Check your configured runtime with az webapp config show -g rg -n app --query linuxFxVersion, and check the running version from SSH or Kudu with php -v. A restart (az webapp restart -g rg -n app) moves the app onto the latest image once Microsoft has published it. If you are on an end-of-life PHP stack, move to a supported one.
  • App Service and Container Apps with custom containers. Rebuild your image as in the Docker section, push it to ACR, and update the app or revision to the new tag.
  • AKS. Follow the Kubernetes steps above.
  • Virtual Machines. Patch with the Linux steps, or across a fleet with Azure Update Manager:
    az vm assess-patches -g rg -n web-01
    az vm install-patches -g rg -n web-01 --maximum-duration PT2H \
      --reboot-setting IfRequired --classifications-to-include-linux Security
    Then restart PHP-FPM and the web server.
  • Detection. Microsoft Defender for Cloud vulnerability assessment covers VMs and ACR images. Azure Front Door and Application Gateway WAF can cap request body sizes.

Google Cloud

  • Compute Engine. Patch with the Linux steps, or run a VM Manager OS patch job across a fleet:
    gcloud compute os-config patch-jobs execute \
      --instance-filter-names="zones/europe-west2-a/instances/web-01" \
      --display-name="php2shell" --reboot-config=default
    Then restart PHP-FPM and the web server.
  • Cloud Run. Rebuild the image on a fixed PHP base, push it to Artifact Registry, then run gcloud run deploy app --image=europe-west2-docker.pkg.dev/proj/repo/app:php8.3.36. Cloud Run never re-pulls a base image on its own.
  • App Engine standard. Google manages the PHP runtime. Redeploy (gcloud app deploy) to make sure your instances run on the latest runtime build, and move off any end-of-life PHP runtime.
  • App Engine flexible. Rebuild and redeploy, as you would a custom container.
  • GKE. Follow the Kubernetes steps above.
  • Detection. Artifact Analysis scans images in Artifact Registry. Cloud Armor can enforce request-size limits on external load balancers.

If you can't patch right now: interim mitigations

These reduce exposure to the known attack paths. None of them is a fix. Set them in configuration that loads before request parsing (php.ini, a conf.d file, or php_admin_value in the FPM pool or Apache config). Don't use ini_set() or .user.ini. Then restart every worker.

; /etc/php/8.3/fpm/conf.d/99-php2shell.ini  (adjust the path to your version)

; CVE-2026-109619 - disable the session upload-progress path
session.upload_progress.enabled = 0

; CVE-2026-109619 - PHP 8.5 only
; fatal_error_backtraces = 0

; CVE-2026-109618 - remove the finite memory limit the exploit relies on.
; Only safe together with request-size and container memory limits below.
; memory_limit = -1

; Keep request bodies small
post_max_size = 8M          ; 32M if the app takes uploads
upload_max_filesize = 4M
  • Cap request body size, header size and field counts at the reverse proxy, load balancer or WAF. Use Nginx client_max_body_size, Apache LimitRequestBody, or your cloud WAF.
  • Managed WAF rules are now available from several vendors. Cloudflare, for example, deployed protection for all plans on 21 October. Treat them as a speed bump, not a substitute for patching.
  • If a PHP system can't be patched or mitigated, take it off the public internet until it can.

Check you weren't hit before you patched

  • PHP error logs containing Allowed memory size of … exhausted … in Unknown on line 0, especially from very large URL-encoded POSTs.
  • Web server logs showing worker segfaults, for example Apache AH00052: child pid … exit signal Segmentation fault (11), often in bursts from failed exploit attempts.
  • PHP-FPM or Apache workers spawning shells (sh, bash, curl, wget). This is the clearest sign of a successful exploit.
  • New or modified PHP files in web roots, unexpected cron jobs, and new SSH keys.

If anything looks suspicious, isolate the host and rotate every secret the application could read: database passwords, API keys, APP_KEY, and cloud credentials.

While you're in there

PHP2Shell is a good prompt to fix the hygiene problems that make incidents like this worse. A common one on long-lived servers is a default vhost that serves the parent web directory and quietly exposes every application's .env file. Worth checking on yours:

  • Does a default or catch-all vhost serve a directory that contains other applications? Point it at an empty directory.
  • Do requests for /.env, /.git/config and /storage/logs/ return 404 or 403 on every hostname and on the bare IP address?
  • Is expose_php = Off set, so you aren't advertising your PHP version?
  • Do you have an inventory of every PHP runtime (host, container image and managed service) with an owner and an update path? The next PHP critical will be much faster to handle if you do.

Summary checklist

  1. Inventory every PHP runtime, including FPM, mod_php, containers and managed services.
  2. Upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or to your vendor's fixed package.
  3. Remove PHP versions you don't use.
  4. Restart or replace every worker, container and pod.
  5. Verify the version the web server runs, not just the CLI.
  6. Apply the interim mitigations anywhere you can't patch today.
  7. Review logs for signs of exploitation, and rotate secrets if in doubt.

Need help patching a mixed estate of servers, containers and cloud services, or want a second pair of eyes on your exposure? Talk to Noviqent.

Frequently asked questions

Which PHP versions fix PHP2Shell?

PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, all released on 21 October 2026. Every earlier release on those branches is affected. Distribution packages may carry the fix under their own version numbers, so check your vendor's advisory, not just the upstream number.

Is PHP 7.4 or 8.1 affected?

Yes. Older branches are affected and get no official release from php.net. The PHP project published source backports for 7.4, 8.0 and 8.1, and some distributions and extended-support vendors ship patched packages. The safest fix is to move to a supported branch. If nothing on the server needs the old version, remove it entirely.

Does my application's login or authentication protect me?

No. PHP parses the malicious request before any application code runs, so authentication, frameworks and application-level filters cannot block it.

Is a WAF rule or a php.ini change enough?

No. Setting memory_limit=-1, disabling session.upload_progress and adding WAF rules each reduce exposure to specific attack paths. The PHP project is clear that only updating PHP fully fixes the vulnerabilities.

I updated the package. Am I done?

Only after every running PHP worker has been restarted or replaced. Then confirm the version the web server actually runs, which can differ from the php command-line binary. If the server was exposed before you patched, also check for signs of compromise and consider rotating any secrets the application could read.

Want to talk this through?

Happy to go into more detail, or look at how it applies to your own setup.

Speak with us