PHP2Shell is the name for two critical PHP bugs, CVE-2026-109618 and CVE-2026-109619, that allow unauthenticated remote code execution. PHP fixed them on October 21, 2026. CISA added both to its Known Exploited Vulnerabilities (KEV) catalog on October 26 with a due date of October 29. That deadline has passed. If you still have unpatched PHP serving web traffic, treat it as a live incident, not a backlog item.
This morning I wrote about taking RDP and SSH off the internet with Azure Bastion. This afternoon is about the other front door: the web tier. Here's how to find every PHP runtime in your Azure estate, patch it, and check whether someone got in first.
TL;DR
What: two PHP flaws (both CVSS v4.0 9.2, per PHP) that let an unauthenticated request run code. PHP says the risk is highest for Apache with mod_php, where a demonstrated exploit fires before any application code runs.
Who: every PHP installation serving web requests, including PHP-FPM, mod_php, php-cgi, FrankenPHP and LiteSpeed.
Fix: PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (or your distro's fixed package), then restart PHP-FPM or the web server.
Azure: App Service on Linux built-in runtimes are patched, but not on the Extended release channel first, not in custom containers, and not on Windows, where the built-in PHP runtimes are end of life.
Also patch the kernel: CISA says a Linux kernel bug, CVE-2026-46300, can be chained with PHP2Shell to implant webshells and run commands as root.
What is PHP2Shell?
From PHP's October 21 security advisory:
CVE-2026-109618 (RCE via OOM corruption): out-of-memory handling can interrupt PHP while internal request data is only partly initialized, and request cleanup may then treat that invalid data as valid.
CVE-2026-109619 (RCE via stale global state): request-owned internal state can survive after its memory is freed, and a later request on the same worker may use it before normal initialization resets it.
Microsoft's MSRC says exploitation attempts were reported before the coordinated disclosure date and several proof-of-concept exploits appeared within hours of the fixes. CISA's alert puts it plainly: organizations that can't update, or that still run PHP 8.1 or earlier unsupported versions, should take those systems offline until they're patched.
Who patches what in Azure?
Where PHP runs | Who fixes it | What you do |
|---|---|---|
App Service on Linux, built-in PHP 8.2 to 8.5 | Microsoft (rolled out to Latest and Standard channels on Oct 21) | Move off the Extended channel, verify the version, upgrade any EOL runtime |
App Service on Windows, built-in PHP | Nobody. Microsoft says these runtimes are EOL and no patch is planned | Move to App Service on Linux, or bring your own supported PHP runtime |
Custom containers / bring-your-own PHP | You | Rebuild images on a fixed PHP and redeploy |
Linux VMs and scale sets | Automatic guest patching or OS image upgrades, inside your maintenance window (Linux distros like Azure Linux and Ubuntu) | Don't wait for a distant window on internet-facing hosts; patch now with Azure Update Manager |
PHP you installed on Windows (IIS) | You. Windows Update can't patch it | Install a fixed php.net build, recycle app pools or FastCGI processes |
Step 1: Find your App Service PHP apps
Example, adapt before use. This lists every web app in the current subscription with its Linux stack string, so you can spot PHP apps and their versions:
az webapp list --query "[].{name:name, rg:resourceGroup}" -o tsv |
while read -r name rg; do
az webapp config show -n "$name" -g "$rg" \
--query "{app:name, stack:linuxFxVersion, phpWindows:phpVersion}" -o tsv
done | grep -i phpAnything on PHP 8.1 or older is end of life and won't get the fix, even if you change the release channel. For the rest, in the portal open the app, go to Stack settings, set Platform Release Channel to Latest or Standard, and save. The App Service team recommends Latest for internet-facing or high-risk apps because it gets fixes first.
Verify it worked: open an SSH session to the app (the App Service team points to the Web SSH page on your app's .scm.azurewebsites.net site) and run php --version. You want 8.2.35, 8.3.36, 8.4.27 or 8.5.12 or later.
WordPress note: Microsoft says apps from the official WordPress on App Service Marketplace offering were updated, except those still on the older Alpine-based images, which won't be patched.
Step 2: Patch PHP and the kernel on Linux VMs
Example commands, adapt to your distro and PHP packaging:
# What's installed?
php -v
dpkg -l | grep -E '^ii\s+php' # Debian/Ubuntu
rpm -qa | grep -i php # RHEL/Azure Linux
# Update, then restart so the fixed runtime actually loads
sudo apt-get update && sudo apt-get install --only-upgrade 'php*'
sudo systemctl restart php*-fpm apache2 # use the unit names on your box
# Kernel fix for the CVE-2026-46300 chain needs a reboot
uname -r
sudo rebootOn a fleet, use Azure Update Manager for a one-time update on internet-facing machines rather than waiting for the next scheduled window, which is what MSRC recommends when the window is days away. Defender Vulnerability Management and Defender for Cloud software inventory can help you find PHP you didn't know you had. CISA points out that PHP is often embedded in other software, so it won't always show up as a package named "php".
Step 3: If you can't patch today
PHP's own interim measures reduce exposure but don't make an install safe:
Set
session.upload_progress.enabled=0in php.ini or the FPM pool (not viaini_set()or.user.ini) and restart every worker. This blocks the confirmed session paths for CVE-2026-109619.On PHP 8.5, set
fatal_error_backtraces=0and restart workers.For CVE-2026-109618, PHP suggests
memory_limit=-1enforced withphp_admin_value, a lowpost_max_size(8M without uploads, 32M with), and a container or cgroup memory limit. Don't useRLIMIT_ASorRLIMIT_DATA, which can trigger the vulnerable path.Put the app behind Azure WAF. Microsoft has published PHP2Shell upgrade guidance and Azure WAF mitigations. A WAF is a speed bump, not a patch.
Step 4: Check for compromise
Because exploitation started early, patching isn't the end. CISA marks both PHP CVEs and the kernel CVE for forensic triage in the KEV catalog, and its alert says to watch for PHP worker processes launching shells or downloading tools. A quick first pass (example, adapt before use):
# Shells or downloaders spawned under PHP workers
ps -eo pid,ppid,user,etime,cmd --forest | grep -B2 -A3 -E 'php-fpm|apache2|httpd' | grep -E 'sh -c|bash|curl|wget'
# PHP files changed since disclosure (Oct 20, 2026) in your web root
sudo find /var/www -name '*.php' -newermt '2026-10-20' -lsCISA has also published a SIGMA detection rule for this activity in its SIGMA rules GitHub repository. If you find a webshell, don't just delete it. Isolate the host, preserve evidence, rotate every secret the app could read (connection strings, storage keys, API tokens), and rebuild from a clean image. Managed identities and Key Vault references make that rotation much less painful.
Common pitfalls
Patching without restarting. Running PHP-FPM workers keep the old code until restarted.
Forgetting containers. Your base image froze PHP at build time. Rebuild and redeploy.
Assuming Windows App Service is covered. It isn't. Built-in PHP there is EOL.
Stopping at PHP. Patch and reboot for CVE-2026-46300 too, so a web foothold can't become root.
PHP on Windows and Brotli. PHP shipped rebuilt Windows packages (8.5.12-1, 8.4.27-1, 8.3.36-1, 8.2.35-1) with Brotli 1.2.1 for CVE-2026-111497, also in KEV. The version number doesn't change, so check the build suffix.
Key takeaways
PHP2Shell is exploited and the KEV deadline has passed. Patch internet-facing PHP first, today.
App Service on Linux is patched on Latest and Standard. Extended, custom containers, EOL versions and Windows are your problem.
Restart workers, rebuild images, and reboot for the kernel fix.
Hunt for webshells and rotate secrets on anything that was exposed before you patched.
Related reading: the same "exploited, patch now" pattern hit self-managed GitLab and Ivanti Connect Secure in the last week.
Sources
Patched everything already, or found a PHP runtime hiding somewhere you didn't expect? Tell me in the comments. I read every one.
