SecurityDevOps

21 October 2026: PHP2Shell, Node.js, GitLab, LiteLLM and Railway, and What to Patch First

October 26, 2026
14 min read
Server racks in a dim data center with scattered red warning lights among green ones
Share:

On Wednesday 21 October 2026, PHP, Node.js and GitLab all shipped critical security releases, a poisoned LiteLLM 1.107.0 sat on PyPI and Docker Hub for about two hours, and Railway told its users to rotate every secret they keep on the platform. TechCrunch called it ‘a very bad cybersecurity day’, and nothing I found links these incidents to each other.

Five days later, this is the triage I would run: one table ranked by urgency, a read-only check for each item, and what the week changes for teams that ship code. PHP2Shell and the secret rotation each get their own deep dive; this post is the map.

1. Context: What Happened Between 20 and 26 October

This was not one attack. It was a pile-up of independent disclosures in the same 48 hours, landing on a week that already had a broken certificate authority and two exploited remote-access products. Times are UTC where the source gives them.

Timeline (php.net, nodejs.org, docs.gitlab.com, LiteLLM, Railway)

12 OctGitLab fixes CVE-2026-97229 (CVSS 10): a crafted regular expression in CI/CD configuration could let an unauthenticated user run code on the server.
20 OctPHP pre-announces a release for two critical bugs, both CVSS v4.0 9.2. Railway’s attack window opens at 13:58 UTC, according to its preliminary report.
21 Oct 11:39litellm 1.107.0 is published to PyPI with a credential stealer and a remote access trojan; poisoned Docker Hub and GHCR tags follow at 11:46.
21 OctPHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Node.js 22.23.4, 24.22.1 and 26.12.1. GitLab 19.5.1 plus six backports for CVE-2026-110264. Railway publishes its disclosure.
22 OctRailway’s preliminary report: at most 1.85% of workspaces had environment variables potentially compromised. Deployments are fully restored late that day.
23 OctPHP publishes an informational advisory for CVE-2026-115560, a third RCE path the 21 October releases do not fix.
26 OctPHP rebuilds its Windows packages (8.5.12-1 and siblings) with Brotli 1.2.1 for CVE-2026-111497, the same Brotli bug Node.js fixed.

Separate incidents, not one campaign

TechCrunch’s roundup says the day’s issues were unrelated, and its Railway story says that attack appears separate from PHP2Shell. I found no source linking PHP, Node.js or GitLab to Railway or LiteLLM. The thread that does connect several incidents is the loot: Railway says its attacker wanted AI provider API keys for distillation, OpenCode blamed a ‘distillation attack’ for its 17 October key leak, and the LiteLLM payload harvests AI provider keys. LiteLLM’s own report does not tie its compromise to that campaign.

The practical consequence: there is no single fix. Runtime patching (PHP, Node.js, GitLab) and credential rotation (Railway, LiteLLM, OpenCode) are separate work streams, often owned by different people. Start both today.

2. Triage Table: What to Patch First

Ranked by what I would do first: unauthenticated remote code execution on internet-facing services and confirmed secret theft at the top, then bugs that need a specific code path. ‘Today’ assumes the row applies to you; if it does not, skip it.

