2026年10月16日至23日期间,PHP,Node.js,GitLab,LiteLLM,OpenCode和eMudhra证书管理局都发生了严重的安全事件。 PHP,Node.js和GitLab于10月21日发运了修正,同日糟糕的LiteLLM放出。 每一次咨询都以某种版本的升级,重建或旋转而结束。
做升级是容易的部分。 需要时间的是工作,为你自己的帐户,受影响的东西在哪里运行,谁拥有它,以及它能达到什么。 如果你有你信任的清单,那就是一个下午。 如果你不这样做,那就一个星期的Slack线程,这个星期没有给任何人一个星期。 兹克 1Z的MadPot蜂蜜罐在虫子被披露前四天记录了PHP2壳的尝试。
本周的事件
名单上没有任何东西是模糊的。 两个是运行时间,大部分的网络坐落着,一个是源控服务器,许多公司仍然主营,一个是AI网关,如果你在AWS或Azure上经营一个合理大小的地产,至少其中有一个可能是你的。
开发窗口越来越短
Mandiant追踪攻击者在披露后利用脆弱性需要多长时间。 其开出时间分析显示,2018-2019年平均为63天,2020-2021年平均为44天,2021-2022年平均为32天。 2023年是五天,70%的被利用的弱点曼迪安特跟踪当年是零天,开采之前有一个补丁。
德鲁·西尔科克的文章这是新的网络正常吗?10月23日达到了Hacker News的头版,认为LLMs已经进一步压缩了这一点,因此坏的RCE变成公开攻击和自动攻击之间的间隔现在是小时而不是周。 PHP2壳适合。 这些错误是在AI模型的帮助下发现的,补丁的二进制在禁运争议后出现在咨询前,并在大多数人可以安装补丁之前开始利用尝试。
您的曝光窗口是找到受到影响的东西,大部分团队花费了多年时间,在CI/CD,管理运行时间和自动补丁方面缩减了第二名。 第一个数字仍然是电子表格,Slack线程,谁记得旧的GitLab盒是如何设置的。
这就是为什么框架将清单放在首位。 独联体管制局对企业资产进行盘点,对软件进行盘点;弱点管理直到控制7才出现。 CISA的BOD 23-01要求美国联邦机构每七天进行自动化资产发现,每14天进行脆弱性查点。 如果您不知道服务器存在,您不会补齐它。
利用当地工具提供咨询
AWS和Azure用本周的事件为例,下面的每个命令都是只读的。
1. 运行地点
从Node.js开始。 在Lambda上,被支持的被管理的运行时间默认会自动得到运行时间更新,因此当前节点上的大部分功能.js运行时间在没有重新部署的情况下取回固定。 两个团体没有。 已贬值的nodejs20.x运行时间上的功能在2026年4月Node.js 20到寿终期时停止获得安全补丁。 作为容器图像所部署的功能包含着任何将节点.js烤入图像的功能。 因此,你实际上需要的是一个旧运行时间或被打包成图像的函数列表:
# 节点.js和集装箱图像Lambda在每一个启用区域都有功能
以$(aws ec2)表示的区域区域Name" --output text; do
呼声"=="
aws Lambda 列表功能——区域"$区域"\?
......... 名称,运行时间 \\\ packageType]"\\
--输出文本
已执行
基于图像的功能就存在于这个输出中,因为Lambda API无法告诉你里面有什么。 对于那些你需要你的图像扫描仪,或重建。
对于Azure上的PHP2Shell,App Service记录了每个Linux应用程序的运行时间堆栈,Azure资源图形可以搜索每个订阅内容,读作:
az 图表查询 -q "
资源
“ microsoft.web/ sites” 类型
mv-扩展设置 = 属性。 siteProperties.properties.
\\ 在哪里设置。 name “ LinuxFxVersion” 和 tostring( setting. value) 起始于“ PHP ”
QQ 工程名称、 资源组、 订阅Id、 堆栈 = tostring( setting. value)
" --第一个1000-o表
PHP 8.1或更上层的任何内容都得不到正常的上游修补,所以将其放在榜首。
因为云控平面不知道安装在它们上面的是什么。 AWS系统管理者库存、Amazon检查员和Microsoft Cloud捍卫者就是这个用途,我将回到这个缺口。
2. 互联网能否达到
GitLab补丁了GitLab.com本身。 自我管理的情况会发生在你身上,我最担心的是平台团队运行的GitLab 这支队伍在2019年成立
先按名字和标签搜索,然后查看曝光:
#看起来像GitLab的事例,有公共IP和保安团体
aws ec2 描述 - 提示 -\
- 过滤器“ Name=tag: Name, Values *gitlab*, *GitLab*” \ @
——克来"保留"[. (原始内容存档于2018-09-21). Emerican [InstanceId, State. 名称,PublicIpAddress,加入 (',',安全小组 []. GroupId]" \
--输出文本
{\fn方正粗倩简体\fs12\an8\1cHFFFF00\b0}安全集团一条对网络开放的入门规则
aws ec2 描述- 安全组 \
--filters name=ip-permission.cidr,瓦卢埃斯=0.0.0.0/0==
--query "安全小组[]. [GroupId, GroupName, VpcId]"\
--输出文本
在 Azure 中,az vm 列表 - d --query "[? contains(name, 'gitlab')]. {name:name, rg:resourceGroup, except: publicIps}" -o 表格获得相同的首个通过。
小心点,这告诉你。 名称过滤器只可使用发现服务器诚实地命名,一个没有公共IP的实例仍然可以通过公共负载平衡器达到。 两者都使得这很难在事发中从控制台回答。 如果你已经有一份可以维持关系的清单,那就容易多了。
3. 由谁拥有
一旦你找到服务器,就会有人批准停机时间,标记是第一个查看的地方。 如果没有,CloudTrail 可以告诉你是谁创建了资源:
aws 云轨浏览事件
-- Lookup- 属性 Key=资源Name,属性Value=i-0abc123 实例 \
--query "Events[]. [Eventime, EventionName,用户名]"\ \
--输出表
事件历史只可追溯到90天,Azure活动日志也是如此。 对于一个2019年的服务器,您需要您在S3中的跟踪和雅典娜查询,谁创建了这个AWS资源?穿过即使这样,创造者也可能离开了。 有一次我们看,89个闲置的Azure VM来自一次收购,现在的团队中没有人能说出他们的目的。 这种资源正是一个没有被分配的GitLab最终生活的地方。
4. 它所能达到的
LiteLLM 显示为什么一个软件包的查找只是开始。 根据Microsoft的分析,恶意放出是追随AWS,Azure和Google Cloud证书缓存,Kubernetes服务账户文件,Docker配置和挂载秘密。 它还试图从AWS和GCP元数据服务中提取证书,在代理主机上从LiteLLM自己的数据库中读取提供者密钥。
坏包是扫描仪报告的内容。 你实际上必须面对的是宿主的身份对于安装了1.107.0的每一种环境,请找到它所扮演的角色以及允许它扮演的角色:
# EC2 实例背后的例子简介
aws ec2 描述- 描述- 标识 i-0abc123 实例 \
——克来"保留"[. 案例[]. Iam Instance Profile (英语). 阿恩" --输出文本
{\fn方正粗倩简体\fs12\an8\1cHFFFF00\b0}内在的角色以及相关政策
aws iam 获得 - 身份介绍 - 姓名介绍 - Littellm - 代理 \
——克瑞"InstanceProfile" (英语). 角色[]. RoleName" --output文本
aws iam 列表-附图-作用-政策-作用-名称-litellm-proxy-作用
aws iam 列表-作用-政策-作用-名称litellm-proxy-作用
在ECS上,身份是任务定义中的任务角色。 在EKS上,兹克 1Z的指针是用清洁的机器来调查,用经过验证的文物来重建,通过消化而不是像最新一样用漂浮的标记来旋转每一个可到达的证书,并用消化器来固定容器的图像。 我将强调清洁机部分:当Python发动时,有效载荷运行时
5. 秘密或证书的其他地点
OpenCode告诉用户撤销并重新发行被曝光的密钥。 撤销需要点击。 找出工作时的突破点是真正的工作,因为AI提供商的密钥最终会出现在"秘密管理器"或"密钥故障",Lambda和App Service设置,CI变量和开发者的.env文件中。 在 AWS 上,机密管理器至少记录每个秘密上次读取和最后一次旋转时,以及 CloudTrail 的 GetSecret 价值事件显示哪些角色读取:
aws 机密管理列表 - 机密\
--query "SecretList []. [名,上接地,上接地]" \
--输出表
eMudhra 融资证书的同样问题。 有了这么小的私钥,任何人都可以计算出来,并签署浏览器会信任的证书,直到中间件被屏蔽。 两张支票值得运行。 首先,搜索您导入的 eMudhra 或 emSign 的证书。 CloudFront使用的证书都住在我们东一区
以 $( aws acm 列表认证) 为单位
- 包括 keyTypes=RSA 2048,RSA 3072,RSA 4096,EC prime256v1,EC sepp384r1,EC sepp521r1\
--query "证书摘要列表[]. CertificateArn" --output文本;do
aws acm 描述- 证书- 证书- arn "$arn" \
--query "证书。 [域名,发端人,类型]" --output text
完成 Grep -i - E "emudhra" 签名
在 Azure 中,资源图覆盖了 App Service 证书的全订阅:
az 图表查询 -q "
资源
QQ 类型“ microsoft. web/ certificates ”
QQ 托斯通( 财产。 签发者) 包含 'emSign' 或 tostring( 财产。 签发者) 包含 'eMudhra' 的地方
QQ工程名称,资源组,订阅Id,发行商 = tostring( properties. isser),过期 = 属性。 experation 日期
- 一张桌子
其次,拥有自有信托商店的软件一直信任一个CA,直到该商店更新。 旧的AMI,将自己的CA列表捆绑起来的基站图像和运行时间,是被屏蔽的链条在浏览器丢弃之后长时间运行的地方。
扫描仪和控制台停止的地方
Amazon"Cloud"的督察和捍卫者擅长告诉你特定机器上的软件包版本是否脆弱。 AWS和Azure控制台很能告诉你一个地区一个账户里有什么。 你早上需要的也不是获得咨询
第一个问题是覆盖。 GitLab服务器通常坐落在账户中,第二是所有权。 调查发现,一个没有主人的发现只是一个提醒。 第三是背景情况。 扫描仪称包坏。 也没有说主持人的角色可以读取账户里的S3桶,就LiteLLM而言,这主要是事件的背景。
填补这些空白的是一份清单,它保持现有状态并记录存在的情况、谁拥有以及它如何与其它一切联系起来。 这才是我们打造"云·斯卢斯"(Cloud Sleuth)的目的。
在"云花"里的样子
Cloud Sleuth 映射出您的 AWS 和 Azure 资源,给每个资源分配一个所有者,无论它是否标记,并保持两者
本文由 AI宝库自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
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 week’s incidents
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. What it can reach
LiteLLM shows why a package finding is only the start. According to Microsoft’s analysis, the malicious release went after AWS, Azure and Google Cloud credential caches, Kubernetes service-account files, Docker configuration and mounted secrets. It also tried to pull credentials from the AWS and GCP metadata services, and on proxy hosts it read provider keys out of LiteLLM’s own database.
The bad package is what the scanner reports. What you actually have to respond to is the identity the host ran as. For every environment that installed 1.107.0, find the role it had and what that role is allowed to do:
# The instance profile behind an EC2 instance
aws ec2 describe-instances --instance-ids i-0abc123example \
--query "Reservations[].Instances[].IamInstanceProfile.Arn" --output text
# The role inside that profile, and the policies attached to it
aws iam get-instance-profile --instance-profile-name litellm-proxy \
--query "InstanceProfile.Roles[].RoleName" --output text
aws iam list-attached-role-policies --role-name litellm-proxy-role
aws iam list-role-policies --role-name litellm-proxy-role
On ECS the identity is the task role in the task definition. On EKS it’s the role mapped to the pod’s service account. Microsoft’s guidance is to investigate from a clean machine, rebuild from verified artifacts, rotate every reachable credential, and pin container images by digest instead of floating tags like latest. I’d underline the clean-machine part: their analysis says the payload runs when Python starts, so running pip inside a suspect environment to check for the package can trigger it.
5. Where else the secret or certificate lives
OpenCode told users to revoke and reissue the exposed keys. Revoking takes a click. Working out what breaks when you do is the real job, because AI provider keys end up in Secrets Manager or Key Vault, in Lambda and App Service settings, in CI variables and in developers’ .env files. On AWS, Secrets Manager at least records when each secret was last read and last rotated, and CloudTrail’s GetSecretValue events show which roles read it:
aws secretsmanager list-secrets \
--query "SecretList[].[Name, LastAccessedDate, LastRotatedDate]" \
--output table
eMudhra raises the same problem for certificates. With a private key that small, anyone could compute it and sign certificates that browsers would have trusted until the intermediates were blocked. Two checks are worth running. First, search the certificates you’ve imported for eMudhra or emSign issuers. ACM is regional, and certificates used by CloudFront live in us-east-1, so run this in every region you use, us-east-1 included:
for arn in $(aws acm list-certificates \
--includes keyTypes=RSA_2048,RSA_3072,RSA_4096,EC_prime256v1,EC_secp384r1,EC_secp521r1 \
--query "CertificateSummaryList[].CertificateArn" --output text); do
aws acm describe-certificate --certificate-arn "$arn" \
--query "Certificate.[DomainName, Issuer, Type]" --output text
done | grep -i -E "emudhra|emsign"
In Azure, Resource Graph covers App Service certificates across subscriptions:
az graph query -q "
resources
| where type =~ 'microsoft.web/certificates'
| where tostring(properties.issuer) contains 'emSign' or tostring(properties.issuer) contains 'eMudhra'
| project name, resourceGroup, subscriptionId, issuer = tostring(properties.issuer), expires = properties.expirationDate
" -o table
Second, software with its own trust store keeps trusting a CA until that store is updated. Old AMIs, base images and runtimes that bundle their own CA list are where a blocked chain keeps working long after browsers have dropped it.
Where scanners and consoles stop
Amazon Inspector and Defender for Cloud are good at telling you whether a package version on a given machine is vulnerable. The AWS and Azure consoles are good at telling you what’s in one account in one region. Neither is what you need the morning an advisory lands.
The first problem is coverage. A scanner sees the accounts, subscriptions and machines it’s been enabled on, and the GitLab server from the acquisition is usually sitting in the account nobody onboarded. The second is ownership. Findings land in a shared queue where nobody has the authority to restart someone else’s server, so they sit there. A finding with no owner attached is just one more alert. The third is context. The scanner says the package is bad. It doesn’t say the host’s role can read every S3 bucket in the account, or that three other teams’ services call it. With LiteLLM, that context was most of the incident.
What fills those gaps is an inventory that stays current and records what exists, who owns it and how it connects to everything else. That’s the thing we’re building Cloud Sleuth to be.
What this looks like in Cloud Sleuth
Cloud Sleuth maps your AWS and Azure resources, assigns an owner to each one whether it’s tagged or not, and keeps both
Auto-fetched and translated by AI Gem Box. All rights belong to the original author.