← Research

PHP2Shell: finding and patching affected PHP

Oct 21, 2026

On October 21, a large part of the internet went into maintenance mode. DeviantArt and Pokémon Home went offline, and Wikimedia took many of the services behind Wikipedia offline. Government agencies in the Netherlands, Finland and Denmark took public sites down, along with the Port of Rotterdam and several Dutch hospitals. Hosting providers ran emergency maintenance through the night and switched off PHP features across shared servers while they waited for packages.

The cause is two critical bugs in the PHP runtime, CVE-2026-109618 and CVE-2026-109619, each rated CVSS 9.2, that allow unauthenticated code execution and can leak worker memory, including passwords, tokens and API keys from other requests. The CVE IDs leaked two days before the planned release, so the PHP team shipped a day early, and Cloudflare reported attack traffic before the fix was out. CISA, the NSA, the UK NCSC and CERT-EU all said to patch now and, where that is not possible, to take PHP systems offline or off the public internet until you can.

The patch is a package upgrade and a restart. Finding every PHP runtime is the hard part. CISA's alert says it plainly: PHP “is often a component of other software and prevalence may not be directly observable.”

Community resources

Defenders are sharing what they have:

  • Cloudflare deployed WAF rules to every plan, including free, hours before disclosure.
  • Corelight open-sourced Zeek and Suricata detections, and Elastic published rules for what attackers do after code execution.
  • Hostinger and Laravel published update steps for customers who manage PHP on their own servers, such as Hostinger VPS and Laravel Forge.
  • ProjectDiscovery published a Nuclei template for version checks, and Neo has the full find-and-patch flow pre-built to discover and scan your assets for free.
  • SecureTheWeb.org is notifying the operators of affected services directly.
  • Fake exploit repositories are circulating. JFrog found fake PoCs on GitHub shared as pranks. Run public PoCs only in an isolated lab, if at all.

PHP on the internet

PHP serves 69.3% of websites whose server-side language is known, according to W3Techs. On October 21, Shodan returned 1,677,546 service records advertising an explicit PHP version. The bug is in the runtime, so every application on an affected PHP is in scope: WordPress, Drupal, Magento, Laravel and custom code alike.

PHP is used by 69.3% of websites where the server-side language can be identified (W3Techs)69.3%use PHPof sites with a known server-side language · W3Techs1,677,546 Shodan service records advertising an explicit PHP version on October 211,677,54601M2Mservice records advertising a PHP versionShodan, October 21
Each dot is 1% of sites with a known server-side language. The bar counts Shodan service records advertising an explicit PHP version on October 21.

The services most at risk are the ones that run for years without attention. Many of them sit next to the systems that matter most: the firewall, the phone system, the billing database, the mail server.

Where long-lived PHP runsExamples
Hosting serverscPanel and Plesk servers, and every site they host
Network monitoringZabbix, Cacti, Nagios XI, LibreNMS
Firewalls and phonespfSense, OPNsense, FreePBX
Mail and filesRoundcube, Nextcloud, ownCloud
Database adminphpMyAdmin, Adminer
Billing, CRM, helpdeskWHMCS, SuiteCRM, vTiger, osTicket, GLPI
Learning and wikisMoodle, MediaWiki

Why PHP is hard to find

  • Non-standard ports. Admin panels listen on 2083, 2087, 8080, 8443, 8880 and other ports that a scan of 80 and 443 misses.
  • Sub-paths. A host with a static site on / can run phpMyAdmin on /phpmyadmin/ or Roundcube on /webmail/.
  • Hidden versions. Setting expose_php = Off, or a proxy or CDN rule that strips the X-Powered-By header, hides the PHP version. Many PHP services show no version at all.
  • Appliances and panels. Firewalls and hosting control panels often ship their own PHP, so the fix arrives as a vendor update. Until it is installed, the admin interface is vulnerable wherever it can be reached. InMotion closed its CWP admin ports for this reason: the panel runs on PHP and had no fix yet.
  • Containers. An image built on an old php: base keeps that PHP until someone rebuilds it. BIT.nl patched every server it manages on October 21 and noted that containers maintained by customers had not been updated.
  • Forgotten domains. Acquisitions, agency-built campaign sites, staging copies and test servers live outside the main inventory.

