Skip to main content
Cybersecurity & Hardening

PHP2Shell, Node.js and eMudhra: The October 2026 Patch List and How I Protected My Customers

PHP2Shell, a Node.js security release and eMudhra's leaked CA keys landed in one week. What to patch, how to check it, and how I protected my customers.

Published 9 min read

PHP2Shell, two remote code execution bugs in PHP itself, went public on 21 October 2026. The same week brought a Node.js security release and eight compromised certificate authority keys at eMudhra. That last one reaches every Python install that carries its own certificate list. If you run websites, you probably run at least two of those.

None of this needs panic. It needs patching, and a few checks that are easy to get wrong. Below is what to patch, how to prove it worked, and what I did on the servers I manage for customers.

What is PHP2Shell, and how bad is it?

PHP2Shell is the nickname for two bugs, CVE-2026-109618 and CVE-2026-109619. They let someone run code on a web server without logging in. The code runs as the web server’s PHP user. The PHP project rates both 9.2 out of 10. These are flaws in PHP itself, not in your WordPress or Laravel code, so an app update won’t fix them.

The NSA put out a press release the same day. Its most quoted line: “If an immediate update is not feasible, it is recommended that organizations take the system offline until it can be fully mitigated.”

That sounds dramatic. Read it carefully, though. It’s the fallback for systems you can’t update, not step one. For everything else, update.

Where things stand on 26 October 2026: Amazon saw a first exploitation attempt on its honeypots on 17 October, four days before the fix shipped. Researchers have shown working exploits since. Sansec found that the public proof-of-concepts fail against a default PHP install, and its advice was calm: “You do not need to take your store offline. Update PHP anyway.”

There’s no publicly confirmed victim yet, and CISA’s Known Exploited Vulnerabilities catalog doesn’t list either CVE. That’s a snapshot, not a promise.

The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older get no release from the PHP project. It published minimal source backports for 7.4, 8.0 and 8.1, which only help if you build PHP from source yourself.

How to check your PHP2Shell patch (the version number lies)

This is the step people get wrong, in both directions.

Take an up-to-date Ubuntu 24.04 server. php -v says 8.3.6. The fix is 8.3.36. It’s patched anyway. Ubuntu and AlmaLinux copy security fixes into the old version and keep the old number. AlmaLinux says it plainly: “Don’t rely on php -v.” The fixed Ubuntu package is 8.3.6-0ubuntu0.24.04.13.

Debian is the easy one. Bookworm moved to 8.2.35-1~deb12u1 and trixie to 8.4.27-1~deb13u1, so the number matches upstream.

So the version number can’t prove it. The package changelog can:

# Debian / Ubuntu (swap 8.3 for your PHP version)
zgrep CVE-2026-109618 /usr/share/doc/php8.3-common/changelog.Debian.gz

# AlmaLinux / RHEL family
rpm -q --changelog php-common | grep CVE-2026-109618

If the CVE shows up, the fix is on disk. On disk isn’t running, though. PHP-FPM and Apache keep the old code in memory until you restart them:

sudo systemctl restart php8.3-fpm   # or apache2 / httpd for mod_php

Two more traps:

  • The php in your terminal may not be the one serving your site. Many servers have several PHP versions installed side by side. Check the one your web server actually uses. In WordPress, that’s Tools › Site Health › Info › Server.
  • Hosting panels ship their own PHP builds. Update PHP through the panel and check its changelog for the CVE. apt upgrade alone won’t touch them.

If someone else runs your server, ask one question: was PHP updated and restarted after 21 October 2026?

PHP in containers, panels and boxes

Your Nextcloud or WordPress container has its own PHP inside the image. Restarting the container changes nothing. It starts the same old image again.

An image is like an install disc. You need the new disc and a fresh install from it:

docker compose pull && docker compose up -d

Build your own images? Rebuild with docker build --pull so the base image gets refreshed too.

One catch: the official php:8.1 image on Docker Hub hasn’t been rebuilt since January 2026. Pulling it again gets you nothing. If an app still needs 8.1, it’s time to move it, or to pay a vendor that backports fixes for old branches.

NAS boxes, firewalls and appliances often bundle PHP in their web interface. You can’t patch that yourself. The Dutch NCSC keeps a list of vendor statuses. If your model shows no fix yet, take its admin page off the internet until it has one. That’s where the NSA’s “take it offline” advice actually fits.

The Node.js security release: 22.23.4, 24.22.1 and 26.12.1

Node.js shipped security releases on 20 and 21 October 2026: 22.23.4, 24.22.1 and 26.12.1. Ten CVEs, four rated high. The worst is an out-of-bounds write in the Brotli decoder that can lead to remote code execution (CVE-2026-111497). Two others let TLS hostname checks be bypassed, which is exactly what you don’t want in an app that calls APIs.

What people miss with Node:

  • Node 18 and 20 are end of life. Node 18 since April 2025, Node 20 since April 2026. They got no fix. “Still on 18” means “permanently vulnerable”, not “a bit behind”.
  • nvm installs are invisible to apt. Every user with nvm has their own Node. System updates won’t touch it, and node -v as root tells you nothing about the deploy user.
  • CI runners and build images count too. If your pipeline builds on an old Node image, the old Node is part of your release process.

