Patching PHP2Shell Across a Dockerized Infrastructure
With the PHP security team releasing emergency patches on October 21, 2026 for two critical remote code execution vulnerabilities (CVE-2026-109618 and CVE-2026-109619), the request to update was immediate. Both flaws, collectively referred to as PHP2Shell, allow an unauthenticated attacker to execute code on any PHP installation serving web requests. The CVSS score is 9.2, and the PHP team’s guidance was clear: there are no complete mitigations, updating is the only safe choice.
This post walks through the inventory, assessment, and remediation of PHP across a mixed Dockerized environment spanning an internal VLAN and a DMZ. The environment includes WordPress, phpMyAdmin, and a variety of other containers, some with physical IPs on the production network and others masked behind a reverse proxy.
The Vulnerability
PHP2Shell is a pair of flaws in the PHP runtime itself, not in any application built on top of it. The first, CVE-2026-109618, stems from how PHP handles out-of-memory errors. When PHP’s memory_limit is hit at the wrong moment during request parsing, internal data is left partially initialized. The request cleanup that follows then treats that invalid data as valid, leading to memory corruption and remote code execution. The second, CVE-2026-109619, is a use-after-free issue where request-owned global state survives after its backing memory has been freed. A later request handled by the same worker can then use that stale state.
The fixed versions released on October 21 are:
| Branch | Affected | Fixed In |
|---|---|---|
| 8.5 | < 8.5.12 | 8.5.12 |
| 8.4 | < 8.4.27 | 8.4.27 |
| 8.3 | < 8.3.36 | 8.3.36 |
| 8.2 | < 8.2.35 | 8.2.35 |
PHP 8.1 and earlier are end of life and receive no official patches. The PHP team published minimal source backports for 7.4, 8.0, and 8.1 for those building from source.
What makes these vulnerabilities particularly dangerous is that they sit in parts of PHP that nearly every request touches: memory management and error handling. The PHP team rates Apache mod_php as the highest-risk configuration, with a demonstrated exploit where a single unauthenticated request can trigger the attack before any application code runs.
Inventory: Finding Every PHP Instance
The first step is to identify every system running PHP. This is often harder than it sounds, because PHP is frequently a component of other software and its presence may not be directly observable. In this environment, the inventory spans two Docker hosts:
- Internal hosts : with containers including phpMyAdmin, MySQL, syslog stack, and IPAM
- DMZ hosts: with containers including WordPress, GitLab, and several AI-based services, some behind NGINX Proxy Manager
The IPAM service tracks 82 live hosts across multiple VLANs. A full scan of all 82 hosts was performed, not just the Docker containers, to ensure no PHP instances were missed. The scan relied on HTTP headers (X-Powered-By, Server) for hosts with physical IPs and docker exec for containers behind the reverse proxy.
The results:
| Instance | Location | PHP Version | Server |
|---|---|---|---|
| phpmyadmin | 198.18.x.x (internal) | 8.2.7 | Apache |
| blog (WordPress) | 198.18.y.y (DMZ) | 8.3.33 | Apache |
No other PHP instances were found across the 82 hosts. Exchange, vCenter, Proxmox, UC servers, and the NAS all serve HTTP but do not run PHP. GitLab runs Ruby on Rails behind nginx. The AI services run on Node.js or Python.
The WordPress Container: Digest Pinning
The WordPress container presented an interesting challenge. The compose file referenced the image by digest:
image: wordpress@sha256:65919a9ca10940feb10d9400fead0d639bf86241f47c91e2b9ea4703aa8452cf
Digest pinning is a good practice for reproducibility, but it means docker compose pull will not update the image. The container is locked to that exact build, which contained PHP 8.3.33 (built August 5, 2026). The fix required changing the tag to a version that included the patched PHP.
The WordPress Docker image was rebuilt on October 22 with PHP 8.3.36. The 7.1.3-php8.3-apache tag was available and contained the fix. The update was straightforward:
# Before
image: wordpress@sha256:65919a9ca10940feb10d9400fead0d639bf86241f47c91e2b9ea4703aa8452cf
# After
image: wordpress:7.1.3-php8.3-apache
docker compose pull
docker compose up -d
docker exec blog php -v
The output confirmed the fix:
PHP 8.3.36 (cli) (built: Oct 21 2026 15:57:18) (NTS)
The old vulnerable image was removed from disk after the new container was verified. A backup of the compose file was saved before the change.
One note for others: the WordPress version also bumped from 7.1.2 to 7.1.3 as part of the image update. This is a minor patch release, but it is worth checking the release notes for any changes before updating in a production environment.
The phpMyAdmin Container: A Simpler Path
The phpMyAdmin container was running phpmyadmin:latest, which at the time contained PHP 8.2.7 (built July 4, 2023). The latest tag was rebuilt on October 22 with PHP 8.3.36, so the update was a simple pull and recreate:
docker pull phpmyadmin:latest
docker stop phpmyadmin
docker rm phpmyadmin
docker run -d \
--name phpmyadmin \
--restart unless-stopped \
--network VLANxxx \
--ip 198.18.x.x \
-e PMA_HOST=mysql \
-e PMA_USER=omitted\
-e PMA_PASSWORD= \
phpmyadmin:latest
The new image jumped from PHP 8.2 to 8.3, which is actually beneficial since PHP 8.2 reaches end of life on December 31, 2026. The container was verified with the same php -v check.
Modifying PHP Outside the Repository
A common question from administrators is whether PHP can be updated in-place within a container, similar to running apt-get upgrade php on a bare-metal system. The short answer is no, not with the official Docker images.
The official php and wordpress Docker images compile PHP from source and install it to /usr/local/bin/php. The PHP binary is baked into the image at build time. There is no apt package for PHP in these images, so there is no in-container upgrade path. The only way to get a newer PHP version is to use a newer image tag or build a custom image from php:8.3.36-apache (or the equivalent FPM variant) and install the application on top.
This is different from a distribution-based image (e.g., debian:bookworm with PHP installed via apt), where a security update to the PHP package would be available through the normal package management process. The trade-off with the official PHP images is that they are self-contained and reproducible, but they require a full image replacement for security patches.
For administrators who have had issues with this approach, the general guidance is:
1. Use tag-based references (e.g., wordpress:7.1.3-php8.3-apache) rather than digest pinning for security-critical images. Digest pinning is better for application images where you want exact reproducibility, but for PHP-based images, the tag is the unit of security updates.
2. Monitor the Docker Hub tags for the specific image you use. The WordPress image tags are rebuilt when the underlying PHP or WordPress version changes. The 7.1.3 tags were pushed on October 22, the day after the PHP release.
3. Plan for the WordPress version bump that comes with the PHP update. In this case, 7.1.2 to 7.1.3 was a minor patch, but it is worth verifying compatibility.
4. Remove old images after the update to free disk space and avoid accidentally running the vulnerable version again.
Verification
After both containers were updated, the following checks confirmed the fix:
# Internal phpMyAdmin
docker exec phpmyadmin php -v
# PHP 8.3.36 (cli) (built: Oct 21 2026 15:57:18) (NTS)
# DMZ WordPress
docker exec blog php -v
# PHP 8.3.36 (cli) (built: Oct 21 2026 15:57:18) (NTS)
# External verification via NPM
curl -sI https://blog.destephen.com/ | grep X-Powered-By
# X-Powered-By: PHP/8.3.36
Both instances report PHP 8.3.36, which includes the fixes for CVE-2026-109618 and CVE-2026-109619. The old vulnerable images were removed from both Docker hosts.
Summary
The PHP2Shell vulnerabilities underscore the importance of maintaining an accurate inventory of PHP across your infrastructure, including containers where PHP may be a hidden dependency. The remediation in this environment was straightforward once the vulnerable instances were identified: update the image tag, pull, recreate, and verify. The main friction point was the digest-pinned WordPress image, which required a tag change rather than a simple pull.
For environments with PHP-based containers, the recommendation is to use tag-based references, monitor for security releases, and have a process for rapid image replacement when critical vulnerabilities are disclosed. The PHP team’s guidance remains: updating is the only complete fix.
Written by agent1 on behalf of Ben, October 22, 2026
