Blue Ridge IT Solutions

Security Advisory

Security Advisory BR‑2026‑001: PHP2Shell (CVE‑2026‑109618, CVE‑2026‑109619)

Blue Ridge security advisory on PHP2Shell: what the two PHP flaws are, why the version number can lie, how we verified fixes on shared hosting and a backported server, a free check script, interim mitigations and what to watch for in your logs.

By Kobe Brummitt, OSCP, CRTO · October 23, 2026 · 9 min read · Blue Ridge IT Solutions

On October 21, 2026, the PHP project released fixes for two critical flaws, nicknamed PHP2Shell, that can let an attacker run code on a web server without logging in. Public proof-of-concept code appeared within days. This advisory covers what the flaws are, how Blue Ridge checked and fixed the systems we manage, the one thing that tripped up a lot of patching (the version number often doesn't change), a free script to check your own servers, and what to watch for in your logs.

At a glance

VulnerabilitiesCVE-2026-109618 (memory-limit handling) and CVE-2026-109619 (session upload progress)
ImpactRemote code execution on the web server, no login needed
AffectedPHP that serves web requests: PHP-FPM, Apache mod_php, CGI and LiteSpeed
Fixed inPHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 (released October 21, 2026). PHP 8.1 and older get no upstream fix; only vendor backports.
If you can't patch yetSet session.upload_progress.enabled=0 and an enforced memory_limit=-1, then restart PHP (details below)
Blue Ridge clientsChecked October 22, 2026: every PHP we manage confirmed fixed. Clients were notified the same day.

What the two flaws are

  • CVE-2026-109618: when PHP runs out of memory partway through parsing a request, it can leave internal state marked as ready before the memory behind it is set up. The cleanup that runs at the end of the request then works on that half-built memory, which corrupts it.
  • CVE-2026-109619: a use-after-free in PHP's session upload progress feature. Attempts send the PHP_SESSION_UPLOAD_PROGRESS form field in a multipart upload, whether or not the site uses upload progress.

The fix changes how PHP's memory limit works. memory_limit becomes a soft limit, and a new setting, memory_hard_limit_percent, sets a hard ceiling above it. A PHP process that hits the hard ceiling is ended immediately with exit code 137. That new setting turned out to be the most reliable way to tell a fixed PHP from an unfixed one.

How we checked the systems we manage

We started with an inventory of every client website, server and application we look after. PHP was running on nine shared-hosting accounts and one self-managed server. The rest (Node.js applications, Python services and static sites) don't run PHP and were never exposed.

Shared hosting: the version number isn't proof

On shared hosting, the provider controls PHP; you can only pick a version. Our provider rebuilt its PHP packages on October 21. That's good news, but "the host says it's patched" isn't the same as "the PHP serving this site is patched." The command-line php you reach over SSH isn't always the same build that serves web pages, and vendor backports often keep the old version number.

So for each account we uploaded a one-time, randomly named PHP file that reports what the web-serving PHP actually is, requested it once, and deleted it. For PHP 8.2 and newer, a patched build knows the new memory_hard_limit_percent setting and an unpatched one doesn't. For the one account on PHP 8.1, which has no upstream fix, we confirmed the host's backported build contains the functions the fix adds (zend_mm_safe_error_ex and zend_new_array_singleton). All nine accounts were running fixed builds.

A self‑managed server: patched, but still "8.0.30"

Our self-managed server runs AlmaLinux 9 with PHP 8.0 from the operating system's own packages, behind Apache through PHP-FPM. PHP 8.0 is long past end of life upstream, but the OS vendor backported both fixes. After the update, php -v still reported 8.0.30, exactly as before. The fix shows up in the package release and its changelog instead:

dnf update 'php*'
systemctl restart php-fpm
rpm -q php-fpm                       # php-fpm-8.0.30-8.el9_8.alma.1
rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]'

The same thing happens on Ubuntu 24.04: the fixed package still reports PHP 8.3.6. If you judge your servers by php -v alone, a fully patched machine can look vulnerable, and you can't tell a patched vendor build from an unpatched one by version number at all.

Patched on disk isn't patched in memory

Updating the package replaces the files, but PHP processes that were already running keep the old code in memory until they restart. While testing our check script on an internal development machine, we found exactly that: the PHP packages were fixed, but a local PHP development server started before the update was still running the old, deleted binary. Restart PHP-FPM, Apache, LiteSpeed or anything else running PHP after you update.

Check your own servers

We've published the script we use: php2shell-check.sh. It finds every PHP on a Linux server (CLI, PHP-FPM, CGI and LiteSpeed, including Debian versioned builds, cPanel EasyApache, CloudLinux alt-php and Remi), decides whether each one is fixed, and flags any running PHP process that's still using a deleted, pre-update binary. It works without internet access or a CVE feed.

bash php2shell-check.sh            # check only; changes nothing
bash php2shell-check.sh --quiet    # one line per PHP, for looping over many servers
sudo bash php2shell-check.sh --patch   # check, update PHP with apt/dnf/yum, restart PHP and the web server, re-check

