Patch Week: PHP2Shell, HTTP/3, LiteLLM, Node.js and GitLab
Five security events in one week: unauthenticated RCE in PHP and GitLab, a Brotli RCE in Node.js, QPACK memory bombs against HTTP/3 and a poisoned LiteLLM release.

On this page
Most weeks, keeping a home lab or a small hosting setup patched is background noise. The week of 2026-10-19 was not one of them. In about 48 hours, PHP, Node.js and GitLab all shipped critical fixes, a QUIC research team published a denial-of-service class against HTTP/3 servers, and LiteLLM’s release pipeline was used to publish malware. Here is what each one is, and what to do about it, in the order I’d deal with them.
| Issue | Worst case | Fix |
|---|---|---|
| PHP2Shell (PHP) | Unauthenticated RCE, exploited in the wild | 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
| LiteLLM 1.107.0 | Credential theft and a backdoor | Rebuild on 1.106.2, rotate secrets |
| GitLab CVE-2026-110264 | Unauthenticated RCE, CVSS 10.0 | 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10, 18.11.13 |
| Node.js (10 CVEs) | RCE through Brotli decompression | 26.12.1, 24.22.1, 22.23.4 |
| Header accordions (HTTP/3) | One connection runs a server out of memory | quic-go 0.57.0, quiche 0.29.3, lsquic 4.9.0 |
PHP2Shell: patch PHP first
Update 2026-10-26: CISA has added both CVEs to its Known Exploited Vulnerabilities catalog.
PHP shipped emergency releases on 2026-10-21 for two bugs in PHP itself, both CVSS v4.0 9.2 and both reachable without a login:
- CVE-2026-109618: PHP runs out of memory part way through setting up a request, and the cleanup code trusts the half-built state it leaves behind.
- CVE-2026-109619: internal state outlives the request memory it points to, so the next request on the same worker acts on freed data.
No framework, plugin or particular application code is needed. Every way of serving PHP is in scope: Apache with mod_php (rated the highest risk), PHP-FPM, php-cgi, FrankenPHP and LiteSpeed. Details leaked on 2026-10-20, a day before the planned disclosure, and watchTowr saw exploitation attempts the day the fixes shipped. If a PHP server was on the internet and unpatched on the 21st, assume someone tried it.
- Update and restart every worker. A new package does nothing while old FPM pools or Apache children are still running. Check
php -vandphp-fpm -vseparately; they often come from different packages. - Don’t rely on configuration. PHP says no setting fully mitigates these bugs. A WAF may catch some attempts while you patch.
- 8.1 and older get no official release; use your distribution’s or LTS vendor’s package, or upgrade. Anything 7.3 or older has no fix at all, so take it off the internet.
- Find the PHP you forgot about: control panels, old phpIPAM or WHMCS installs, appliance web UIs. On shared hosting, PHP is your host’s job, but check your account isn’t pinned to an end-of-life version in a PHP selector.
- Look for PHP workers doing things PHP doesn’t do: spawning shells, running
curlorwget, writing new files into the docroot. CISA published SIGMA rules for exactly that.
A WordPress site with every plugin up to date is still exposed here; the bug is underneath WordPress.
LiteLLM 1.107.0: a poisoned release
LiteLLM is the proxy a lot of people put in front of AI model providers, which means a LiteLLM host holds the keys for every provider behind it. On 2026-10-21 an attacker used the project’s own release pipeline to publish a malicious 1.107.0. There is no CVE because nothing in LiteLLM’s code was vulnerable; the artifacts were the attack.
| Where | What | Live (UTC, 2026-10-21) |
|---|---|---|
| PyPI | litellm==1.107.0 |
11:39 to 13:23 |
| Docker Hub | v1.107.0, latest, main-latest, main-stable |
11:46 to 14:20 |
| GHCR | The same tags of litellm, litellm-database, litellm-non_root |
11:46 to about 13:51 |
Two hours is long enough for CI jobs, auto-updaters and any container that pulled latest. The package dropped cook.pth and sitecustomize.py into site-packages, which Python runs on every interpreter start, even in environments that never import LiteLLM, and even when you start pip to remove it. It stole the credentials it could reach (cloud credentials, private keys, AI provider tokens, the process environment) and started a daemon holding /tmp/remote_code_loader.lock that could fetch more code.
litellm-1.107.0.dist-info
cook.pth, cook.py, sitecustomize.py
__pycache__/cook*.pyc, __pycache__/sitecustomize*.pyc
Docker Hub digest sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339eIf you find any of that: don’t run pip first, kill the daemon, rebuild the environment or image from scratch pinned to 1.106.2, and if the host had outbound internet access, rotate everything it could reach. That means cloud instance roles, every provider key in the proxy database, the master and virtual keys, the database password and every secret in the environment.
LiteLLM’s root cause analysis is a chain of ordinary failures: developers’ GitHub CLI tokens ended up in the logs of an internet-exposed internal deployment, and three stolen tokens (one an org owner’s) were enough to disable branch protection, point the release workflow at a lookalike repository, approve the release and delete the run records. The lessons I’m taking from it:
- Pin container images by digest, not by tag.
- Lock Python dependencies with hashes, so a rebuild can’t pull a new release unasked.
- Wait a few days before taking new releases of anything that holds secrets.
- Keep the proxy itself off the internet, and give it scoped keys with spending limits.
GitLab CVE-2026-110264: CVSS 10.0, back to 2014
GitLab’s 2026-10-21 patch release fixes an integer overflow when compiling a crafted regular expression in Onigmo, the Ruby regex engine GitLab bundles. Under certain conditions it gives an unauthenticated user code execution on a self-managed server. Community and Enterprise editions are both affected, all the way back to 7.0. GitLab hasn’t said which features expose it and offers no workaround.
| Line | Fixed in |
|---|---|
| 19.5 | 19.5.1 |
| 19.4 | 19.4.3 |
| 19.3 | 19.3.6 |
| 19.2 | 19.2.9 |
| 19.1 | 19.1.9 |
| 19.0 | 19.0.10 |
| 7.0 to 18.11 | 18.11.13 |
The October 12 release is not enough. It fixed a different CVSS 10.0 regex bug (CVE-2026-97229), only for 19.2 and later. This is the fourth regex engine code execution bug the same researcher, joaxcar, reported in a month, so expect more.
The release also fixes an account takeover when SAML and LDAP auto-linking are both enabled (CVE-2026-15723) and OAuth and OIDC cache poisoning on shared caches (CVE-2026-112212). 19.4.3 and 19.3.6 carry database migrations, so a single-node install will have some downtime; take a backup and copy gitlab.rb and gitlab-secrets.json first.
GitLab reported no exploitation at release and published two Sigma rules (“Onigmo native worker crash” and “Org mode render error burst”). For a home lab or a small team, the better fix is structural: a GitLab that is only reachable over a VPN turns “unauthenticated RCE” into “RCE for people already on your network”.
Node.js: Brotli RCE and hostname bypasses
Node.js 26.12.1, 24.22.1 and 22.23.4 fix ten CVEs, four rated high. Node 20 and older are end of life and get nothing.
The headline is CVE-2026-111497: the bundled Brotli decoder treated an invalid block switch as “need more input” and kept appending bytes to an eight-byte buffer, writing past its end. Anything that decompresses attacker-supplied Brotli through node:zlib (request bodies on a server, responses from an untrusted server in a client) can be pushed into a crash, a memory leak or code execution.
The other three high-severity bugs are about Node accepting a name it should reject:
- CVE-2026-105482:
dns.resolvetruncated names at a NUL character, so you could resolve a different name from the one you validated. - CVE-2026-105483 and CVE-2026-106069: TLS hostname checks bypassed by IPs written in fullwidth digits or loose forms that resolve to an IP.
Those matter most for code that fetches user-supplied URLs (webhooks, link previews, SSRF filters). The medium fixes include a cache in crypto.randomUUID() and randomInt() that was saved into startup snapshots and single executable applications, so every process started from one returned the same first values; rebuild those artifacts on a fixed release.
Upgrading node on the host is the easy part. The real work is finding every Node you run: Docker base images, Electron apps, editors and CI runners all carry their own, and containers need a rebuild on a patched base image.
Header accordions: memory bombs against HTTP/3
FoxIO’s research, published 2026-10-20, isn’t a single bug but a class of them. HTTP/3 compresses headers with QPACK: common headers are one byte, and a per-connection dynamic table lets later requests refer back to earlier ones by index. Pack one frame with 160,000 tiny unique headers, or insert one 4 KiB entry and send thousands of one-byte references to it, and a server that doesn’t budget for the decoded size allocates thousands of times more memory than it received. RFC 9114 already requires that budget, including 32 bytes of overhead per field; seven of the ten implementations tested got it wrong.
| Implementation | Status |
|---|---|
| quic-go | Fixed in 0.57.0 |
| Cloudflare quiche | Fixed in 0.29.3 |
| lsquic (LiteSpeed and OpenLiteSpeed) | Fixed in 4.9.0 |
| Wireshark | Fixed in 4.6.2 and 4.4.12 |
| Bun 1.4.3 | Unpatched: about 4000x amplification and an unrecoverable 100% CPU loop |
| hyperium h3 | Unpatched: set max_field_section_size |
| aioquic and mitmproxy | Unpatched: --set http3=false in mitmproxy |
| Google quiche, IIS, nghttp3 | Not affected |
lsquic is the one hosting people should care about: in FoxIO’s tests, under 10 MiB of traffic filled a 5 GiB memory cap in about a second. If HTTP/3 terminates at a CDN and your origin only speaks HTTP/1.1 or HTTP/2, your origin never decodes client QPACK, but check HTTP/3 isn’t also switched on at the origin. Either way, a memory limit on the service turns “the host fell over” into “one service restarted”.
The pattern
Three of these five are memory-safety bugs in code that parses hostile input (PHP request setup, a Brotli decoder, a regex compiler), the fourth is a decompression bomb that the spec had already warned about, and the fifth needed no bug at all, just enough stolen tokens. The practical defences are the same boring ones every time: know every copy of a runtime you run, keep management tools like GitLab and LiteLLM off the open internet, pin what you deploy, and put resource limits on services so one bad request can only take down one thing.
Sources
- CISA: Critical Vulnerabilities in PHP Require Immediate Patching
- watchTowr: PHP2Shell FAQ
- LiteLLM: security incident report for 1.107.0
- Microsoft: Inside the LiteLLM 1.107.0 supply chain compromise
- The Hacker News: Critical GitLab flaw (CVSS 10.0) could allow unauthenticated RCE across 12 years of releases
- Node.js: October 2026 security releases
- FoxIO: Header Accordions: QPACK Amplification Attacks Against HTTP/3 Servers


