Security ·

How to check whether your website is exposed to PHP2Shell

A serious flaw in PHP, the software behind WordPress and a great many other websites, was disclosed on October 21. Here is how to find out, in plain English, whether your site is affected, and what to ask the person who looks after it.

On October 21, 2026, the PHP project published two critical security flaws, CVE-2026-109618 and CVE-2026-109619, nicknamed PHP2Shell. In an unpatched version of PHP, either one can let a stranger on the internet run their own code on your web server, and no login is needed. If your website runs on PHP, this is worth settling now.

You may not know whether your site uses PHP. WordPress, Joomla and Drupal all run on it. If your site is WordPress, it does. If a freelancer or agency built your site, or a hosting company runs the server, ask. A quick answer is better than an assumption.

This guide is in two parts: one for people who own a website, and one for the people who run the servers.

For people who own a website

Three questions for whoever runs your website

You do not need to read code. You need three answers. Whoever looks after your site, whether that is a hosting company, an agency, a freelancer or a colleague, should be able to give each one in a sentence.

1. Which PHP version do we run, and was it updated after October 21?

PHP fixed the flaws in new versions. A site is covered if it runs this version or a later one in the same line:

  • PHP 8.5: 8.5.12 or later
  • PHP 8.4: 8.4.27 or later
  • PHP 8.3: 8.3.36 or later
  • PHP 8.2: 8.2.35 or later

These are the versions listed as patched in PHP’s own advisories for CVE-2026-109618 and CVE-2026-109619.

2. If the version looks old, can you show me it is still patched?

A version number can mislead. Linux distributions often fix a flaw inside their own package without changing the PHP version it reports. Ubuntu’s fix for 24.04 is labeled 8.3.6, which looks far older than 8.3.36 but includes the fix. AlmaLinux tells administrators not to rely on the version PHP prints for the same reason, and to check the package instead (AlmaLinux’s notice). A good answer names the package and says where the fix is recorded.

3. Was PHP restarted after the update?

Installing an update puts new files on the server. A running PHP keeps using the old code until it is restarted. AlmaLinux’s instructions include the restart for this reason. This is general practice and not specific to this flaw, but it is the step most easily forgotten.

If the answer is “not yet”

Updating is the only complete fix, according to BleepingComputer’s report. If it cannot happen today, ask your IT person about the temporary measures in PHP’s advisories. They reduce the risk without removing it, and some change how file uploads work, so they need a person who can test your site.

If the site runs an older PHP, the answer is harder. PHP no longer releases updates for 8.1 and earlier. Its maintainers did publish source patches for 7.4, 8.0 and 8.1, and some Linux vendors ship their own fixed builds. AlmaLinux, for example, patched several retired PHP streams, and says future fixes for them are not guaranteed. Either way, the lasting answer is a plan to move to a supported PHP.

An example, so it is concrete

This is a hypothetical, not a customer story. Suppose a small landscaping company has a WordPress site that a freelancer built and a web host runs. The owner emails the freelancer the three questions above. A good reply names the PHP version, says when and by whom it was updated, and confirms PHP was restarted. A reply that says “we are on the latest WordPress” answers a different question. WordPress and PHP are updated separately.

If you are the person who does the work

Questions 1 and 2 are quick to answer for one server and slow for fifty. We published a free, open-source checker, php-vuln-check, that answers them for every server, container image and pipeline at once. Open the technical version of this guide for how to run it.

For people who run the servers

Why the version number is not enough

On Debian, Ubuntu and the RHEL family, php -v can print an old version on a machine that is already patched, because the distribution fixes the flaw inside its own package and leaves the version number alone. A check that reads only the version flags those machines and sends your team after problems that are not there.

For package-owned PHP, the vendor changelog names the CVE IDs:

# Debian, Ubuntu
zgrep CVE-2026-109618 /usr/share/doc/php8.2-common/changelog.Debian.gz

# RHEL, Alma, Rocky
rpm -q --changelog php-common | grep CVE-2026-109618

Two catches. Ubuntu fixed 24.04 without changing the version at all: it stays on 8.3.6, and the fix is the package build 8.3.6-0ubuntu0.24.04.13 (USN-8948-1). And in our tests, the official ubuntu and debian Docker images strip /usr/share/doc, so inside a container there is no changelog to read. Compare the package version instead: dpkg-query -W php8.3-cli.

Check one server

php-vuln-check is a single Python file with no dependencies. It never sends anything that could harm a server. It reads versions, package changelogs and configuration only.

pipx install git+https://github.com/ethoic-inc/php-vuln-check
php-vuln-check

