sckit 蠕虫:首个专门针对 AI Agent 记忆层的供应链攻击
💡 工具推荐:API Key 轮换工具、JSON 格式化、AI 正则构建器
供应链攻击向来瞄准开发者工具,但这一次,目标变了。据 Forkast 分析,2026 年 9 月 23 日 UTC 02:23 到 05:55 之间,MemTensor 的开源记忆框架 MemOS(面向 LLM 与 AI Agent,GitHub 约 1.15 万星)在 npm 和 PyPI 上同时出现了恶意版本,载荷是一个用 Go 写的跨平台凭据窃取器 sckit。它被认为是有记录以来首个专门针对「Agent 记忆基础设施」的供应链蠕虫。更值得警惕的是它的手法:完全不用 postinstall 钩子,而是藏进 Agent 本来就会调用的正常代码路径里。本文拆解它怎么做到的、偷了什么,以及你现在该怎么自查。
攻击不再靠 postinstall 钩子,而是藏进正常代码路径
发生了什么
据 Forkast 的分析,攻击从向 MemTensor 自己的 GitHub 仓库直接提交开始,通过维护者账号 leason1974 发布,且缺少 gitHead。恶意版本之间夹着干净版本(npm 上的 0.1.22 与 0.1.24),Semgrep 的研究人员把这读作攻击者在可用的时间窗口里测试载荷优化。受影响的版本是:npm 的 @memtensor/memos-cloud-openclaw-plugin 0.1.21、0.1.23、0.1.25,以及 PyPI 的 MemoryOS 2.0.34。
# The incident, in one object. Timeline, versions and C2 details are
# from Forkast's analysis of the MemTensor/MemOS compromise.
INCIDENT = {
"window_utc": "2026-09-23 02:23 -> 05:55",
"target": "MemTensor MemOS (memory framework for LLMs/agents, ~11.5k stars)",
"registries": ["npm", "PyPI"],
"payload": "sckit (cross-platform Go credential stealer)",
"affected": {
"npm": "@memtensor/memos-cloud-openclaw-plugin 0.1.21, 0.1.23, 0.1.25",
"pypi": "MemoryOS 2.0.34",
},
"c2": ["skyleen[.]fr"],
"campaign": "cloud-openclaw-semi-nuclear",
"not_after": "2026-10-23",
}为什么安装期扫描器漏掉了它
sckit 不使用 postinstall 钩子——因为现在大家都会扫这个。相反,它把自己接到 Agent 本来就会调用的功能上。在 npm 一侧,恶意版本新增了 lib/sckit.js,它从隐藏的 .sckit/ 目录启动一个平台专用的 Go 二进制,触发点有两个:OpenClaw 网关启动时,以及每次「记忆召回」钩子触发时。第二个触发点尤其致命——每次召回都会通过 SCKIT_EVENT_TEXT 环境变量,把用户当前的提示词文本传给那个二进制;StepSecurity 的解读是:只要受影响的版本被加载,开发者在会话里问过 Agent 的任何内容都应视为已泄露。在 Python 一侧,攻击更「优雅」:载荷通过 memos/log.py 触发,configure_logging() 会追加调用 memos._stage0.trigger();由于 Python 在导入时执行模块级语句,一句 import memos 就能顺着日志钩子走到启动器,完全不需要安装脚本。
# Why install-time scanners missed it. sckit uses no postinstall
# hooks. It attaches to functionality the agent calls anyway.
# npm side: lib/sckit.js launches a Go binary from .sckit/, at two
# moments: when the OpenClaw gateway starts, and on every memory recall.
# Each recall passes the current prompt text via SCKIT_EVENT_TEXT.
# python side: import triggers everything, no install script needed.
# memos/log.py
from . import _stage0 # legitimate-looking
def configure_logging():
_stage0.trigger() # <-- the payload rides along
# Lesson: if you only scan lifecycle scripts, you miss attacks that
# ride ordinary import paths.被盯上的是「发布令牌」——供应链里最敏感的凭据
它偷了什么、发去了哪里
sckit 会从 13 类来源收割凭据:npm token、PyPI token、GitHub 与 GitLab 个人访问令牌、AWS 访问密钥、Hugging Face token、HashiCorp Vault token、Slack token、Stripe 生产密钥、SendGrid 密钥、SSH 密钥、通用 JWT,以及形似密钥的环境变量。数据被送往位于 skyleen[.]fr 及其子域的命令与控制基础设施(C2)。从命令行参数 --config64 解出的内嵌配置把这场活动命名为 cloud-openclaw-semi-nuclear,把 $HOME 设为清点根目录,并配置了三个带 /config、/status、/batch 端点的外部前端;活动的 not_after 时间戳对应 2026 年 10 月 23 日。
# What you would grep for right now, across lockfiles, requirements
# files, poetry.lock and SBOMs. Run these across every repo and CI cache.
# Shell one-liners (safe to run, read-only)
grep -r "memos-cloud-openclaw-plugin" --include="package-lock.json" .
grep -r "MemoryOS" --include="requirements*.txt" --include="poetry.lock" .
find . -type d -name ".sckit" -not -path "*/node_modules/*"
# Python: audit an installed environment for the malicious version
def check_env(env):
risks = []
for dist in env.iter_installed():
if dist.name == "MemoryOS" and dist.version == "2.0.34":
risks.append(("pypi", dist.name, dist.version))
return risks为什么 Agent 栈是「好目标」
Forkast 指出了三重原因。第一,凭据密度高:Agent 要干活就得拿到密钥,所以跑 Agent 的机器上通常囤着发布令牌、云密钥和 API 凭据。第二,很少有团队会为 AI 项目去威胁建模一次 pip install。第三,提示词本身携带敏感数据——这是传统供应链攻击从未触碰过的东西。sckit 的二进制里还包含名为 recursivePublish、prepareRemoteNode、prepareRemotePython、prepareRemoteWorkflow 的函数,以及请求在 push 时执行的 GitHub Actions 模板,意图很清楚:偷发布令牌,用它们推送更多被污染的包,如此循环。据 Forkast 截至发稿的观察,尚未记录到实际的下游扩散——能力在代码里,但真实世界的激活还没有被证实。
# Credential rotation order matters. Rotate everything reachable
# from any host that imported an affected version, registry tokens
# first, because those let the worm spread.
ROTATION_ORDER = [
"npm + PyPI publish tokens (registry first)",
"GitHub and GitLab personal access tokens",
"AWS access keys",
"HashiCorp Vault tokens",
"Hugging Face tokens",
"Stripe live keys, SendGrid keys, Slack tokens",
"SSH keys and generic JWTs",
"any env var matching secret-shaped patterns",
]
BLOCK = ["skyleen.fr", "*.skyleen.fr"] # add to DNS egress denylist记忆框架第一次被明确列为「Agent 攻击面」
现在该做什么
如果你的环境里可能导入过受影响版本,按这个顺序处理:先把 npm 固定到 0.1.20、PyPI 固定到 2.0.33,或直接移除这些包;然后轮换任何可从受影响主机访问到的凭据,注册表 token 优先(它们能让蠕虫继续扩散),再依次是源码托管、云平台、Vault、SSH 密钥;在 DNS 出口黑名单里封掉 skyleen[.]fr 及其所有子域;最后,在 lockfile、requirements*.txt、poetry.lock 和 SBOM 里搜索受影响的包名,并审计:如果一台受影响的机器曾握有发布令牌,你自己的发布也要一并查。
# Prevention for the next one: make installs boring again. Pin, use
# deterministic installs, and give CI publish tokens the least power
# they can possibly hold.
HARDENING = {
"pin": "exact versions in lockfiles; never auto-bump on install",
"ci": "npm ci / pip --require-hashes, not npm install",
"tokens": "scoped, short-lived publish tokens; require 2FA to publish",
"sandbox": "build agents in ephemeral containers without prod secrets",
"egress": "deny outbound by default; allowlist registries only",
"monitor": "alert on any new outbound domain, not just on CPU spikes",
}
# If a compromised host held a publish token, audit your own releases.更大的教训
把这次事件放进更大的图景:本周的 Agent 安全报道已经覆盖了内核层、虚拟机层和浏览器层,而 MemTensor 事件把「包生态」——尤其是 Agent 所依赖的记忆框架——补进了这份清单。它同时暴露了一个长期被忽略的事实:安装期扫描是不够的,因为攻击可以藏在正常导入路径里;而 Agent 的记忆层,因为同时接触提示词、凭据和长期状态,正在成为最高价值、也最缺乏可视化的一层。对团队来说,真正的动作不是换一个扫描器,而是让安装变得「无聊」:精确锁定版本、确定性安装、把 CI 发布令牌的权限压到最小、给构建环境断掉生产密钥和默认出站。
📌 常见问题 FAQ
这次事件到底发生了什么?
据 Forkast 分析,2026 年 9 月 23 日 UTC 02:23 至 05:55 之间,MemTensor 的开源记忆框架 MemOS 在 npm 与 PyPI 上出现了恶意版本,载荷是名为 sckit 的跨平台 Go 凭据窃取器。它被认为是首个专门针对 Agent 记忆基础设施的供应链蠕虫。
哪些版本受影响?
据 Forkast,受影响的是 npm 的 @memtensor/memos-cloud-openclaw-plugin 0.1.21、0.1.23、0.1.25,以及 PyPI 的 MemoryOS 2.0.34。干净版本夹在恶意版本之间,被研究人员解读为攻击者在测试载荷。
为什么安装期扫描器没发现?
因为 sckit 不使用 postinstall 钩子。据分析,在 npm 侧它通过 lib/sckit.js 在网关启动和每次记忆召回时触发;在 Python 侧通过 memos/log.py 的 configure_logging() 在导入时触发。它藏在正常代码路径里,所以只扫生命周期脚本的扫描器会漏掉。
它偷了哪些凭据?
据 Forkast,sckit 收割 13 类凭据,包括 npm 与 PyPI token、GitHub/GitLab 个人访问令牌、AWS 访问密钥、Hugging Face token、HashiCorp Vault token、Slack token、Stripe 生产密钥、SendGrid 密钥、SSH 密钥、通用 JWT 以及形似密钥的环境变量,并发送到 skyleen[.]fr 及其子域。
我现在应该做什么?
按建议:把 npm 固定到 0.1.20、PyPI 固定到 2.0.33 或移除相关包;轮换所有可从受影响主机访问的凭据,注册表 token 优先;封禁 skyleen[.]fr 及子域;在 lockfile、requirements 和 SBOM 中搜索受影响包名;并审计自己的发布是否受影响。