Pangram verdict · v3.3
We believe that this entire text is AI.
AI likelihood · overall
AIArticle text · 1,417 words · 14 segments analyzed
Between October 16 and 23, 2026, PHP, Node.js, GitLab, LiteLLM, OpenCode and the eMudhra certificate authority all had serious security incidents. PHP, Node.js and GitLab shipped fixes on October 21, the same day the bad LiteLLM release went out. Every advisory ended with some version of upgrade, rebuild or rotate. Doing the upgrade is the easy part. What takes time is working out, for your own accounts, where the affected thing runs, who owns it, and what it can reach. If you have an inventory you trust, that’s an afternoon. If you don’t, it’s a week of Slack threads, and this week didn’t give anyone a week. Amazon’s MadPot honeypots logged a PHP2Shell exploit attempt four days before the bugs were disclosed.
The internet this week.
Tempting, but someone still has to find the servers. The week’s incidents IncidentWhat happenedThe fixWhat you had to find outPHP2Shell (CVE-2026-109618, CVE-2026-109619)Two pre-authentication remote code execution bugs in PHP, each rated 9.2. Exploit attempts came before disclosure.PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35. The interim config workarounds don’t cover every variant, so upgrading is the only full fix.Which instances, containers and App Service apps run PHP, and which are on 8.1 or older?Node.js (CVE-2026-111497)An out-of-bounds write in the bundled Brotli decoder that can lead to a crash, memory disclosure or remote code execution, plus nine other fixes.22.23.4, 24.22.1 or 26.12.1. End-of-life release lines get no fix.Which Lambda functions, containers and VMs run Node.js, and which are on 20.x or older?GitLab (CVE-2026-110264)Unauthenticated remote code execution, rated CVSS 10.0, through an integer overflow in GitLab’s regular expression compiler. It reaches back to GitLab 7.0, released in 2014.Patch releases on October 21. No workaround for self-managed instances.Is there a self-managed GitLab nobody remembers, and can the internet reach it?LiteLLM 1.107.0 (Microsoft’s analysis)A malicious release on PyPI and in the official container images ran a credential stealer whenever Python started.Rebuild from a verified version, then rotate every credential the environment could reach.Where did it run, what identity did it run as, and what can that identity reach?OpenCode Console (TechCrunch)An intruder reached a legacy system, and AI provider keys that users had saved in the Console may have been exposed. Some users reported unauthorized charges.Revoke and reissue the keys.Where else is that key stored, and which workloads break when it’s revoked?eMudhra (The Register)Eight intermediate CA private keys were integers below 256. One was the number 4.Browsers blocked the intermediates.Do we hold certificates from this chain, and do our images still trust it? Nothing on that list is obscure. Two are runtimes that a large share of the web sits on, one is the source-control server plenty of companies still host themselves, and one is the AI gateway a lot of teams adopted in a hurry to route LLM traffic. If you run a reasonably sized estate on AWS or Azure, at least one of these was probably yours to deal with. The exploit window keeps getting shorter Mandiant tracks how long attackers take to exploit a vulnerability after disclosure. Its time-to-exploit analysis put the average at 63 days in 2018–2019, 44 days in 2020–2021 and 32 days in 2021–2022. In 2023 it was five days, and 70% of the exploited vulnerabilities Mandiant tracked that year were zero-days, exploited before a patch existed. Drew Silcock’s post Is this the new cyber normal?, which reached the front page of Hacker News on October 23, argues that LLMs have compressed this further, so the gap between a bad RCE becoming public and automated attacks against it is now hours rather than weeks.
PHP2Shell fits.
The bugs were found with help from an AI model, patched binaries appeared before the advisory after an embargo dispute, and exploit attempts started before most people could install the fix. Your exposure window is the time to find what’s affected plus the time to fix it. Most teams have spent years shrinking the second number with CI/CD, managed runtimes and automatic patching. The first number is still a spreadsheet, a Slack thread, and whoever remembers how the old GitLab box was set up. This is why the frameworks put inventory first. The CIS Controls make inventory of enterprise assets and inventory of software Controls 1 and 2; vulnerability management doesn’t show up until Control 7. CISA’s BOD 23-01 requires US federal agencies to run automated asset discovery every seven days and vulnerability enumeration every 14. If you don’t know a server exists, you won’t patch it. Working through an advisory with native tools Here is how I’d answer each of those questions with what AWS and Azure give you out of the box, using this week’s incidents as examples. Every command below is read-only.
1.
Where it runs Start with Node.js. On Lambda, supported managed runtimes get runtime updates automatically by default, so most functions on a current Node.js runtime pick up the fix without a redeploy. Two groups don’t. Functions on the deprecated nodejs20.x runtime stopped getting security patches when Node.js 20 reached end of life in April 2026. Functions deployed as container images carry whatever Node.js was baked into the image. So what you actually need is the list of functions on an old runtime or packaged as an image: # Node.js and container-image Lambda functions in every enabled region for region in $(aws ec2 describe-regions --query "Regions[].RegionName" --output text); do echo "== $region" aws lambda list-functions --region "$region" \ --query "Functions[?PackageType=='Image' || (Runtime && starts_with(Runtime, 'nodejs'))].[FunctionName, Runtime || PackageType]" \ --output text done Image-based functions are in that output because the Lambda API can’t tell you what’s inside them. For those you need your image scanner, or a rebuild. For PHP2Shell on Azure, App Service records each Linux app’s runtime stack, and Azure Resource Graph can search it across every subscription you can read: az graph query -q " resources | where type =~ 'microsoft.web/sites' | mv-expand setting = properties.siteProperties.properties | where setting.name =~ 'LinuxFxVersion' and tostring(setting.value) startswith 'PHP' | project name, resourceGroup, subscriptionId, stack = tostring(setting.value) " --first 1000 -o table Anything on PHP 8.1 or older gets no regular upstream fix, so put it at the top of the list. VMs and containers are harder, because the cloud control plane doesn’t know what’s installed on them. That’s what AWS Systems Manager Inventory, Amazon Inspector and Microsoft Defender for Cloud are for, and each one only sees the machines it’s enabled on. That coverage gap is the one that hurts most, and I’ll come back to it.
2.
Whether the internet can reach it GitLab patched GitLab.com itself. Self-managed instances are on you, and short of upgrading there’s no workaround. The one that worries me is rarely the GitLab the platform team runs. It’s the one some team stood up in 2019, or the one that came along with an acquisition, still answering on 443. Search by name and tag first, then look at exposure: # Instances that look like GitLab, with public IPs and security groups aws ec2 describe-instances \ --filters "Name=tag:Name,Values=*gitlab*,*GitLab*" \ --query "Reservations[].Instances[].[InstanceId, State.Name, PublicIpAddress, join(',', SecurityGroups[].GroupId)]" \ --output text # Security groups with an inbound rule open to the whole internet aws ec2 describe-security-groups \ --filters Name=ip-permission.cidr,Values=0.0.0.0/0 \ --query "SecurityGroups[].[GroupId, GroupName, VpcId]" \ --output text In Azure, az vm list -d --query "[?contains(name, 'gitlab')].{name:name, rg:resourceGroup, ips:publicIps}" -o table gets you the same first pass. Be careful with what this tells you. A name filter only finds servers someone named honestly, and an instance with no public IP can still be reachable through a public load balancer. Both make this hard to answer from the console in the middle of an incident. It’s much easier if you already have an inventory that holds the relationships.
3.
Who owns it Once you’ve found the server, somebody has to approve the downtime, and ideally that person knows how it was installed. Tags are the first place to look. If there aren’t any, CloudTrail can tell you who created the resource: aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=ResourceName,AttributeValue=i-0abc123example \ --query "Events[].[EventTime, EventName, Username]" \ --output table Event history only goes back 90 days, and the same is true of the Azure Activity Log. For a server from 2019 you need your trail in S3 and an Athena query, which Who Created This AWS Resource?
walks through.
Even then the creator may have left. In one case we looked at, 89 idle Azure VMs came from an acquisition and nobody on the current team could say what they were for. Resources like that are exactly where an unpatched GitLab ends up living.
4.