EST. 2026 — Notes from real server work SYSADMIN · SECURITY · AI · DIRECTADMIN Issue 01
THE SYSADMIN ALMANAC
Home / Security / Two Critical PHP RCE Flaws (php2shell): Patch Today
Security

Two Critical PHP RCE Flaws (php2shell): Patch Today

by Ton-Or ·22 Oct 2026·10 min read
Two Critical PHP RCE Flaws (php2shell): Patch Today

This is not news you can read and then schedule an update for the end of the month. Exploits are already available, and unauthenticated requests can trigger them before application code even runs. If you have machines running PHP, check them today.

Yesterday I saw the PHP Foundation's announcement, followed by a DigitalOcean blog post titled "Patch Now." I went through all the machines I maintain first, then put this together for others looking after PHP websites. This vulnerability does not require an unusual plugin or a misconfiguration to affect you. It is in PHP itself and affects every machine using PHP to handle web requests, whether through PHP-FPM, Apache's mod_php, php-cgi, FrankenPHP, or LiteSpeed.

The nickname Red Hat and Ubuntu use in their advisories is php2shell. The official identifiers are CVE-2026-109618 and CVE-2026-109619. Both have a CVSS v4.0 score of 9.2, and proof-of-concept code has already been published. In practical terms, that means anyone who has not updated could be attacked right now.

9.2CVSS v4.0 for both flaws
2 CVERCE in PHP itself
PoC availablePublic exploits
PHP 5 and upAffected versions

What are these two vulnerabilities?

PHP's announcement is brief but clear. Here is my summary in Thai.

  • CVE-2026-109618 (RCE through OOM corruption) When PHP runs out of memory, error handling interrupts PHP while its internal request data is still being constructed. During cleanup at the end of the request, PHP then uses that incomplete data as though it were valid.
  • CVE-2026-109619 (RCE through stale global state) Some internal state belonging to a request remains after its memory has been freed. The next request handled by the same worker may use that stale state before normal initialization clears it.

The part I find most concerning is this statement in the announcement: the PHP team considers mod_php to be at the highest risk, and an exploit has demonstrated that an unauthenticated request can trigger an attack before application code even runs. That means even a well-written application, an application-level WAF, and a tightly secured login system cannot help, because the vulnerability is reached before any of that code.

Why well-written apps cannot helprequestNo login neededPHP parse requestFlaws are hereApplication codeHas not startedLogin / WAFInside the appCVE-2026-109618OOM interrupts request data setupCVE-2026-109619Stale state across requests in one workerCode execution on the host (RCE)
The vulnerabilities occur while PHP handles a request, before passing it to application code. Defenses inside the application have not had a chance to run.

Which versions are safe, and which need replacing?

The announcement says all versions from PHP 5 onward are affected. Patched releases are available only for the 4 branches still maintained.

PHP branch Patched version Notes
8.5 8.5.12
8.4 8.4.27
8.3 8.3.36
8.2 8.2.35 The 8.2 branch reaches end of life on 31 Dec. 2026
8.1, 8.0, 7.4 No new release The security team has released minimal source patches (backports); you must compile them yourself
7.3 and below, 5.x Nothing available You must move to a maintained version
Distro package version numbers may not change

Debian, Ubuntu, and RHEL often fix vulnerabilities by applying patches to an existing package version (backporting). php -v may therefore still show the same version number. Check the package changelog for both CVE identifiers. Do not judge by the version number alone.

Step 1: Check every PHP installation on the machine

The trap I encounter most often on machines hosting multiple websites is having more than one PHP version installed on the same machine, then checking only the one that php on PATH points to. That is often not the version the websites actually use, so I need to check every installation.

# ตัวที่อยู่บน PATH (มักเป็นแค่ CLI)
php -v

# ทุก binary ที่มีในเครื่อง (รวมของ DirectAdmin ที่อยู่ใต้ /usr/local/phpXX)
for p in /usr/bin/php* /usr/local/php*/bin/php; do
  [ -x "$p" ] && echo "$p : $($p -r 'echo PHP_VERSION;')"
done

# PHP-FPM ที่กำลังรันอยู่จริง และรุ่นของมัน
ps -eo pid,args | grep -E 'php-fpm: master' | grep -v grep

On Debian/Ubuntu, check the changelog to see whether the installed package includes the patches.

apt changelog php8.2-fpm 2>/dev/null | grep -E 'CVE-2026-10961[89]' | head

For RHEL, Rocky, and AlmaLinux, use this command.

rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]' | head

If you cannot find both CVE identifiers, it has not been patched.

Step 2: Update according to how PHP was installed

  1. Debian / Ubuntu Update every PHP package, then restart FPM or the web server.
  2. RHEL / Rocky / AlmaLinux / Fedora When the announcement was published, patches for this family were still rolling out. Check the changelog again after updating.
  3. DirectAdmin Update CustomBuild's version list first, then rebuild PHP.
  4. Docker Change the base image tag to a patched version, rebuild the image, then redeploy every container.
  5. Windows See the next section, because bundled packages tend to lag behind upstream releases.

Debian / Ubuntu:

sudo apt update
sudo apt install --only-upgrade 'php*'
sudo systemctl restart 'php*-fpm' apache2 2>/dev/null

RHEL and related distributions:

sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm httpd

For DirectAdmin with CustomBuild 2, if multiple PHP versions are configured in options.conf, ./build php will build every configured version. Before running it, check that the version list includes the patched releases.