IncidentWho is exposedFixed version / actionUrgency
PHP2Shell: CVE-2026-109618, CVE-2026-109619 (CVSS v4.0 9.2)Every PHP install serving web requests: PHP-FPM, mod_php, php-cgi, FrankenPHP, LiteSpeed. PHP rates mod_php highest.8.5.12 / 8.4.27 / 8.3.36 / 8.2.35. 8.1 and older: no official release; php.net posted source patches and distros such as Ubuntu shipped backports, so check the package revision, not php -v. Set session.upload_progress.enabled=0.🔴 Today
LiteLLM 1.107.0 supply-chain compromiseAnyone who installed litellm 1.107.0 or pulled v1.107.0, latest, main-latest or main-stable on 21 Oct (PyPI 11:39–13:23 UTC, Docker Hub until 14:20).Treat the host as compromised: remove cook.pth, cook.py and sitecustomize.py, rebuild, pin 1.106.2, rotate everything it could reach.🔴 Today
Railway environment variable breachEvery Railway customer who signed up before 21 Oct; Railway asks all of them to rotate.Rotate every variable and private registry credential, recreate RAILWAY_TOKEN / RAILWAY_API_TOKEN (Railway revoked all its tokens), review logs, redeploy, reconnect GitHub.🔴 Today
GitLab: CVE-2026-110264 (CVSS 10)Self-managed CE and EE from 7.0. Servers patched on 12 Oct are still affected. GitLab.com is already patched.19.5.1 / 19.4.3 / 19.3.6 / 19.2.9 / 19.1.9 / 19.0.10 / 18.11.13.🔴 Today if internet-facing
BeyondTrust Remote Support: CVE-2026-102764Self-hosted Remote Support 26.2.3 or earlier, exploited since late August. SaaS was patched on 8 Oct.Apply the BT26-05 patch and review internet-reachable appliances as potentially compromised.🔴 Today if you run it
Node.js: 4 high, 3 medium, 3 lowNode 22, 24 and 26 below the fixed releases. Node 20 and older are end-of-life and get no fix.22.23.4 / 24.22.1 / 26.12.1, then rebuild your images.🟠 48 h (today if you decompress untrusted Brotli)
Plex2Shell (Plex Media Server)Versions before 1.43.3.10861; exploitation reported since 17 Oct.Update to 1.43.3.10861 or later.🟠 48 h
eMudhra intermediate CA compromiseClients with their own trust store: older Node.js builds, Python certifi, macOS /usr/bin/curl. Browsers already block them.Update Node.js, certifi and OS ca-certificates.🟡 This week
OpenCode Console and TiDB Cloud keysProvider keys linked in OpenCode Console (1.4% of users exposed); TiDB Cloud API keys (encrypted values were exported).Rotate the provider keys. Rotate TiDB Cloud API keys too, even though PingCAP has not asked yet.🟡 This week

Urgency is my judgment, not a vendor score. Distro packages (Debian, Ubuntu, RHEL, Amazon Linux) often backport fixes without changing the upstream version number, so read your distro’s advisory before you decide a box is unpatched.

3. The Five Main Incidents in Brief

One card per incident: what the primary source says, what is still uncertain, and where to go for depth.

PHP2Shell: two unauthenticated RCEs in PHP itself

CVE-2026-109618 · CVE-2026-109619 · CVSS v4.0 9.2 · php.net

PHP’s advisory describes two separate bugs that live below your framework. CVE-2026-109618 hits the request that runs out of memory: the failed allocation can interrupt PHP while that request’s data is half-initialized, and its own cleanup then trusts the invalid state. CVE-2026-109619 is stale state: a fatal error, including memory exhaustion or an execution timeout, can leave global state pointing at freed memory, and a later request on the same worker reuses it. PHP rates mod_php the riskiest setup, with a demonstrated exploit that fires before any application code runs.

Discovery was a multi-team, AI-assisted effort: the GitHub advisories credit four reporters, Wiz says it used AI models, and press reports say Calif’s researchers used an OpenAI model. Wiz adds that every PHP version of the last decade is affected by at least one of the bugs. On 23 October PHP added CVE-2026-115560, which the 21 October releases do not fix. It needs an app that switches session.serialize_handler at runtime; php.net says apps that never do can keep upload progress on, but I would set session.upload_progress.enabled=0 anyway, because it also closes CVE-2026-109619 paths.

Patch steps, mitigations and the ini settings that do not help: PHP2Shell deep dive →

Node.js: Brotli RCE, DNS truncation and two TLS hostname bypasses

4 high · 3 medium · 3 low · nodejs.org

The headline bug is CVE-2026-111497: malformed Brotli input can write past an eight-byte buffer in the bundled decoder, and an attacker who controls data you decompress with node:zlib can cause a crash, a memory leak or remote code execution. CVE-2026-105482 lets dns.resolve*() and dns.reverse() silently cut a hostname at a NUL byte, so an allowlist check and the real lookup can disagree. CVE-2026-106069 and CVE-2026-105483 let tls.checkServerIdentity() accept a certificate for the wrong IP when the host is written as 0127.0.0.1 or with fullwidth digits.

The releases also refresh the bundled CA store for the eMudhra compromise. Node’s policy is blunt: end-of-life versions are always affected, so Node 20 and older stay vulnerable. The same Brotli CVE reached PHP on Windows through cURL’s automatic decompression, which is off by default; PHP shipped -1 rebuilds of its Windows packages on 26 October.

GitLab: a second CVSS 10 regex bug in nine days

CVE-2026-110264 · CVE-2026-97229 · docs.gitlab.com

