~/satyajit

The week of 14-23 October 2026: php2shell, a poisoned LiteLLM, a CVSS 10 in GitLab, and eMudhra's weak CA keys

mdjsonmcp

2026-10-23 · 42 min · security · disclosure · infrastructure

A 1:55 narrated explainer, drawn in code. Every number and picture in it is this article's own; the sources are below.

› transcript

Hi, I'm Vellum! Broken certificate keys, two PHP bugs, a poisoned AI gateway release, and leaked platform secrets. Here's how they fit. The idea: rank by who is exploiting a bug and who can reach it, not by CVSS alone. A trusted root signs an intermediate, whose private key should be a huge random number. These keys were tiny. Anyone could recompute one and sign a certificate for any site. Browsers pushed blocklists within hours. Software with its own trust store needs an update. The two PHP bugs sit in how a worker handles requests. A request arrives, and PHP sets up its internal state. An error stops the request part way through. Cleanup, or the next request on that worker, trusts memory that is half-built or already freed. The fixed releases reset that state. Restart every worker, or the old code keeps running. LiteLLM shipped a malicious release. Here is its path. Attackers held stolen developer credentials with release access. They pointed the release workflow at a lookalike repository. The wheels and Docker images, including latest, were out for about two hours. Planted files run whenever Python starts, so installing was enough. Sorting by CVSS puts the scariest worst case first. Sorting by risk asks: is it exploited, does it need a login, can the internet reach it? The leak said ten. Both PHP bugs are published at nine point two. Update, restart, and rotate. A patched file protects nothing until the process is new. A leaked key keeps working until you rotate it. Weak keys break trust. Pin releases, rotate secrets. Rank by exploitation and reach. Every source is in the full article. I'm Vellum. Bye!

Why read this

Solidtop 85%

Each item checked against the CVE record, KEV, EPSS and the vendor's own notice, ranked by exploitation and exposure, with a local version checker.

  • Original analysis
  • Widely used
  • Concrete numbers to act on

Security & safetyNothing to runPractitioner roundup

How this was scored
Is it new?
1 of 3: An incremental tweak
Can I trust it?
2 of 3: Measures key facts from files, code or configs
Can I run it?
1 of 3: API-only, gated or restrictive licence
Will I understand it?
2 of 3: Mechanism from first principles with figures
Can I act on it?
2 of 3: A concrete recipe, numbers or comparison
Will it last?
1 of 3: Relevant for months
Does it affect many?
2 of 3: A widely used model, tool or lab release
Only here?
2 of 3: A teardown or measurement few others did

Score 59 of 100, ranked 383 of 607 rated articles. Each question is answered 0–3 by hand, and a 3 is rare. How articles are scored

This is a defender's write-up of the past week: the eMudhra keys, the PHP RCEs, and whatever else carried a CVSS 10 or was being exploited. For each item: the class, affected and fixed versions, severity as published, exploitation status, who is exposed, and how to check. Nothing here says how to exploit anything. Where a public exploit exists I say so and stop.

I worked from primary records: CVE records from the CVE Services API (cveawg.mitre.org), CISA's KEV JSON feed (catalog 2026.10.21), FIRST's EPSS API (22 October), vendor advisories, and the Mozilla bug where eMudhra is being worked. Every number is labelled measured (I read it from one of those files), reported (a vendor or researcher says so, and I could not re-check it), or reasoned (my own arithmetic on the other two). All times are UTC.

Updated 23 October. A widely shared post by fiveoutofnine listed a dozen incidents from the same days, and my first version missed several. The big misses were the LiteLLM supply-chain compromise, which made my "the AI stack was quiet" line wrong, GitLab's CVSS 10.0, and breaches at Railway, TiDB Cloud and Zeabur. I checked each claim against a primary source. What holds is added below. What I could not source is under "Reported only", not in the text.

Updated again, 23 October. Another post counted 30 CVEs "reported in a SINGLE day" across PHP, Node.js, NGINX, Go and GitLab. This article had 6 of them. Every CVE of 21 October now lists all 30 with severity as published, versions, exploitation and KEV status. None is exploited or in KEV, so the ranking is unchanged.

Updated 24 October. Aikido renamed the Brotli bug "HTTP2Shell" as other stacks were found to carry it. The new subsection lists each ecosystem's affected and fixed versions from its own advisory, with how to check. Exploitation status is unchanged, so the ranking holds, with one new row on the board.

The board, and why it is not sorted by CVSS