It finds every php, php-fpm, php-cgi, lsphp and Apache libphp*.so on the machine, reads the version, asks the package manager which package owns the file, and checks that package’s changelog for both CVE IDs. It only runs PHP binaries that are owned by root and not writable by others, so pointing it at a tenant’s home directory does not execute their code. Each install comes back as one of:

  • PATCHED: the version is at or above the fix, or the changelog names both CVEs.
  • VULNERABLE: below the fix with no backport evidence, or an end-of-life branch with none.
  • REVIEW: it cannot tell, for example a floating image tag, a package whose changelog is not installed, or PHP that was updated on disk but not restarted. That is not a clean bill of health.

It was built with Claude Code and reviewed independently before release. Its tests run against real PHP packages on Debian, Ubuntu, AlmaLinux, Rocky, Fedora, Amazon Linux, Alpine and the official PHP images. The code is open source under the MIT license: github.com/ethoic-inc/php-vuln-check, and the latest release.

Check every server

# hosts.txt: one [user@]host per line
php-vuln-check fleet hosts.txt --format csv -o php-exposure.csv

The script is streamed over SSH, so nothing is installed on the hosts. You get one CSV row per PHP install per host. A host where no PHP was found is marked NONE, which is not the same as patched.

Check your images and pipelines

Plenty of PHP now lives in images, not on servers. The images command reads FROM lines in Dockerfiles (including ARG defaults and multi-stage builds) and image: entries in compose files and Kubernetes manifests, and reports the file and line. php-vuln-check images --kubectl does the same for every pod in your current cluster.

php-vuln-check
$ php-vuln-check images examples/demo -q

VULNERABLE  php:7.4-apache
            PHP 7.4
            branch 7.4 is end-of-life with no upstream release; pulling a
            newer image on this branch cannot fix it
            at examples/demo/legacy/Dockerfile:2

VULNERABLE  php:8.3.35-fpm-alpine
            PHP 8.3.35
            8.3.35 is below the fixed release 8.3.36
            at examples/demo/app/Dockerfile:3

REVIEW      php:8.3-cli
            PHP 8.3
            floating tag: resolves to the newest 8.3.x at pull time. Pin it
            to 8.3.36 or later and rebuild with --pull
            at examples/demo/app/docker-compose.yml:5

PATCHED     php:8.4.27-apache
            PHP 8.4.27
            8.4.27 is the fixed release
            at examples/demo/app/docker-compose.yml:3

Summary: 2 vulnerable | 1 need review | 1 patched
Real output from the example project in the repository, with two long lines wrapped to fit the page.

To gate a pipeline, use the GitHub Action and upload the findings as SARIF, so they appear in your repository’s Security tab with the Dockerfile line:

on: [push, pull_request]
jobs:
  php2shell:
    runs-on: ubuntu-latest
    permissions: { contents: read, security-events: write }
    steps:
      - uses: actions/checkout@v4
      - uses: ethoic-inc/php-vuln-check@v0.1.4
        with:
          fail-on: vulnerable   # or "review" to also fail on floating tags
          sarif-file: php-vuln.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: php-vuln.sarif }

What it deliberately does not do

There are already active scanners that probe a host by forcing PHP out of memory, and their authors describe that as risky by nature. php-vuln-check never sends exploit traffic, so it is safe on production. The trade-off is that it tells you whether the PHP is patched, not whether a given host falls over when probed. Use both if you want both answers.

It cannot see inside an image from a tag alone. The configuration hints it prints (session.upload_progress.enabled, memory_limit) come from php -i, plus any php_value or php_admin_value overrides it finds in the packaging’s usual php-fpm.conf and pool files. Pools kept in unusual places are not read. A PHP you built yourself from PHP’s backport branches for 7.4, 8.0 or 8.1 still reads as vulnerable, because its version number does not change.

If it says VULNERABLE

Update PHP and restart PHP-FPM or Apache. If you cannot yet, blocking multipart/form-data at the web server is a partial, temporary mitigation that breaks file uploads; Sansec has the snippets. On PHP 8.1 and older there is no upstream release, so use your vendor’s backport, build from PHP’s backport branches, or upgrade.

The tool is new. It is tested against fixtures and in CI on real distro containers, and we want to hear about anything it gets wrong on your setup, especially RHEL-family systems and third-party PHP repositories. Open an issue with the output of php-vuln-check scan --format json.

Where this comes from

Ethoic Inc builds AI tools that keep people in control. php-vuln-check is a small open-source project from us, separate from our products. It runs on your own machine, has no telemetry, and only talks to the hosts you point it at.

The fix for PHP2Shell is already available. The work left is making sure it reached your site.

All Journal entries Our mission