Find and patch PHP

Five steps, each feeding the next. The commands use free ProjectDiscovery tools, plus jq for step 3.

List assetsdomains + IP rangesFind web servicesevery portMark PHPheaders + cookiesCheck versionsNuclei templatePatch + restartconfirm on hostInstalledRunningfixed packageold binarypatched after restart
Installing the fixed package doesn't change a worker that is already running: the old binary keeps serving until the restart.
  1. 1

    List everything you own

    Put every root domain in domains.txt: product, marketing, regional, acquired, and the ones agencies registered for you. Put public IP ranges and cloud egress addresses in ranges.txt.

  2. 2

    Find every web service, on every port

    writes web.txtshell
    subfinder -dL domains.txt -all -silent | cat - domains.txt | dnsx -silent > hosts.txtcat ranges.txt >> hosts.txtnaabu -l hosts.txt -p 80,443,2082,2083,2086,2087,2095,2096,8000,8080,8443,8880,8888,9443 -silent > open.txthttpx -l open.txt -silent > web.txt

    For the hosts that matter most, scan every port with -p -. If port 9000 answers from the internet, check what is listening. It is PHP-FPM's default FastCGI port, and the official php-fpm Docker images listen on it on every interface. If it is PHP-FPM, close it.

  3. 3

    Mark the services that run PHP

    Probe the root and the paths where PHP apps usually live, then keep every response that shows PHP: an X-Powered-By or Server header naming PHP, or a PHPSESSID cookie.

    writes php.txtshell
    httpx -l web.txt -silent -td -irh -j -o probe.json \  -path /,/index.php,/phpmyadmin/,/webmail/,/roundcube/,/zabbix/,/cacti/,/glpi/,/wp-login.phpjq -r 'select((.tech // [] | tostring | test("PHP")) or (.header | tostring | test("PHP/|PHPSESSID"))) | .url' \  probe.json | sort -u > php.txt

    php.txt is your PHP inventory. Services that show PHP without a version go straight to step 5.

  4. 4

    Check the advertised versions

    The CVE-2026-109618 Nuclei template reads the version from the X-Powered-By header and flags PHP 5.4 and later below the fixed releases. It sends one ordinary GET per URL and attempts no exploitation. It is on the nuclei-templates main branch and not yet in a release, so download it first:

    writes affected.txtshell
    curl -sO https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2026/CVE-2026-109618.yamlnuclei -t CVE-2026-109618.yaml -l php.txt -o affected.txt
  5. 5

    Confirm on the host, patch, restart, verify

    The banner is a lead. The host is the answer. On each server, check the installed PHP and what is actually running:

    run on each servershell
    for b in /usr/bin/php* /usr/sbin/php-fpm* /usr/local/bin/php /usr/local/sbin/php-fpm; do [ -x "$b" ] && echo "$b: $("$b" -v 2>/dev/null | head -1)"; donedpkg -l 'php*' | grep ^ii          # Debian, Ubunturpm -qa 'php*'                      # RHEL, Rocky, Almaps -eo pid,args | grep -E 'php-fpm|php-cgi|lsphp|frankenphp|httpd|apache2' | grep -v grep

    Patch Apache mod_php hosts first: php.net rates them highest risk, because one unauthenticated request can trigger the attack before any application code runs. Upgrade to a fixed release, then restart PHP-FPM, Apache and LiteSpeed. For containers, rebuild the image on a fixed php: base and redeploy it. This lists processes still running a PHP build that was replaced on disk; it should print nothing:

    should print nothingshell
    sudo grep -lE '(php|libphp).*\(deleted\)' /proc/[0-9]*/maps 2>/dev/null

    Finish with one request through the normal production route. Where the version header is exposed, confirm it shows the fixed release. Elsewhere, confirm with php -v and php-fpm -v on the host.