CVSS describes a bug's worst case in isolation. It does not say whether anyone is exploiting it or whether your copy is reachable. An unexploited 9.8 behind a VPN is a smaller problem this week than an 8.6 on an internet-facing gateway that CISA has watched being exploited. So the board ranks by three published facts:

The total runs from 0 to 7. CVSS is shown, not added. Switch to "CVSS only" and watch GitLab's 10.0 jump to the top and LiteLLM, which has no score at all, fall to the bottom.

The weights are a judgment call, shown rather than hidden. A malicious release counts as no access needed and full reach: whoever installed it already ran the attacker's code. Ties break on how direct the exploitation evidence is, then on CVSS. So LiteLLM 1.107.0 comes first, then the five unauthenticated, internet-facing KEV items. Then Plex, PHP and the three platform breaches at 6. eMudhra's public but browser-blocked keys come next. GitLab's 10.0 sits mid-table: maximal severity, no reported exploitation.

eMudhra: eight CA keys anyone could recompute

What happened

eMudhra is an Indian certificate authority whose emSign roots are trusted by the Chrome, Mozilla, Apple and Microsoft root programs. On 17 October at 18:05 a researcher who goes by Wayne emailed all four and eMudhra. He reported that the private keys of eight eMudhra intermediate CA certificates were recoverable, with signed proof that he held all eight (Mozilla Bugzilla 2083449, reported).

The class of bug is simple to state. A P-384 private key is meant to be a random integer between 1 and a 384-bit group order. Mozilla's Dennis Jackson recovered the keys himself at 21:48 without the reporter's method, then wrote in the bug:

The 8 impacted keys all used integers less than 256. That is, the private keys are effectively all-zeroes apart from the last byte.

