Skip to main content

php2shell-check

CI License: MIT Python 3.9+ Dependencies: none

Fast, zero-dependency checker and remediation helper for the PHP2Shell remote code execution flaws — CVE-2026-109618 and CVE-2026-109619.

What this is: a defensive tool. It inspects systems you operate — the local PHP install, version strings, Dockerfiles/compose files, your own running containers, and your own logs — tells you whether you're affected, and gives you the exact steps to fix it. It sends no exploit traffic and cannot be aimed at a remote host. It is not an exploit and does not contain one.

Respond in 60 seconds

curl -sO https://raw.githubusercontent.com/SirjanSingh/shiny-php/main/php2shell_check.py
python3 php2shell_check.py check      # Am I affected?
python3 php2shell_check.py harden     # If so, exact fix for my OS
# Then update PHP to 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 and re-check.

The vulnerability

On 2026-10-21 the PHP project shipped emergency patches for two critical core RCE vulnerabilities, collectively nicknamed PHP2Shell:

CVEs CVE-2026-109618, CVE-2026-109619
Severity Critical — CVSS v4.0 9.2
Affected PHP 5.4 through 8.5
Fixed in 8.5.12, 8.4.27, 8.3.36, 8.2.35
Workaround Disabling session.upload_progress blocks the confirmed path but is incomplete — patching is the only full fix

PHP 5.x and 7.x are end-of-life and receive no official fix; affected servers on those branches must move to a supported, patched branch.

Install

Single file, no dependencies, Python 3.9+:

curl -O https://raw.githubusercontent.com/SirjanSingh/shiny-php/main/php2shell_check.py
chmod +x php2shell_check.py

Or install it as a command with pipx (or pip):

pipx install php2shell-check          # from PyPI (once published)
# or straight from source, no release needed:
pipx install git+https://github.com/SirjanSingh/shiny-php
php2shell-check check

Maintainers: see RELEASING.md for the one-time PyPI Trusted-Publishing setup. Tagging vX.Y.Z then publishes automatically.

Usage

Check this machine

./php2shell_check.py check
PHP2Shell check  (CVE-2026-109618, CVE-2026-109619, disclosed 2026-10-21)

  /usr/bin/php
    PHP 8.4.26 (cli) (built: Oct  1 2026 10:00:00) (NTS)
    AFFECTED: PHP 8.4.26 is affected. Update to 8.4.27 or later on this branch.

Action required. Run `php2shell-check harden` for the exact upgrade steps.

Check a version string (no live PHP needed — great for CI / inventory)

./php2shell_check.py check --version 8.3.40

Machine-readable output

./php2shell_check.py check --json

Exit codes: 0 not affected · 1 affected · 2 couldn't determine. Drop it straight into CI:

./php2shell_check.py check --json || echo "patch PHP before shipping"

Remediate

./php2shell_check.py harden              # print exact upgrade + workaround steps
./php2shell_check.py harden --apply --conf-dir /etc/php/8.4/fpm/conf.d

harden detects your OS and prints the right package-manager commands. With --apply it writes a drop-in ini that disables session.upload_progress — a partial mitigation; it will still tell you to update PHP.

Scan logs for attack attempts

./php2shell_check.py scan-logs /var/log/apache2/error.log /var/log/modsec_audit.log
./php2shell_check.py scan-logs /var/log/nginx/access.log --json

Read-only. It flags attempts, not confirmed compromise, keying on indicators published by security vendors after disclosure:

Indicator Log type Source
PHP_SESSION_UPLOAD_PROGRESS in a request body WAF / ModSecurity / body Splunk, Zafran
Upload filename= with a <?php tag WAF / body Splunk
Serialized PHP object marker (O:.., a:..) in a body WAF / body Splunk
PHP Fatal error … Unknown on line 0 PHP / error log Abstract Security
Apache AH00052 child-exit Apache error log Abstract Security
Repeated large POSTs from one source IP access log (heuristic) Bitdefender, Beazley

Important: plain access logs almost never record request bodies, so a clean access-log scan is not an all-clear — the body/field indicators need a WAF/ModSecurity log. And patch PHP regardless; detection is not prevention. These are behavioral signals based on expected exploit behavior, not confirmed atomic IoCs — validate against your own logs.

Scan Docker images

./php2shell_check.py scan-docker                       # Dockerfiles/compose in cwd
./php2shell_check.py scan-docker path/to/Dockerfile compose.yml
./php2shell_check.py scan-docker --running             # + your running containers

Reads PHP base-image tags. A pinned tag (php:8.3.10) is judged exactly; a floating branch tag (php:8.4) is flagged as ambiguous (pin a patched tag), and an end-of-life branch tag (php:8.1) is flagged affected outright.

Check a whole fleet (offline)

./php2shell_check.py check --inventory hosts.txt

hosts.txt is one label version per line — straight from your inventory / Ansible facts, no network or SSH involved:

web01 8.3.10
web02=8.4.27
db01: 7.4.33

SARIF → GitHub code scanning

./php2shell_check.py check --sarif > results.sarif
./php2shell_check.py scan-logs /var/log/modsec_audit.log --sarif > results.sarif

Upload it so findings land in your repo's Security tab:

# .github/workflows/php2shell.yml
- run: python php2shell_check.py scan-docker --sarif > results.sarif
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif

Why there's no remote "URL scan" mode

Some PHP2Shell tools send a crafted probe to a target URL to test it. This one deliberately doesn't. It only inspects systems you operate (local, inventory, your Dockerfiles, your containers, your logs), so you can't accidentally point it at a host you don't own, and running it is never itself an attack. If you want to check a server, run it on that server. Full reasoning in SECURITY.md.

Tests

python -m unittest discover tests

Sources

Detection guidance:

Version/fix data in this tool reflects the 2026-10-21 release. Always confirm against php.net for your exact branch.

License

MIT — see LICENSE.

Metadata

Release files for php2shell-check 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for php2shell-check 0.1.0
File Size Uploaded Provenance
php2shell_check-0.1.0.tar.gz 17.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for php2shell-check 0.1.0
File Interpreter ABI Platform
php2shell_check-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 33.3 kB

Release files / php2shell_check-0.1.0.tar.gz

Download URL php2shell_check-0.1.0.tar.gz
Size 17.3 kB
Tags Source
SHA-256 checksum
How to use checksums
37bfe464c6a50345cdf5c5980f66a2cdf1f8192a5178368add58adb1bb748711
BLAKE2b-256 checksum
How to use checksums
1edb99baa4ff8b134c7a0cc66bf5d96cab57ab7af4550eba74ca498d89b3b0d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / php2shell_check-0.1.0-py3-none-any.whl

Download URL php2shell_check-0.1.0-py3-none-any.whl
Size 16.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b90ef8af68b861a74c2e8d938734b998fc364dfc5a1269840eb6a5b8d9e02626
BLAKE2b-256 checksum
How to use checksums
afde64e3e89216f03eb0aa644be8191ad8a748543fd8551aeea7eb5098e1363d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for php2shell-check 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for php2shell_check-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page