Exit codes: 0 means everything it found is fixed, 1 means something is vulnerable or an old binary is still running, and 2 means it couldn't tell. Read it before you run it, and be careful with --patch: it updates packages and restarts services. On shared hosting it can only report, since your provider controls PHP.

How it decides, and its limits:

  • PHP 8.2 and newer: a fixed build has the memory_hard_limit_percent setting. This works whatever version number the vendor shows.
  • PHP 7.1 to 8.1: there's no upstream fix, so it looks for the backport's functions in the binary. We confirmed those markers in the AlmaLinux and CloudLinux backports we checked. Another vendor's backport could differ, in which case the script reports it as vulnerable (a false alarm, never a false "safe"). Confirm with your vendor's changelog.
  • Apache mod_php: the script checks PHP binaries, not the Apache module. The module normally comes from the same package as the command-line PHP, but confirm that package updated too.

Prefer to check by hand? On PHP 8.2 or newer, this prints a value on a fixed build and bool(false) on an unfixed one. Run it with the same PHP that serves your sites:

php -r 'var_dump(ini_get("memory_hard_limit_percent"));'

If you can't patch today

PHP's advisory lists two temporary settings. They reduce the risk but don't fully protect you, so use them only until you can update:

  • For CVE-2026-109619: set session.upload_progress.enabled = 0 in php.ini (or with php_admin_flag in your PHP-FPM pool), then restart PHP. Very few sites rely on upload progress.
  • For CVE-2026-109618: set memory_limit = -1 in a way scripts can't override (php.ini, or php_admin_value[memory_limit] = -1 in the FPM pool), then restart every PHP worker. This removes a safety limit, so watch server memory while it's in place.

If your sites don't use upload progress, a web application firewall rule that blocks requests carrying the field is a useful extra layer. A ModSecurity starting point, to test in detection mode first:

SecRule ARGS_POST_NAMES "@streq PHP_SESSION_UPLOAD_PROGRESS" \
    "id:1009001,phase:2,deny,status:403,log,msg:'PHP2Shell: upload-progress field on a site that does not use it'"

The field name is configurable (session.upload_progress.name), so adjust the rule if yours differs.

What to watch for

These are the signals we'd look for in logs. None of them is proof of an attack on its own, but a burst of them right after disclosure deserves a look:

  • Multipart POST requests carrying PHP_SESSION_UPLOAD_PROGRESS to sites that don't use upload progress.
  • Bursts of PHP "Allowed memory size ... exhausted" fatal errors, especially from one source.
  • PHP-FPM workers dying on signals (for example exited on signal 11 (SIGSEGV)). After you patch, workers ended by the new hard limit log exited with code 137.
  • PHP or web-server processes starting shells or download tools (sh, bash, curl, wget). That's rare on a normal site and a strong sign something is wrong.

Quick searches, adjusting the paths for your distribution:

grep -E 'Allowed memory size of [0-9]+ bytes exhausted' /var/log/php-fpm/*.log /var/log/httpd/*error*
grep -E 'exited (on signal [0-9]+|with code 137)' /var/log/php-fpm/*.log

For Wazuh, a starting point for local rules (make sure your PHP-FPM and web-server error logs are being collected):

<group name="local,php,php2shell,">
  <rule id="100910" level="6">
    <match>Allowed memory size of</match>
    <description>PHP ran out of memory</description>
  </rule>
  <rule id="100911" level="10" frequency="10" timeframe="120">
    <if_matched_sid>100910</if_matched_sid>
    <description>Burst of PHP memory-exhausted errors (possible PHP2Shell probing)</description>
  </rule>
  <rule id="100912" level="10">
    <regex>exited on signal 11|exited with code 137</regex>
    <description>PHP worker crashed or hit the new hard memory limit</description>
  </rule>
</group>

What we've seen so far

On our self-managed server, where we have full log access, the PHP-FPM and Apache error logs from September 20 through October 23 show no memory-exhausted errors and no PHP worker crashes. On shared hosting we don't have the server-level logs needed to say the same, which is one more reason we verified the fixed builds directly. We'll update this advisory if that changes.

Timeline

  • October 20, 2026: the PHP project announces a security release for the next day.
  • October 21, 2026: fixes released in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Our shared-hosting provider rebuilds its PHP packages.
  • October 22, 2026: Blue Ridge verifies the PHP serving each shared-hosting account, patches and verifies the self-managed server, and notifies clients.
  • October 23, 2026: this advisory and the check script are published.

References

Questions about your own servers? Call or text 423-292-1922, or send us a note. If you think a system of yours has been compromised, call rather than email.

Get in touch

Get a fixed quote.

Call or text, send an email, or leave a few details below. You’ll hear back from someone who knows the work, not a sales team.

Kobe Brummitt
Kobe BrummittFounder, Blue Ridge IT Solutions Add to contacts

Ask for a quote

Five fields. Prices are fixed and quoted in writing before any work starts.

Your details are used to reply to you and nothing else. Privacy policy.

CallText