The key space is about 23842^{384}; these keys came from 256 values. The curve and the signatures are fine. The keys were generated badly. Mozilla noted local AI agents recovered them on the first try. Several people recovered them independently, some publicly, which is why Mozilla escalated on 20 October (reported, comment 18). Scott Helme's write-up lists all eight values. One TLS intermediate's key was 4, and the two code-signing keys were 66 and 237, as the post said (reported, consistent with Mozilla's "less than 256"). Everything is revoked and blocklisted, so the numbers no longer open anything.

Which certificates: six TLS intermediates, EM DV/OV/EV TLS CA - G3B and - G3C, under emSign Root TLS CA - G3, and two code-signing intermediates trusted only by Microsoft. Sectigo had cross-signed the six under a root Apple and Microsoft still trust, and revoked the cross-signs on 19 October (comment 13). All eight came from a 30 April 2025 ceremony that made 24 intermediates; only the P-384 keys of the B and C series were weak (comment 5).

Who did what, and when

Time (UTC)EventSource
17 Oct 18:05Report sent to the root programs and eMudhraBugzilla, comment 2
17 Oct 19:45Chrome ships a CRLSet blocking the affected intermediate keysGoogle
17 Oct 22:59Firefox OneCRL block completeMozilla
18 Oct 09:24-09:38eMudhra revokes all eight (cACompromise)Bugzilla, comment 10
19 Oct ~22:00Android equivalent via Google Play servicesGoogle
20 OctFirefox 158.0.1, ESR 115.43.1 and 153.5.1; NSS 3.131.1 drops the rootMozilla
20 OctMozilla and Chrome announce emergency removal of emSign ECC Root CA - G3; eMudhra stops issuing from itBugzilla, comments 18-21
22 OcteMudhra announces it will leave the global TLS certificate businesseMudhra press release
27 Oct (PDT, planned)Root removed from the Chrome Root Store on all devicesGoogle

Apple's block on 19 October and Microsoft's on 20 October appear only in news reports. I could not find either vendor's own notice, so treat those two dates as reported second-hand.

What is and is not affected

Google says no certificate chaining to the affected intermediates was in Certificate Transparency before the block, so no forgery could have worked against a CT-enforcing client (reported). eMudhra says seven of the eight were never in production and the eighth issued nothing (status page, reported). That is not "no harm". A forgery needs no log entry to fool a client that skips CT and revocation checks, which is a lot of non-browser software. Cloudflare shipped fixed WARP, Cloudflare One and cloudflared (2026.10.2) builds for exactly that reason (write-up).

The root removal is what breaks legitimate sites. eMudhra estimated "about 30,000 certs that require migration", 97% via ACME (comment 19). Any chain ending at emSign ECC Root CA - G3 fails in Chrome once the 27 October removal lands. Google's instruction: "immediately acquire and install a new TLS certificate from another PKI hierarchy".

For India, MeitY said the issue is limited to eMudhra's global website-certificate business and does not touch the DSC and eSign services the Controller of Certifying Authorities regulates (reported, Business Today). eMudhra agrees. I found no statement from the CCA itself. As far as I can tell, Indian DSCs chain to the CCA's own root, not to eMudhra's browser roots. If so, nobody filing returns with a USB-token DSC has anything to change (reasoned, not confirmed by the CCA).

How to check

How keys made under an HSM key-generation policy ended up this weak is an open question on the bug. eMudhra's account, a faulty wrap and unwrap step in the ceremony script, is preliminary and unverified.

PHP: php2shell, CVE-2026-109618 and CVE-2026-109619

The leak, and the 10.0 that wasn't

Details leaked before the patch. watchTowr says information circulated on 20 October, ahead of the coordinated date, and that it confirmed the bugs were real (reported). PHP's notice that day reserved "the right to release earlier should details of either vulnerability become public". On 20 October a social post said PHP core had an RCE rated CVSS 10.0, with details due that day. PHP's own pre-announcement that day said "two critical vulnerabilities, both rated CVSS v4.0 9.2" and set the release for 21 October at 14:00 (reported, php.net). The published CVE records agree with PHP. Both carry CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N, score 9.2 (measured). The "high complexity" and "attack requirements present" in that vector are what keep it under 10. The CVSS 10.0 figure came from the leak, not from the advisory.

What they are

Both are memory-safety bugs in how PHP keeps state across the request lifecycle. Neither needs application code. In the CNA's words, from GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp:

Both records list CWE-416 (use after free), CWE-755, CWE-824, CWE-1325 and CWE-1419 (measured). Both credit Wiz, Ryan at Calif.io and OpenAI as reporters. The fixes are credited to four PHP core developers. Wiz says it reported to PHP on 24 August (reported).

Affected and fixed

BranchFixed inStatus
8.58.5.12supported
8.48.4.27supported
8.38.3.36supported
8.28.2.35supported until 31 December 2026
8.1, 8.0, 7.4no releaseminimal source backports from PHP; check your distro or LTS vendor
7.3 and oldernoneaffected per watchTowr; upgrade

The CVE records list only 8.2 through 8.5, each as "affected below the fixed version" (measured). PHP's news post adds the 7.4, 8.0 and 8.1 backports. watchTowr says older branches back to PHP 5 are affected too (reported). PHP 8.6.0 RC4 carries the fixes; earlier 8.6 builds do not.

PHP says every installation serving web requests is at risk, including PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. It names mod_php the highest risk, "with a demonstrated exploit where an unauthenticated request can trigger the attack before any application code runs" (reported). The CLI is out of scope, since the bugs concern persistent workers serving many requests.

Exploitation status

The sources disagree, so here they are separately:

So attempts are reported, not verified, and neither CVE was in KEV when I re-read the feed on 23 October (measured, catalog 2026.10.21).

The response: offline before patched

What made this week unusual was governments telling people to unplug before the fix existed:

Then the sites went dark:

Nearly all were precautions, and most came back within a day. A day of downtime against unpatchable code on servers anyone could reach.

Which applications. The bugs sit below application code, so everything on a vulnerable PHP inherits them: WordPress, Drupal, Joomla, MediaWiki, Magento, Laravel (reasoned). Wiz demonstrated unauthenticated RCE against WordPress, Drupal and Joomla (reported). No source I found names Magento or Laravel; that changes nothing.

How many servers. I could not find the post's Censys figure of "about 1.5 million servers". CybelAngel counts 2.24 million internet-facing PHP hosts, 59% on branches with no fix coming, and calls it provisional (reported). Order of magnitude only.

Mitigation, if you cannot update today

PHP is blunt about this: "There are no complete mitigations, updating is the only safe choice." Its stopgaps narrow specific paths. In PHP's own terms:

CISA's line for unsupported versions is harsher: take PHP 8.1-and-older systems that cannot be updated offline. Restart is the step people forget. A patched package on disk does nothing for an FPM pool that has been running since Tuesday.

How to check

Check the version that is actually running (php -v, or phpinfo() behind authentication) against the table. For a distro package, look for the two CVE ids in its changelog. A backport keeps the old version number, which is why the checker below says "check your vendor" rather than "vulnerable" for a distro build. For detection, the indicators PHP worker processes produce are generic. CISA suggests watching for PHP workers that start shells or download tools. Wiz suggests watching for unusual multipart requests followed by memory-limit errors or repeated HTTP 500s, unusually large or malformed PHPSESSID cookies, and unexplained worker churn (reported).

The checker is pure version comparison against the advisories' ranges for PHP, Node.js, GitLab, NGINX, Go and LiteLLM. For Node.js and GitLab it counts how many of the release's CVEs still apply to your version. Nothing leaves the browser.

LiteLLM 1.107.0: a poisoned release, the second this year

This should top a Python shop's list. LiteLLM puts many model providers behind one API, so its proxy usually holds every provider key an organisation has. The maintainers' incident report is the source below unless marked.

Indicators the maintainers publish: cook.pth, cook.py and sitecustomize.py in site-packages, matching __pycache__/cook*.pyc and sitecustomize*.pyc files, a litellm-1.107.0.dist-info directory, the lock file /tmp/remote_code_loader.lock, and the image digest sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e. They warn that not finding these files does not prove a host is clean.

What to do, in the maintainers' order:

  1. Do not run pip install first in an affected environment, because Python startup re-runs the planted files. Remove them, and stop the detached Python process holding the lock file.
  2. Rebuild environments and images from scratch, pinned to litellm==1.106.2 or another release from before the incident.
  3. If 1.107.0 ran on a host with outbound internet access, assume its data left.
  4. Rotate every secret that host could reach: the instance or task IAM role, every provider key in the proxy database, the master and virtual keys, the DATABASE_URL password, and anything else in the process environment.
  5. On AWS, look in CloudTrail for GetCallerIdentity from the instance role around install time, and for that role's credentials being used from outside your infrastructure.

Pin by version and by digest. A tag called latest is a promise someone else keeps.

Three hosting platforms, three breaches

Three platforms disclosed breaches within a week. Railway ties its incident to a series of attacks on platform companies aimed at AI inference providers. That is Railway's framing; I do not link the three beyond it.

On any of these, the response is the same: rotate every credential that lived in the platform, at the provider that issued it.

GitLab: a CVSS 10.0 in every self-managed release since 7.0

WordPress "XSS2Shell": CVE-2026-64638 is August's bug with a new exploit

A post on 22 October announced "XSS2Shell", a pre-auth XSS-to-RCE chain in WordPress, with a public PoC repository I am not linking. The advisory side:

So this is not new: a two-month-old fix got a public exploit. Sites with background updates were patched in August; check that yours took it.

Node.js: Node2Shell, CVE-2026-111497, and two TLS bypasses

24 October: HTTP2Shell — the same Brotli bug, beyond Node

On 23 October Aikido renamed the bug "HTTP2Shell", saying it leaves "many libraries and applications using HTTP vulnerable to pre-auth RCE via Brotli decompression", and that fixes were "not yet available for all affected services". It is the same CVE and the same flaw. Nothing new was found on 23 October. What changed is that people noticed the Node.js advisory described a library bug: the CVE record has always named Google's Brotli 1.0.3 through 1.2.0, "on all platforms", and Node was only the first consumer to ship a fix. On 23 October the record's affected list still named only google/brotli. Its update added three references: the PyPI advisory, an oss-security post making the same point, and Debian's fix (measured). Aikido's write-up relays OpenAI's account that the root cause was fixed in Brotli's source in February without being recognised as a security bug, and that no release carried the fix until 21 October (reported).

Brotli is only reached where something decodes it, and decoding is opt-in in most stacks. That is the exposure question for every row below.

EcosystemAffectedFixedPrimary sourceDecodes untrusted Brotli when
libbrotli (C, google/brotli)1.0.3–1.0.9, 1.1.0, 1.2.01.0.10, 1.1.1, 1.2.1, tagged 21 OctoberCVE record (Google as CNA); upstream tags (measured)Whatever links it calls the decoder
Python Brotli (PyPI)1.0.3 to 1.2.01.2.1, on PyPI 22 October 13:57GHSA-9v8h-qqgm-4m35; PyPI (measured)It is installed and a library uses it (next row)
Python brotlicffithrough 1.2.0.21.2.1.0, on PyPI 22 October 16:19GHSA-9v8h-qqgm-4m35; PyPI (measured)As above
urllib3 (so requests), httpx, aiohttpNo advisory of their own; they load whichever of the two packages is installedUpdate the Brotli packageTheir source (measured)urllib3 and aiohttp clients then advertise br. httpx decodes a br response. aiohttp's parser, which its server shares, decodes br too
Node.js22, 24, 26 below the fix22.23.4, 24.22.1, 26.12.1Node.js release (above)fetch() and body parsers (above)
Bunbefore 1.4.41.4.4, 22 OctoberBun's release notes list "Brotli" among the security fixes and name no CVE; the mapping is Aikido's (reported)Not stated
Envoybuilds pinning Brotli 1.2.0 (1.1.0 on 1.36)1.40.0, 1.39.3, 1.38.6, 1.37.8; 1.36.12 on an unsupported branchEnvoy advisory GHSA-3wvj-j6qm-62mr (not readable from here); the pinned Brotli at each tag (measured)A Brotli decompressor filter is configured
Istio1.29, 1.30, 1.31 below the fix1.29.9, 1.30.6, 1.31.2, 15 OctoberIstio release notes (measured)"Users who enable Brotli decompression, for example with an EnvoyFilter"
Debianbookworm 1.0.9-2, trixie 1.1.0-2; sid unfixed1.0.9-2+deb12u1 (DLA-4855-1), 1.1.0-2+deb13u1 (DSA-6582-1)Debian security tracker (measured)Debian's nodejs uses this system library
Ubuntuevery release "Needs evaluation", priority Mediumnone yetUbuntu CVE page (measured)n/a

Envoy fixed it early, quietly. Its 6 October releases moved the pinned Brotli from 1.2.0 to an unreleased upstream commit of 30 September, two weeks before the CVE existed (measured, from bazel/repository_locations.bzl at each tag). Aikido's line that advisory databases "don't map the CVE to either PyPI package" was out of date by the time I read it: GHSA-9v8h-qqgm-4m35 names Brotli and brotlicffi with their fixed versions, and OSV carries it as PYSEC-2026-4322 and -4323 (measured, 24 October). Aikido also reports that Apache's mod_brotli and NGINX do not decode Brotli by default (reported). The oss-security post calls an OpenSSL path through compressed certificates "conceivable", which is a guess, not a finding.

Does it move up the board? No. The CVE record's CISA entry, updated on 23 October, says Exploitation: none, Automatable: no. It is not in KEV (catalog 2026.10.23), and EPSS is 0.0048 (measured). The only public code-execution claim is still Imperva's unreliable result on one Node build. "Pre-auth RCE across HTTP" describes the class at its worst, not anything observed. I added one row to the board for the library outside Node. It scores 3: no exploitation, no authentication needed, and reach conditional on a decoder being switched on. Node's row did not move. If a vendor reports attempts, or it lands in KEV, the row climbs to 5 or 6.

How to check. Python: python -m pip show brotli brotlicffi in every image and virtualenv, including the ones you never chose it for. It arrives as an extra of urllib3[brotli], httpx[brotli] and aiohttp[speedups]. System library: dpkg -l 'libbrotli*' or rpm -q brotli, then trust the distro's fixed version, not the upstream number, because a backport keeps the old one. curl -V prints the brotli/x.y.z it links. Anything that vendors or statically links Brotli needs its own rebuild: Node, Bun, Envoy, compiled Go with cbrotli, and your own binaries. The checker above now takes Python and C library versions. Where a product has no fix yet, Aikido's advice is to turn Brotli decoding off until it does (reported).

Every CVE of 21 October

On 22 October a post counted the CVEs "reported in a SINGLE day": PHP 2, Node.js 10, NGINX 5, Go 1 and GitLab 12, plus eMudhra's CA compromise. Satyajit asked whether this article had all of them. It had 6 of the 30: both PHP bugs, three of Node's and GitLab's critical one (reasoned). This section adds the other 24, each from the vendor's advisory and the CVE record, with the same rule as everywhere else here: class, versions, exposure, fix and how to check, and no exploit detail.

The counts hold; the "single day" mostly does. I count 2, 10, 5, 1 and 12, so 30 CVEs (measured). PHP, Node.js and GitLab published on 21 October. NGINX did not: its five fixes shipped on 14 October in 1.31.7 and 1.30.6, the CVE records went public that day, and the 21 October release, 1.31.8, carries no security fix (measured, from nginx.org's changelog and the records). Go released 1.27.3 and 1.26.10 on 19 October by its release history, and the CVE record went public on 21 October at 00:52 (measured).

None of the 24 changes the ranking. None of the 30 is in KEV (catalog 2026.10.21, re-read on 23 October). No vendor reports exploitation, and every record that carries CISA's SSVC entry says Exploitation: none (measured). Outside PHP, the highest EPSS on 22 October was 0.00554, for NGINX's CVE-2026-93479 (measured). On the board above, NGINX and Go come in at 3 out of 7: unauthenticated, but each needs a specific configuration or code path. That puts them below GitLab's 10.0, which scores 4.

The filter is the useful part: "No login needed" narrows the 30 to 17, and "Published severity" puts GitLab's 10.0 first, then the five rows rated critical at 9.2: PHP's two and NGINX's three (measured, from the table's own rows). Node.js publishes a word, not a vector, so its rows sort by that word.

Node.js: ten CVEs, one release

Every row is fixed in 22.23.4, 24.22.1 and 26.12.1, released 21 October; end-of-life lines get nothing (release). Severity is Node's own word. Only the Brotli bug has a public CVE record, from Google as CNA; on 23 October the CVE API returned "does not exist" for the other nine, so they have no vector yet (measured). None is in KEV, and Node reports no exploitation.

CVEComponent and classNode.js severityLines affected
CVE-2026-111497Bundled Brotli decoder, out-of-bounds write (CNA: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/..., 9.2)high22, 24, 26
CVE-2026-105482dns.resolve truncates names that contain a NUL characterhigh22, 24, 26
CVE-2026-105483TLS hostname bypass for IPs written in fullwidth digitshigh24, 26
CVE-2026-106069TLS hostname bypass when the host loosely resolves to an IPhigh22, 24, 26
CVE-2026-105481HTTP keep-alive socket processes a response written before the request was sentmedium22, 24, 26
CVE-2026-106072DNS SANs accepted for non-canonical IPv4 endpointsmedium22, 24, 26
CVE-2026-106074crypto.randomUUID() and randomInt() entropy caches saved into startup snapshots and SEAsmedium22, 24, 26
CVE-2026-106070ZipFile returns attacker-controlled prefix bytes after a failed header checklow26
CVE-2026-106071Permission Model filesystem over-grant via wildcard lookuplow26
CVE-2026-106073Permission Model bypass with fs.realpathSynclow22, 24, 26

That is 10 CVEs on 26.x, 8 on 24.x and 7 on 22.x (measured, from the release page's per-issue lines). Four are rated high; the Brotli bug and the two TLS bypasses are covered above. The fourth high, dns.resolve, matters where a host name an attacker controls reaches a DNS lookup, as with webhook targets or user-supplied URLs (reasoned). The three TLS items together weaken what a Node TLS client checks about the host it reached. So they matter to any process that makes outbound TLS connections to targets someone else can influence (reasoned). Two only bite in specific setups. The entropy cache affects apps shipped as startup snapshots or single executable applications (SEAs): every process starts with the same cached values, so UUIDs repeat. The Permission Model bugs matter only if you run with --permission and rely on it as a boundary. How to check: node -v in every image, then the checker above.

NGINX: five CVEs, released 14 October

Fixed in NGINX Open Source 1.31.7 (mainline) and 1.30.6 (stable). F5's records list NGINX Plus fixes in 37.0.5.1 and 37.1.0.1, and in 37.0.7.1 and 37.2.0.1 for the HTTP/3 bug. R33 to R36 are listed as affected with no fix on those releases (measured, F5's CVE records and nginx.org's advisories). F5's own K-articles (K000162416, K000162607, K000162613, K000162606, K000162608) would not render without JavaScript, so the Plus details come from the records F5 published as CNA.

CVENeeds this configurationClassCVSS (F5, CNA)nginx.orgVulnerable (Open Source)
CVE-2026-15176alias with rewrite, and $request_filename in set, return or a log formatHeap buffer overflow in a workerCVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/... 9.2; v3.1 8.1medium0.1.29 to 1.31.6
CVE-2026-93479map with regex captures used before the map's output, in root or a log formatHeap buffer overflow in a workersame as above, 9.2; v3.1 8.1medium1.31.2 to 1.31.6, 1.30.0 to 1.30.5
CVE-2026-97205aio on, serving SSI pages with include virtualUse after free in a workersame as above, 9.2; v3.1 8.1medium0.8.11 to 1.31.6
CVE-2026-97629ssl_session_cache shared:… without an ssl_session_ticket_key fileUninitialised ticket key, session forgeryCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:L/VA:N/... 8.3; v3.1 6.5medium1.23.2 to 1.31.6
CVE-2026-95101HTTP/3 with proxy_request_buffering onData injected into the proxied request bodyCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/... 6.9; v3.1 5.3medium1.25.0 to 1.31.6

nginx.org rates all five medium, while F5's records score three of them 9.2, critical under CVSS v4.0 (measured). The gap is the precondition. Each bug needs the configuration in the second column, and F5 says the three memory-safety ones crash a worker, with code execution possible only where ASLR is disabled or bypassed (reported). The session-forgery bug can bypass client-certificate verification, so it matters most to mutual-TLS deployments. None needs a login. EPSS on 22 October ranged from 0.00175 to 0.00554 (measured).

F5's workarounds, if you cannot upgrade today: avoid alias in that location and avoid $request_filename; use named captures in map regexes; turn off aio or stop serving SSI; load a static key with ssl_session_ticket_key, or drop the shared cache (servers with ssl_session_tickets off are not affected); and set proxy_request_buffering off where HTTP/3 is on. How to check: nginx -v for the version, then nginx -T to dump the effective configuration and look for those directives. Packages, container images and ingress controllers that bundle NGINX ship on their own schedules, so check the version they actually run (reasoned). The same day, njs 1.0.2 fixed two more CVEs (CVE-2026-101025, CVE-2026-101019) in its XML module; the post did not count them.

Go: one CVE, and it is eMudhra again

CVE-2026-111943 (GO-2026-6809) is in crypto/x509's Certificate.Verify. The Go CNA files it under CWE-299, improper check for certificate revocation, and publishes no CVSS score. It affects every Go before 1.26.10 and 1.27.0 to 1.27.2, and is fixed in 1.26.10 and 1.27.3 (measured). Go's announcement says only that it is "blocking trust in a small set of intermediate certificates due to specific security concerns around their operation", without naming them. The fix (CL 851047) adds a denylist of eight SHA-256 hashes of public keys. I downloaded the six eMudhra TLS intermediates, EM DV/OV/EV TLS CA - G3B and - G3C, from crt.sh and hashed their keys: all six are in Go's list (measured). The other two are presumably the two code-signing intermediates; I did not confirm that. So the post's "Go — 1 CVE" and its "eMudhra" line are the same incident, counted twice (reasoned).

Who needs it: Go mostly trusts the system's roots, and Go says the denylist is for "the narrower set of cases" where the system store is not updated (reported). Go links the standard library into every binary, so the fix reaches a program only when it is rebuilt with 1.26.10 or 1.27.3 (reasoned). Check which toolchain built a binary with go version -m <binary>, or run govulncheck on the source or the binary. Not exploited, not in KEV, EPSS 0.00145 (measured).

GitLab: twelve CVEs in 19.5.1

All twelve are in the 19.5.1 patch release of 21 October. Vectors are CVSS 3.1 as GitLab, the CNA, published them. None is exploited, per GitLab, and none is in KEV (measured).

CVEArea (edition)ClassCVSS 3.1Affected from
CVE-2026-110264Regular expression compiler (CE/EE)Integer overflow, unauthenticated code executionAV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H 10.07.0
CVE-2026-15723OmniAuth-to-LDAP auto-linking (CE/EE)Improper authentication, account takeoverAV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:N 8.210.1
CVE-2026-112212OAuth/OIDC discovery endpoints (CE/EE)Shared-cache poisoningAV:N/AC:H/PR:N/UI:R/S:C/C:H/I:L/A:L 7.518.3
CVE-2026-12664GraphQL work item email field (CE/EE)Token scope not enforcedAV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N 6.818.2
CVE-2026-16255SAML/LDAP membership lock (EE)Lock not enforced on older requestsAV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N 5.912.0
CVE-2026-105777Internal response headers (CE/EE)Internal credential exposureAV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N 4.317.6.5
CVE-2026-13319Group milestones API (CE/EE)Missing authorization checkAV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N 4.315.10
CVE-2026-112378AI Gateway feature header (EE)Paid AI features without a subscriptionAV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N 4.317.8
CVE-2026-105711Merge request title rendering (CE/EE)Confidential title leaks through a shared cacheAV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:N 3.515.0
CVE-2026-14288Merge request approval policy (EE)Required approval bypassAV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N 3.117.4
CVE-2026-112326Security findings (EE)Read-only user can delete vulnerability recordsAV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N 3.117.8
CVE-2026-103515Duo admin-lock for Amazon Q (EE)Group Owner can re-enable a locked AI data flowAV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N 2.719.2

That is one critical, two high, five medium and four low; five are EE only (measured). The catch is the fixed versions. GitLab backported only the CVSS 10.0 fix to 18.11.13, 19.0.10, 19.1.9 and 19.2.9. The other eleven are fixed only in 19.3.6, 19.4.3 and 19.5.1. So an instance moved to 19.2.9 this week is clear of the critical bug but still carries all eleven others (measured, from each row's impacted versions). Of those, the two highs are the ones to weigh. CVE-2026-15723 needs a logged-in user and an instance with both OmniAuth SAML and LDAP auto-linking enabled. CVE-2026-112212 needs no login, but it needs a shared cache in front of GitLab and a user who takes part in the flow. The rest need an account and expose or change a limited amount of data. One inconsistency: GitLab's text for CVE-2026-105777 says "an unauthenticated user", while its vector says PR:L; the table gives the vector. The checker above reports, for any version, how many of the twelve still apply.

The KEV additions, 13-20 October

CISA added 15 CVEs between 13 and 20 October, 13 of them from 14 to 20 October (measured). Most are due three days after being added under BOD 26-04; two Plex bugs and the Siemens bug get 14 (measured). CVSS is as the CNA published it. KEV names no threat actors, so neither do I.

Unauthenticated, internet-facing (patch first):

Authenticated or conditional: Plex Media Server CVE-2026-96652, -96654, -96655 and -96656 (20 Oct), two SSRFs and a URL injection that chain to an admin file write, fixed in 1.43.3.10861. Windows Shell CVE-2026-96951 (13 Oct), CVSS 3.1 8.8, user interaction, October cumulative update. PAN-OS CVE-2026-0322 (14 Oct), admin code injection on the management interface, CVSS 4.0 8.6. Fortinet CVE-2025-61624 (16 Oct), admin CLI path traversal, CVSS 3.1 5.4.

Local: SiteGuard Server Edition CVE-2026-106931 (20 Oct), command injection as root or SYSTEM, CVSS 4.0 8.5. Siemens Software Center CVE-2026-110936 (20 Oct), DLL search path, fixed in V4.0.9.8.

The AI stack: not a quiet week after all

My first version said the AI stack was quiet. It was not. LiteLLM is an AI-infrastructure supply-chain compromise aimed at exactly the keys an AI team holds, and Railway frames its breach as part of a campaign against AI inference providers. Among CVEs, the one relevant item is vLLM CVE-2026-114049 (21 October): one client's token-allowlist mask can apply to another client's request. Integrity, not code execution: CVSS 4.0 6.3, CVSS 3.1 3.7, fixed in 0.31.0 (measured). It matters on multi-tenant constrained decoding.

The other AI story: OpenAI is credited on both PHP CVEs and OpenAI Codex Security on the Node one. eMudhra's keys were reportedly first broken with AI-assisted tooling, and Mozilla's local agents recovered them from a one-line prompt. One week is a data point, not a trend. Related here: sandboxing agents in Perplexity SPACE and NVX, and what a vendor's disclosure can establish in ZCode, open-sourced.

Elsewhere this week

Reported only

These claims from the post had no primary source I could find. They stay out of the analysis:

What I could not verify

What to do on Monday

  1. Search every environment and image for litellm 1.107.0 and the published indicators. Where found, rebuild and rotate everything the host could reach. First.
  2. Inventory PHP everywhere, including appliances and images. Update and restart the workers. If you cannot, apply PHP's stopgaps or go offline, as CISA and the NCSC advise.
  3. On Railway, TiDB Cloud or Zeabur, rotate the secrets you kept there, at the issuing provider.
  4. Upgrade self-managed GitLab, even if you patched on 12 October, to 19.3.6, 19.4.3 or 19.5.1 if you can. The 18.11 to 19.2 backports fix only the CVSS 10.0 bug.
  5. Patch the five unauthenticated KEV items, Ivanti and Ahsay first by EPSS, and look for compromise: they were exploited before you heard.
  6. Update CA bundles in services and images. Check that nothing you serve chains to emSign ECC Root CA - G3 before 27 October.
  7. Update Node.js to 22.23.4, 24.22.1 or 26.12.1 in every image. That clears all ten of its CVEs. Then find every other Brotli decoder: Brotli 1.2.1 or brotlicffi 1.2.1.0 in Python images, the distro's libbrotli fix, Envoy, Istio and Bun.
  8. Update NGINX to 1.30.6 or 1.31.7, or NGINX Plus to the F5 fixes, after checking nginx -T for the five configurations. Rebuild Go binaries with 1.26.10 or 1.27.3.
  9. Confirm WordPress is on 7.0.3 or a patched backport. Patch internet-facing Plex, and multi-tenant vLLM.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "The week of 14-23 October 2026: php2shell, a poisoned LiteLLM, a CVSS 10 in GitLab, and eMudhra's weak CA keys", ai.thesatyajit.com, October 2026.

bibtex
@misc{ghana2026securityweekoct2026,
  author = {Satyajit Ghana},
  title  = {The week of 14-23 October 2026: php2shell, a poisoned LiteLLM, a CVSS 10 in GitLab, and eMudhra's weak CA keys},
  url    = {https://ai.thesatyajit.com/articles/security-week-oct-2026},
  year   = {2026}
}
share