On 12 October GitLab fixed CVE-2026-97229, a crafted regular expression in CI/CD configuration that could let an unauthenticated user run code on the server. On 21 October came CVE-2026-110264, an integer overflow when compiling a crafted regex, affecting every CE and EE version from 7.0. GitLab’s own affected range (19.4 before 19.4.3) means a server you moved to 19.4.2 on 12 October is still exposed.

The advisory does not mention exploitation in the wild, GitLab.com is already patched, and the release includes database migrations, so single-node instances get downtime. Book the window today rather than discovering it mid-upgrade.

LiteLLM 1.107.0: a signed release built from a lookalike repo

PyPI · Docker Hub · GHCR · LiteLLM, Datadog, Aikido

LiteLLM’s 23 October root-cause post, which corrects its first update, says a master key for its Agent Platform was committed to a public repo on 27 May and never revoked. On 10 October the attacker used it, and the platform’s API returned a GitHub organization owner’s GitHub CLI OAuth token. With that token and two maintainers’ tokens, the attacker disabled branch protection and pointed the release workflow at Berri-AI/litellm, a fork one hyphen away from BerriAI/litellm; the real workflow built, signed and published the result. The official v1.107.0 source tag is clean, and enterprise customers on the private release were not affected.

The wheels drop cook.pth, cook.py and sitecustomize.py into site-packages, so the payload runs on every Python start, even when nothing imports litellm. Aikido and Datadog describe an infostealer (cloud, SSH, browser, CI and AI-tool credentials, plus the proxy’s stored provider keys) and a trojan that polls for new code every hour. LiteLLM’s recommended pin is 1.106.2; 1.106.4 (25 October) is the current PyPI release.

What to rotate after LiteLLM and in which order: secrets rotation runbook →

Railway: encrypted env vars pulled, some decrypted

Disclosure 21 Oct · preliminary report 22 Oct · blog.railway.com

Railway says an attacker went after its build infrastructure from 20 October, pulled encrypted environment variables and decrypted a subset with compromised CI credentials. Its preliminary report puts the exposure at ‘at most 1.85% of all workspaces’, about 100,000 workspaces by Railway’s own count, and says user accounts were not accessed. It has not ruled out access to source code.

Railway believes the attacker wanted AI provider API keys for distillation and says it sent key lists to GitHub, GitLab, Anthropic, OpenAI, OpenRouter and Stripe for revocation. Its ask to every customer who signed up before 21 October: rotate the keys stored on Railway, roll registry credentials, remove public network access to databases, review GitHub and registry audit logs, and redeploy the latest commit. Railway also revoked every Railway access token and reset its GitHub App installations, so CLI and CI tokens (RAILWAY_TOKEN, RAILWAY_API_TOKEN) have to be recreated.

Step-by-step rotation for Railway variables, databases and provider keys →

Also that week, in one line each

  • eMudhra: eight intermediate CA certificates used weak P-384 keys recoverable by trying 256 values. Mozilla and Chrome blocked them on 17 October; eMudhra says it found no evidence of misuse (BleepingComputer).
  • TiDB Cloud: PingCAP says stolen service-account credentials gave an attacker access to internal systems from 9 September to 7 October, including source code and an exported table of encrypted API-key values. It reports no evidence that data stored in TiDB was impacted.
  • Plex2Shell: four chained Plex Media Server bugs, fixed in 1.43.3.10861 back in August, exploited against honeypots since 17 October (BleepingComputer).
  • BeyondTrust: Remote Support zero-day CVE-2026-102764, exploited since late August and attributed by Mandiant to a suspected China-nexus actor (BleepingComputer).
  • OpenCode: on 17 October OpenCode said, on X and by email, that stored provider keys of 1.4% of Console users, all with external providers linked, were exposed, and called it a distillation attack. I found no OpenCode blog post; this rests on BleepingComputer.

4. Five Implications for Teams That Ship Code

Beyond the individual patches, these are the shifts I would plan around for the rest of the year.

🤖

AI-assisted bug hunting shrinks the patch window

PHP2Shell · Wiz, php.net, AWS

Several teams found PHP2Shell with AI models in the loop, PHP gave one day of notice, and Amazon’s honeypots logged an exploitation attempt on 17 October, four days before disclosure. The gap between advisory and attack is now hours, sometimes negative. Internet-facing runtimes need a same-day patch path, not a monthly cycle.

🏭

Signed provenance proves the pipeline, not the source

LiteLLM · incident page, Datadog

