Skip to main content

Keeping up with an avalanche: PHP2Shell, node2shell and a week of CVEs

Critical CVEs, a hijacked release and three cloud platform breaches hit security teams between 19 and 25 October 2026. PHP2Shell and node2shell in detail, the rest of the week in brief, and how runtime context helps teams keep up.

Hannes Ullman

Hannes Ullman

bifrost security

Keeping up with an avalanche: PHP2Shell, node2shell and a week of CVEs

Some weeks a security team gets one big thing. Last week, 19 to 25 October, it got an avalanche. Two critical flaws in PHP itself, with governments taking websites offline. A remote code execution flaw in a compression library that sits inside Node.js and beneath the most common Python HTTP clients. A CVSS 10.0 in GitLab. A hijacked LiteLLM release. Three cloud platforms breached, one of them shutting down. A certificate authority whose private key was the number 4. Most of it landed between Monday and Wednesday.

Any one of these is a normal week’s work: find out where you are affected, decide what to patch first, patch, look for signs that someone got there first, rotate what might have leaked. Stacked into three days, it is more than any team can work through at once. Security engineers everywhere spent last week deciding what to leave for later, knowing the next disclosure could land before they got to it. That is the strain: not any single CVE, but the queue.

Two of the week’s flaws reach furthest, and both end in a shell: PHP2Shell and node2shell. We start there.

PHP2Shell

We published our PHP2Shell advisory on the day of the release: what the two flaws are (CVE-2026-109618 and CVE-2026-109619), what is affected, how to patch, and what an enforced AppArmor profile stops in the meantime. That advice stands. The week added three things.

The fix came early, and so did exploitation. The CVE IDs leaked about 23 hours before a fix existed, and the PHP project brought its release forward by a day, to 21 October (PHP Foundation). Amazon’s honeypots had already logged an exploitation attempt on 17 October (AWS), and CISA added both CVEs to its Known Exploited Vulnerabilities catalogue on 26 October (CISA).

Patch, or go offline until you can. CISA, the NSA, the FBI, the UK’s NCSC and CERT-EU gave the same advice: patch now, and if you cannot, take PHP systems offline or isolate them. Many took the second option. The Dutch NCSC took down its own website, and government sites in Finland, France, Germany and Belgium followed, along with the Port of Rotterdam (Reuters). Censys counts 4.18 million hosts running PHP, and versions 5.4–7.3 have no fix at all (Censys).

A third flaw. On 23 October PHP announced CVE-2026-115560, a lower-likelihood flaw to be fixed in the next regular release. Until then PHP recommends session.upload_progress.enabled=0, the same setting our advisory listed (PHP on oss-security).

Taking a site offline is a sound answer when you cannot tell where PHP runs, which version it is, and what an attacker could do from inside it. Most of the organisations that went dark last week were answering that question with the tools they had. The aim is to have the answer before you need it.

node2shell: a CVE in a library nobody chose

On the same Wednesday, Node.js shipped a security release with ten CVEs (Node.js). One of them reaches well beyond Node.js.

CVE-2026-111497 is an out-of-bounds write in the decoder of Brotli, Google’s compression library, in versions up to 1.2.0. A crafted Brotli stream corrupts memory in the process that decompresses it. Google rates it 9.2, critical, and lists remote code execution as a possible impact. The CVE record credits OpenAI’s Codex Security. Imperva’s red team, which ran code on an unmodified Node.js image, gave it the name node2shell (Imperva).

Few teams chose Brotli. It arrives with something else:

  • Node.js compiles it in. Imperva’s exploit ran through fetch() and through Express 5 with body-parser 2, which decompresses br-encoded request bodies.
  • Python gets the same C decoder from the Brotli and brotlicffi packages on PyPI, which together see more than 84 million downloads a month. Most applications never import them directly. When either one is installed, urllib3 (and with it requests), httpx and aiohttp decode br responses on their own (Aikido, OSV).
  • It works in both directions. A malicious client can attack a server that decodes request bodies, and a malicious server can attack a client that decodes responses.

No exploitation in the wild is known yet. A public proof of concept exists, but it reaches code execution only in specific lab setups. The fixes are Brotli 1.0.10, 1.1.1 or 1.2.1; Node.js 22.23.4, 24.22.1 or 26.12.1; and on PyPI, Brotli 1.2.1 or brotlicffi 1.2.1.0. Rebuild images and restart processes: a fixed package on disk does nothing for a process that loaded the old one.

Brotli is the kind of dependency an SBOM finds everywhere: inside every Node.js runtime and beneath the most common Python HTTP clients. The scanner is right that it is there. The question that sets the order of work is a different one: which of these processes decode Brotli from someone they do not trust, and what could code do inside them?

The rest of the week