Quick check per server:

which -a node && node -v
ls /root/.nvm/versions/node /home/*/.nvm/versions/node 2>/dev/null

Then move every install to a fixed version, rebuild the app and test it. And if nothing on a server uses Node at all, remove it. Less software, less to patch.

eMudhra certificates: update your CA bundle, and pip

On 20 October 2026, Mozilla disclosed that the private keys of eight eMudhra intermediate certificates were compromised. With those keys, someone could issue fake HTTPS certificates. eMudhra announced two days later that it is leaving the global SSL/TLS certificate business.

Firefox blocked them within hours. The slow part is everything else that keeps its own list of trusted certificates:

  • Your OS: install the ca-certificates update and restart long-running services so they reload the bundle.
  • Containers: each image carries its own CA bundle. The Python security team’s advice is to rebuild images with layer caching disabled, so the fresh certificates actually land.
  • Python, the sneaky one. Christian Heimes, a CPython core developer, put it bluntly in the Python advisory thread: “any pip < 26.2.2 will keep trusting the compromised intermediate CAs regardless of the system trust store.” Fixing the OS doesn’t fix pip.

The minimum versions from the Python advisory: pip 26.2.2, certifi 2026.10.19, uv 0.13.4.

python -m pip --version
python -m pip show certifi
python -m pip install --upgrade "pip>=26.2.2" "certifi>=2026.10.19"

Do that in every virtualenv and every image, not just once on the host. The advisory thread also has a hash-pinned pip upgrade command if you don’t want the old pip’s trust list deciding what gets downloaded.

Want to test a client? This host serves a certificate from one of the compromised intermediates. A certificate error is the good result:

curl https://dv-g3b-emudhra.we-have-a-problem.com/

Test your own laptops and desktops too, not just your servers. A system curl can lag behind the browser on the same machine.

LiteLLM 1.107.0: the Python one that wasn’t a bug

One more Python item. On 21 October, attackers hijacked LiteLLM’s release pipeline and published litellm 1.107.0 with a credential stealer. It sat on PyPI for under two hours. On Docker Hub, latest, main-latest, main-stable and v1.107.0 served it between 11:46 and 14:20 UTC.

If you installed or pulled it in that window, follow LiteLLM’s cleanup steps and assume every key on that machine is stolen. Then pin explicit versions. Floating tags like latest are how this one reached people who never chose 1.107.0.

How I protected my customers

I run managed hosting for my customers, so that week was mostly the boring list above, done everywhere at once. Here’s what it looked like on my side. No heroics.

PHP first.

  • Full OS updates across every customer server.
  • PHP security builds installed for every PHP version in use, the old branches included, then PHP restarted so the fixed code actually ran.
  • Where the fixed versions were already in place, I checked them instead of assuming.
  • Servers with new kernels waiting got rebooted and checked afterwards. Dedicated servers never reboot on autopilot: each one needs my explicit OK and a check-in with the customer first.

Then Node.js.

  • Some servers still had end-of-life Node.js as the system version. They moved to a fixed release, and I tested customer builds afterwards.
  • Per-user nvm installs, the CI runner and its build images got the same update.
  • Where Node was installed but nothing used it, I removed it.

Then certificates and apps.

  • The ca-certificates update took the compromised eMudhra root out of the trust bundle.
  • Self-hosted apps got their updates, each one backed up first and checked healthy after.

The groundwork mattered more than any single tool. Every server is managed centrally, so “patch everything” is one run instead of a login per server. SSH host keys are pinned, and password login over SSH is off everywhere.

If you’d rather have someone else watch for the next week like this, that’s what Managed Server Support is for. And if you run your own servers, start with the basics in my Linux server security fundamentals guide.

Your PHP2Shell, Node.js and certificate checklist

  1. Find every PHP: servers, panels, containers, NAS boxes, firewalls.
  2. Update PHP to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or your distro’s fixed package.
  3. On Ubuntu or AlmaLinux, check the changelog for CVE-2026-109618. Don’t trust the version number.
  4. Restart PHP-FPM or Apache. Check the PHP that serves the site, not the one in your terminal.
  5. Containers: docker compose pull && docker compose up -d. Not a restart.
  6. PHP 8.1 or older: get a vendor build or move to 8.3+. If neither is possible today, take it offline.
  7. Node.js: 22.23.4, 24.22.1 or 26.12.1, in every nvm install and CI image. Retire 18 and 20.
  8. Certificates: update ca-certificates, browsers and images. In Python, pip 26.2.2 and certifi 2026.10.19 or later.
  9. LiteLLM users: make sure 1.107.0 never ran. If it did, rotate every key.
  10. Remove the runtimes nobody uses.

On Ubuntu or AlmaLinux, don’t trust the version number. Check the changelog.

Frequently Asked Questions

Want this handled, not just understood?

Reading the playbook is one thing. Running it on production at 2am is another. If you'd rather have me run it for you, the door is open.

Apply for Access