The poisoned artifacts came out of LiteLLM’s real release workflow and were signed; Datadog notes the provenance recorded the malicious fork as the source. Provenance only helps if something checks that field. And branch protection did not hold because an owner token could switch it off.

🔑

AI provider keys are the loot

Railway · OpenCode · LiteLLM

Railway and OpenCode both describe attackers hunting AI provider keys, and the LiteLLM payload reads the proxy’s stored provider credentials. Any platform holding your OpenAI or Anthropic key is part of your billing perimeter: per-project keys with spend caps turn a leak into a bad day instead of a bad month.

🪦

End-of-life runtimes live on borrowed patches

PHP 8.1 and older · Node.js 20 and older

PHP 8.1 and older get no official PHP2Shell release, only php.net source patches and distro backports such as Ubuntu 22.04’s php8.1 (USN-8948-1) and, per secondary reports, Surý’s packages. Node.js says end-of-life lines are always affected. PHP 8.2 itself reaches end of life on 31 December 2026. If a base image or a legacy box still runs those, the options are a vendor backport, an upgrade, or taking it off the internet.

🔁

One patch no longer means patched

GitLab · PHP · Node.js

GitLab’s 12 October fix was superseded nine days later, PHP added a third CVE two days after its release, and PHP’s Windows packages were rebuilt again on 26 October. Re-check versions after every advisory in a series instead of ticking a box once.

5. What to Do This Week

Today is Monday 26 October. This is the order I would work in. The commands below only read versions and files: run them on every host, image and CI runner you own.

Read-only checks for the main incidents

Distro packages can hide backports behind an old version string, so treat a mismatch as ‘go read your vendor advisory’, not as proof. The LiteLLM indicators come from LiteLLM’s incident page and Datadog.

# PHP: check the binary that serves web traffic, not only the CLI
php -v
php-fpm -v        # Debian/Ubuntu: php-fpm8.3 -v (match your version)
php -i | grep -E '^(memory_limit|session.upload_progress.enabled) '
# php -i shows the CLI config; FPM pools and Apache can override it
# fixed: 8.5.12, 8.4.27, 8.3.36, 8.2.35
dpkg -l | grep -E 'php[0-9.]+-(fpm|cgi|cli)|libapache2-mod-php'   # Debian/Ubuntu: compare the package revision with your distro advisory

# Node.js, on hosts and in every image you build
node -p process.version
grep -rn --include='Dockerfile*' 'FROM node:' .
# fixed: v22.23.4, v24.22.1, v26.12.1

# GitLab self-managed (Linux package): see 'GitLab information > Version'
sudo gitlab-rake gitlab:env:info
# fixed: 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10, 18.11.13

# LiteLLM: look for the files BEFORE any Python process starts
find / \( -name 'cook.pth' -o -name 'litellm-1.107.0.dist-info' \) 2>/dev/null
ls -la /tmp/.pc_once_* /tmp/remote_code_loader.lock 2>/dev/null
docker images --digests | grep -i litellm
# bad index digest: sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e
grep -rn --include='*.lock' --include='requirements*.txt' 'litellm' .

Do not let Python run first

LiteLLM’s incident page warns that any Python start re-runs cook.pth and sitecustomize.py, pip included. Search with find, rebuild the venv or image from scratch with litellm==1.106.2, and only then use pip. A missing /tmp marker proves nothing, because containers and tmp cleanup remove it. Some Linux distributions ship a legitimate sitecustomize.py, which is why I search for cook.pth and the 1.107.0 dist-info instead.

🔥 Today (Monday 26 October)

  1. 1. Patch PHP on every internet-facing host to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 and restart every worker. Set session.upload_progress.enabled=0 in php.ini, the FPM pool or server config, not with ini_set() or .user.ini.
  2. 2. Run the LiteLLM check on laptops, CI runners and images. Any hit: isolate the host, rebuild it, pin 1.106.2 and rotate from a clean machine.
  3. 3. If you signed up for Railway before 21 October, rotate every variable and registry credential stored there, recreate your RAILWAY_TOKEN and RAILWAY_API_TOKEN, then redeploy the latest commit and reconnect GitHub.
  4. 4. Upgrade internet-facing GitLab and any self-hosted BeyondTrust Remote Support appliance.