cd /usr/local/directadmin/custombuild
./build update
grep -E '^php(82|83|84|85):' versions.txt     # ต้องเห็น 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12
./build php

For Docker, change the FROM line in the Dockerfile, then rebuild without using the old cache.

FROM php:8.4.27-fpm
docker build --pull --no-cache -t myapp:php-8.4.27 .
Restart every worker after updating

The second vulnerability involves stale state in the same worker. If you only update the files without restarting PHP-FPM or Apache, old workers will keep running the old code. Restart, then use ps to check that the master process really is new.

Step 3: Things that tend to get missed after updating

Often forgotten

Old installations still remain

  • Old PHP versions left installed on the same machine, such as 5.6, 7.4, and 8.1 for some customers' websites
  • VM snapshots or backups that bring back the old PHP version when restored
  • Container images in the registry that have not been rebuilt
  • Dev or staging machines exposed to the internet
What to do

Close every route in

  • Move websites using versions without patches to patched versions, or block external access
  • Rebuild every image, then delete old tags that are still vulnerable
  • Go through machines restored from backups and check their versions again
  • Restrict access to websites that do not need to be public

On the shared hosting machine I maintain, rebuilding brought PHP 8.2, 8.3, and 8.4 up to the patched releases, but 5.6, 7.4, and 8.1 were still installed for some old websites. Those versions have no new release to build. I have to decide, website by website, whether to move it to a newer version or shut it down. There is no shortcut.

While waiting for patches: temporary settings recommended by PHP

The PHP team stresses that no setting fully fixes this; updating is the only complete fix. The settings below only reduce the risk for machines that cannot be updated immediately, and they must be set at a level the application cannot change.

; php.ini หรือ pool ของ FPM / config ของ Apache (ห้ามใช้ ini_set() หรือ .user.ini)
memory_limit = -1
post_max_size = 8M          ; ถ้ามีอัปโหลดไฟล์ ใช้ 32M
upload_max_filesize = 6M    ; ต้องน้อยกว่า post_max_size
session.upload_progress.enabled = 0
; PHP 8.5 เท่านั้น
; fatal_error_backtraces = 0

For a PHP-FPM pool, use php_admin_value so the application cannot override it.

php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[session.upload_progress.enabled] = 0
Do not limit memory with RLIMIT

The announcement explicitly warns against using RLIMIT_AS or RLIMIT_DATA instead, because a failed memory allocation is precisely what triggers the first vulnerability. Restart every worker after applying these settings as well.

If your website is behind Cloudflare, Cloudflare has already released an emergency WAF rule. Enable it, but treat it as an additional layer, not a reason to delay updating.

Windows machines and XAMPP

On Windows, most people use bundles such as XAMPP, whose releases lag far behind upstream. Even on my own dev machine, XAMPP still has PHP 8.2.12 and Apache 2.4.58, versions from 2023. Waiting for an updated bundle is not an option in an incident like this.

I chose to build Apache, PHP 8.4.27, and MariaDB from source on Windows myself, so I could have the patched version on the day upstream released it, and make the next update a matter of changing version numbers in one file and rebuilding. I have written the procedure as a separate series, one installment at a time, so you can follow along: Build Apache + PHP + MariaDB Yourself on Windows.

If you do not want to build it yourself, at least bind Apache to 127.0.0.1 on dev machines that are not exposed to the internet. Do not let other machines on the network access it until PHP has been replaced with a patched version.

Listen 127.0.0.1:80

APM 1.0.0 already ships PHP 8.4.27.

Don't want to build it yourself

If you just want to use APM without compiling anything, the finished build is at github.com/ChonLaWan/APM-Server. Download APM-1.0.0-win64.zip (about 140 MB) from Releases, compare its sha256 with the attached .sha256 file, unzip it anywhere and run manager\APMManager.exe. The Manager picks free ports, writes the config, issues the certificate and creates the database for you. Its Check for updates button downloads a new release, verifies the sha256 and swaps the binaries without touching your config, databases or sites.

Verify after updating

# รุ่นที่เว็บใช้จริง (วางไฟล์ชั่วคราว เปิดดู แล้วลบทิ้งทันที)
echo '<?php echo PHP_VERSION, " ", PHP_SAPI;' > /path/to/docroot/_v.php
curl -s https://example.com/_v.php ; echo
rm -f /path/to/docroot/_v.php

The result must show a patched version number and the SAPI the website uses (such as fpm-fcgi). If it still shows an old version, that website is still running through another PHP installation that has not been updated. Go back through Step 1.

Personally, I am not waiting for the monthly maintenance window this time. I am updating every machine where PHP handles external requests immediately. For machines I cannot update, I am applying the temporary settings and blocking access for now, according to the staff and number of machines each team has to maintain.

P.S. If you use an end-of-life PHP version such as 7.4 or 8.1, this is a good time to move to a maintained version, because next time there may not even be source patches available.

Sources: The PHP Foundation announcement (php.net/archive/2026.php) and DigitalOcean's blog post "Critical PHP RCE Vulnerabilities: Patch Now," dated 21 Oct. 2026. If more details emerge or other distros release patches, I will come back and update this article.

Want us to set this up?

Get help with server setup, configuration, migration, or troubleshooting. Save time for the work that matters.

Get a quote

Hit something like this? Stuck somewhere?

Come trade notes on the LinuxThai board

→ Continue on the board

Comments 0

No comments yet — be the first