How to Patch 'PHP2Shell' (CVE-2026-109618 & CVE-2026-109619) in Ubuntu, Docker & Laravel
Emergency security patches were recently issued for two critical Remote Code Execution (RCE) vulnerabilities in the core PHP engine tracked as CVE-2026-109618 and CVE-2026-109619 (collectively known as PHP2Shell, CVSS 9.2), alongside a related session upload progress vulnerability (CVE-2026-115560).
Because these flaws reside inside the PHP interpreter’s request memory handling rather than application-level code, an unauthenticated attacker can trigger remote code execution with a single crafted HTTP request before your Laravel, Symfony, or WordPress code even executes.
All web-facing PHP runtimes are affected, including Apache (mod_php), Nginx + PHP-FPM, FrankenPHP, php-cgi, and LiteSpeed.
Quick Version Audit: Is Your Server Vulnerable?
Run php -v in your server terminal or inside your running Docker container:
php -v
Compare your active version against the minimum patched releases below:
| PHP Branch | Vulnerable Versions | Minimum Patched Version | Status |
|---|---|---|---|
| PHP 8.5 | <= 8.5.11 |
8.5.12 |
Upgrade Immediately |
| PHP 8.4 | <= 8.4.26 |
8.4.27 |
Upgrade Immediately |
| PHP 8.3 | <= 8.3.35 |
8.3.36 |
Upgrade Immediately |
| PHP 8.2 | <= 8.2.34 |
8.2.35 |
Upgrade Immediately |
| PHP <= 8.1 | End of Life (EOL) | Vendor Backport / Ini Mitigation Only | See Section 4 |
Note on Distro Backports (Ubuntu / Debian / RHEL): Enterprise Linux distributions often backport security patches without bumping the minor PHP version number. If your package build date is October 23, 2026 or newer, inspect the package changelog to confirm:
# Ubuntu / Debian: apt-cache policy php8.3-fpm # RHEL / AlmaLinux / Rocky Linux: rpm -q php --changelog | grep -E "CVE-2026-109618|CVE-2026-109619"
1. How the Vulnerabilities Work (Why App Middleware Cannot Block Them)
Understanding how the exploit triggers explains why Laravel middleware or application-level validation cannot stop it:
- CVE-2026-109618 (Out-of-Memory
longjmpCorruption): When PHP hits an out-of-memory (OOM) condition while parsing an incoming request, it callslongjmp()to abort the context. In unpatched versions, specific memory allocations leave request structures in an uninitialized state, allowing the subsequent cleanup routine to branch onto attacker-controlled memory. - CVE-2026-109619 (Stale Global Request State): Internal global pointers referencing request memory are not cleanly reset after certain failure paths, leaving dangling references to freed memory across subsequent worker requests in persistent SAPIs like
PHP-FPM,mod_php, andFrankenPHP. - CVE-2026-115560 (Multipart Session Upload Progress): Exploits multipart
POSTpayloads whensession.upload_progress.enabledis active prior to script execution.
2. Patching Ubuntu & Debian Servers (ondrej/php & Default Repos)
If your production VPS runs Nginx or Apache with PHP-FPM on Ubuntu (22.04 LTS / 24.04 LTS) or Debian (11 / 12), pull the latest packages and restart the FPM worker pool.
# 1. Refresh package lists
sudo apt update
# 2. Upgrade only PHP packages (replace 8.3 with your active branch: 8.2, 8.3, 8.4, or 8.5)
sudo apt install --only-upgrade "php8.3*" -y
# 3. Restart the PHP-FPM service so workers load the new binary into memory
sudo systemctl restart php8.3-fpm
# 4. If running Apache with mod_php, restart Apache as well:
sudo systemctl restart apache2
Crucial Post-Upgrade Check:
Updating the binary on disk does not protect your server until php-fpm is restarted. Verify the running master process timestamp matches your restart time:
systemctl status php8.3-fpm
(Note: After restarting PHP-FPM, ensure your deployment scripts haven’t altered ownership on your application’s writable directories. If your site throws a 500 error writing to logs after the restart, follow our guide on Fixing storage/logs/laravel.log Permission Denied in Linux & Docker).
3. Patching Docker Containers & Laravel Sail
In Dockerized environments, simply running docker compose restart will not update PHP. Docker caches the base OS and PHP image layers locally when you first build the container.
A. Rebuilding Official php:fpm or php:apache Dockerfiles
Force Docker to pull the freshly patched upstream base image (8.4.27, 8.3.36, or 8.2.35) and rebuild without using stale local layer cache:
# 1. Pull the latest base image layers and rebuild without cache
docker compose build --pull --no-cache app
# 2. Recreate the running containers
docker compose up -d --force-recreate
B. Patching Laravel Sail Environments
If your development or staging server uses Laravel Sail’s published Dockerfiles, rebuild the Sail image with --no-cache:
./vendor/bin/sail build --no-cache
./vendor/bin/sail up -d
Once the container boots, confirm the internal binary version:
docker compose exec app php -v
(If recreating your containers resets your local database host binding or causes a socket crash, see Fixing SQLSTATE[HY000] [2002] Connection Refused in Laravel).
4. Emergency php.ini & FPM Pool Mitigations (If You Cannot Upgrade Immediately)
If you maintain a legacy application locked to an older PHP version, or cannot schedule a maintenance window today, apply the official PHP Foundation configuration mitigations immediately.
Warning: Do not apply these settings using
ini_set()insidepublic/index.phporAppServiceProvider.php. Because PHP parses multipart payloads and session upload progress before handing execution to Laravel,ini_set()runs too late. You must configure these inphp.inior yourphp-fpmpool config (www.conf).
Step 1: Lock Down Your PHP-FPM Pool (/etc/php/8.3/fpm/pool.d/www.conf)
Open your active FPM pool configuration file and append these php_admin_value directives at the bottom so no application script or .user.ini file can override them:
; Mitigate CVE-2026-109619 & CVE-2026-115560: Disable session upload progress before request parsing
php_admin_flag[session.upload_progress.enabled] = off
; Mitigate CVE-2026-109618: Restrict payload sizes to prevent controlled OOM triggers
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 7M
; If running PHP 8.5, disable fatal error backtraces to block the fatal backtrace gadget path:
php_admin_flag[fatal_error_backtraces] = off
Step 2: Enforce Request Size Limits at the Nginx Edge
Prevent oversized multipart payloads from ever reaching PHP-FPM by capping client_max_body_size inside your Nginx server block (/etc/nginx/sites-available/your-app.conf):
server {
# ...
client_max_body_size 8M;
}
Reload both services to apply the lockdown:
sudo systemctl restart php8.3-fpm
sudo systemctl reload nginx
(Note: Disabling session.upload_progress.enabled does not affect standard Laravel authentication sessions or CSRF protection. If your users report session drops after modifying php.ini, check your cookie configuration using our guide on Fixing Laravel 419 Page Expired (CSRF Token Mismatch)).
5. Post-Patch Security Audit: Check for Compromise Indicators (IOCs)
Because automated scanners began probing for CVE-2026-109618 and CVE-2026-109619 immediately after disclosure, run these three quick checks on any server that was exposed prior to patching:
- Confirm
session.upload_progressis Disabled in the Active SAPI: Create a quick CLI check or verify your FPM config dump:php-fpm8.3 -tt | grep "session.upload_progress.enabled" - Scan Your Web Root for Unauthorized
.phpFiles: In a standard Laravel app,public/index.phpshould be the only PHP file insidepublic/, and there should never be.phpfiles insidestorage/app/public/:find public/ storage/ -name "*.php" - Check for Unexpected Child Processes Under
www-data: Inspect the process tree to verifyphp-fpmworkers are not spawning unauthorized shell processes (sh,bash,curl,wget):ps -ef --forest | grep -E "www-data|php-fpm"
Once php -v reports 8.5.12, 8.4.27, 8.3.36, or 8.2.35 and public/index.php is the sole executable entry point in your public directory, your server is protected against PHP2Shell.