PHP2Shell and node2shell were not the whole week. Every item below became public between Saturday 17 and Wednesday 21 October, and most of them landed on the same teams.

  • GitLab, CVSS 10.0. CVE-2026-110264, an integer overflow in the regular expression compiler, lets an unauthenticated user run code on a self-managed GitLab server. Every version since 7.0, released in June 2014, is affected, and lines 18.10 and older get no fix. It follows CVE-2026-97229, another CVSS 10.0 in the same compiler earlier in October.
  • LiteLLM 1.107.0, hijacked. For up to two and a half hours on 21 October, every 1.107.0 wheel on PyPI and the Docker images, latest included, carried a credential stealer and a remote-code loader that ran on any Python start, no import needed. The way in was a master key committed to a public repository in May and never revoked. It is the project’s second compromised release this year, after 1.82.7 and 1.82.8 in March (LiteLLM).
  • Railway paused new deployments for every customer. An attacker with CI credentials pulled encrypted environment variables and decrypted a subset, potentially exposing variables in about 100,000 workspaces. Railway recommends that every customer who signed up before 21 October rotate the keys stored there (Railway).
  • TiDB Cloud. PingCAP says someone used stolen service account credentials inside its systems from 9 September to 7 October and copied account data, business data and source code (PingCAP). The extortion group FulcrumSec claims 10.6 TB; PingCAP has not confirmed a volume.
  • Zeabur is shutting down. The hosting platform was breached in August and again on 14–15 October, the second time exposing 321,810 accounts and more than three million environment variable records. It will wind down by 31 December, citing the incidents alongside growth limits (Zeabur).
  • Node.js, three TLS hostname bypasses. The release that fixed node2shell also fixed three ways past TLS hostname verification, two of them rated high.
  • 333 Tor exit relays. A researcher found exit relays sending SSH traffic to interception servers, some presenting fake host keys to capture passwords. The attacker had broken into legitimate operators’ hosts and rewritten their exit policies. Tor removed the relays in early October, and the report came out on 20 October (kaba).
  • SafePay. A joint advisory from the FBI, CISA, the US Secret Service and German and UK law enforcement counts at least 600 SafePay victims across critical infrastructure sectors since August 2024 (CISA).
  • State Bank of India. India’s largest bank disclosed an “incident involving data loss” to its stock exchanges on 21 October, after customer data turned up on the dark web (filing).
  • eMudhra’s private keys. Eight of eMudhra’s intermediate CA certificates had ECDSA private keys below 256: one was 4, others were 66 and 237. Anyone could recover them by trying at most 255 numbers. Mozilla, Chrome and Apple blocked the certificates, and eMudhra revoked them (Mozilla).

How runtime context helps teams keep up

No team patches all of that in a week. What decides how a week like this goes is how fast you can sort it: which of these CVEs touch something you run, which of those an attacker can reach, and what an attacker could do on arrival. That is where bifrost starts, from the runtime up.

Prioritise from what runs. A scanner reading the SBOM finds PHP and Brotli wherever they are installed. bifrost correlates every CVE with the behaviour it has learned and evaluated for each workload, and gives one verdict per CVE: reachable, mitigated by the profile, or never loaded. For PHP2Shell, that is the list of workloads running an affected PHP version, admin tools and vendor images included. For node2shell, it separates the Python services that load the Brotli decoder from those that only ship it. What reaches your team is the reachable list, with the evidence behind each verdict.

Protect until you can patch. Patching takes days or weeks: finding every image, rebuilding, redeploying. Until then, what counts is what an attacker can do once the exploit lands. The shell in both names needs the kernel to get anywhere, and in enforce mode a profile tailored to each workload refuses what that workload has never done, before it runs:

  • spawning /bin/sh, then downloading and running tools with curl or wget
  • writing a binary, such as a miner or a botnet agent, and running it
  • reading the Kubernetes service account token to move into the cluster

A Node.js service that has never started /bin/sh does not get to start one because a Brotli stream asked it to. Each refusal is reported with its context, and a denied shell from php-fpm or node is a strong sign that an exploit got through. Our PHP2Shell advisory has the full list of steps and the interim PHP settings to apply while you patch.

Answer the next one from what you already know. The CVE after these will be assessed against the context bifrost already holds. The answer leads with protection status, then scope, exposure and the next action. Last week, many organisations answered that question by taking their websites offline. Having the answer before the next disclosure is the alternative.

Hardened before the patch. In enforce mode, the kernel blocks operations outside the profile, including those an exploit not yet discovered would need.

See how it works, or book a demo to look at the workloads you had to think about last week.

Tags

phpnodejspythoncvesupply chainapparmorruntime

Do you know what's exploitable in your environment?

Deploy bifrost via Helm and it reads how every container is actually deployed, cutting your CVE list before a single line of code changes.