Prompts for AI agents

Most coding agent apps and tools already know how to use ProjectDiscovery tools. Whether you use Claude Code, Codex or OpenCode, lean on what your app does best, like goal mode or dynamic workflows, and pick the strongest model you have access to, so one prompt can work through your whole estate. Start with a short setup prompt, then give your agent the objective.

Install the tools
Install the ProjectDiscovery tools with pdtm (github.com/projectdiscovery/pdtm) and confirm subfinder, dnsx, naabu, httpx and nuclei run.
Map internet-facing PHP
Find every internet-facing service that runs PHP across the domains in domains.txt, including admin panels on non-standard ports and apps under sub-paths. Use whatever works: ProjectDiscovery tools, Nuclei templates, a browser, manual checks. For each service, find the PHP version and whether it is below the fixed releases (8.2.35, 8.3.36, 8.4.27, 8.5.12). Return one table: URL, application, PHP version or unknown, affected (yes, no, unknown).
Find PHP in code and infrastructure
Search our repositories for everything that builds or deploys PHP: Dockerfiles with FROM php:, installs of php-fpm or libapache2-mod-php, Helm charts, Terraform and Ansible roles. List each with the PHP version it pins, the service it deploys and the team that owns the repository. Flag every version below 8.2.35, 8.3.36, 8.4.27 or 8.5.12, and every PHP 8.1 or older.
Check one server
On this Linux server, list every PHP binary, PHP package and running PHP process (PHP-FPM, Apache mod_php, php-cgi, LiteSpeed, FrankenPHP), plus PHP inside running containers. For each, give the version and whether it is below the fixed release for its branch: 8.2.35, 8.3.36, 8.4.27, 8.5.12. Flag any process whose PHP binary or libphp module shows as deleted in /proc/<pid>/maps, since it needs a restart. Read only; change nothing.

Our open-source tools work with any agent or setup, and Neo runs this whole flow for free if you would rather not set anything up.

Upgrade

PHP branchFirst fixed release
8.28.2.35
8.38.3.36
8.48.4.27
8.58.5.12
  • Distribution, hosting and LTS packages can carry the fix under an older version string. Confirm the changelog names both CVEs.
  • PHP 8.1 and older: every release back to 5.4 is affected. Move to a supported branch, or install a vendor package that backports both fixes. If you build from source, PHP published minimal source backports for 7.4, 8.0 and 8.1.
  • Appliances: apply the vendor's update as soon as it ships, and keep the admin interface off the internet until then.
  • Anything you cannot patch today: remove public access until the upgrade lands. php.net lists temporary settings, such as session.upload_progress.enabled=0, that reduce exposure on specific paths. They do not make a server safe, and they only take effect in php.ini or the FPM pool after a worker restart.
  • If an affected server was reachable from the internet before you patched, assume secrets in its memory were exposed. Rotate its database passwords, API keys and session signing keys, and check it for signs of compromise.

SecureTheWeb.org

Alongside this guide, we are also launching SecureTheWeb.org, a notification service for critical vulnerabilities that affect the internet at scale. It runs on Chaos, our internet-wide dataset of exposed services, updated in real time and continuously enumerated to cover legacy infrastructure as well as modern. When an advisory like this one lands, SecureTheWeb matches the affected versions against Chaos and emails the security@ inbox of every domain running one, with the fixed release and the steps to patch. These two CVEs are the first event it covers, and those emails are already going out, since attack traffic was flowing before the fix shipped. Over the coming months we will run more of our tools against Chaos at scale to enrich it and map exposed services better, and we will keep the data available for everyone to use in their own research and remediation. This will matter more as the exploitation capabilities of AI agents keep rising.

References

More researchAll notes →