📅 By Wednesday

  1. Node.js: move to 22.23.4, 24.22.1 or 26.12.1, rebuild every image that pins an older node tag, and start the plan off Node 20 and older.
  2. AI provider keys: rotate keys linked in OpenCode Console and check the usage dashboards at OpenAI, Anthropic and OpenRouter for spend you do not recognise. Railway published a short guide per provider.
  3. Side boxes: update Plex Media Server to 1.43.3.10861 or later. It is exactly the kind of home-lab machine that ends up on a VPN with work laptops.
  4. Trust stores: update certifi, OS ca-certificates and anything that ships its own CA bundle, so the eMudhra intermediates stop being trusted.

📆 By Friday: make the next one cheaper

  1. Runtime inventory: a list of PHP SAPI and version, Node major and GitLab version per service, so the next advisory takes minutes instead of a day of SSH sessions.
  2. Pinned, delayed dependencies: exact versions with hashes plus a release-age delay. litellm 1.107.0 was on PyPI for under two hours.
  3. Short-lived, scoped secrets: per-project provider keys with spend caps, OIDC to your cloud instead of static keys in environment variables, and no standing owner token that can quietly disable branch protection.
  4. EOL calendar: PHP 8.2 ends on 31 December 2026. Schedule that upgrade now, while you still remember this week.

6. What Is Confirmed and What Is Not (Checked 26 October 2026)

‘Confirmed’ means a primary source (vendor advisory or project page) or two independent reports. Everything else is marked as such.

🟢

Fixed versions and safe pins

PHP, Node.js, GitLab and LiteLLM versions come straight from php.net, nodejs.org, docs.gitlab.com and LiteLLM’s incident page.

🟢

Railway’s scope

From Railway’s own preliminary report: at most 1.85% of workspaces potentially exposed, a subset of variables decrypted, all customers who signed up before 21 October asked to rotate. The final forensic report is still pending.

🟡

PHP2Shell exploitation

Attempts are confirmed: Amazon’s MadPot honeypots logged one on 17 October, four days before disclosure, and more after proof-of-concept code appeared on 21 October; Cloudflare (quoted by BleepingComputer) also saw early attacks. Success is not: Amazon says it is unclear whether any attempt would have worked, Sophos retracted an early claim of a successful compromise, and Canada’s Cyber Centre said on 23 October it had not identified active exploitation.

🟡

NSA advisory

TechCrunch reports that the NSA told organizations to update immediately or take systems offline until they could. nsa.gov blocked my fetch, so this rests on that report and on other press coverage.

🟡

LiteLLM time windows

LiteLLM gives PyPI 11:39–13:23 UTC and Docker Hub until 14:20; Datadog has PyPI quarantined at 13:20 and images removed at 13:53. I use LiteLLM’s numbers because they are the widest.

⚪

Initial access at Railway (LiteLLM’s is now explained)

Railway is withholding its entry vector, and its final forensic report was not out on 26 October. LiteLLM’s first post blamed tokens taken from an internal deployment’s logs; its 23 October root-cause post replaces that with the leaked, never-revoked Agent Platform master key.

What I could not confirm

I did not test any exploit. nsa.gov and BeyondTrust’s advisory page both refused my fetch, so the BeyondTrust details come from BleepingComputer, as do the Plex2Shell, eMudhra and OpenCode details. I found no CVSS score for CVE-2026-115560, and what the press says about how Calif found the bugs is second-hand.

Golden rule

Run two queues in parallel: patch the runtimes on internet-facing hosts, and rotate secrets on every platform that touched your keys. Neither one replaces the other.

Conclusion

21 October was not one attack; it was a stress test of your inventory. Teams that could answer ‘which PHP SAPI, which Node major, which GitLab, which hosts installed litellm on Wednesday, which keys live on Railway’ within an hour are already done. Everyone else is building that list this week.

Work the red rows today, the orange ones by Wednesday, and use Friday to make the next advisory cheaper. The next ‘very bad day’ will not wait for your patch cycle, and with AI-assisted bug hunting it will probably come sooner.

This week in one screen

Patch

  • • PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35
  • • GitLab 19.5.1 or your backport
  • • Node 22.23.4 / 24.22.1 / 26.12.1

Rotate

  • • Everything stored on Railway
  • • Hosts that ran litellm 1.107.0
  • • Keys linked in OpenCode

Harden

  • • Runtime inventory per service
  • • Pinned deps + release delay
  • • Scoped keys with spend caps

Sources

Diego Rodriguez

Diego Rodriguez

Senior Full-Stack & AI Engineer

Diego has 10+ years of experience building production-grade AI-powered applications, from LLM orchestration and RAG pipelines to ML-driven risk detection and algorithmic trading systems.

Learn more about Diego →