警惕!针对国内某大厂开发人员的定向投毒安全事件"

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

cover_image

警惕!针对国内某大厂开发人员的定向投毒安全事件

原创

suntiger
suntiger

二进制空间安全

2026年8月11日 09:00
北京

在小说阅读器读本章

去阅读

将二进制空间安全设为\"星标⭐️\"

第一时间收到文章更新

事件背景

近日,安全研究人员披露一起针对阿里系开发工具用户的npm供应链投毒事件。攻击者发布了18个恶意包,名字刻意模仿 @ali 作用域下的内部私有包,例如无作用域的 lib-mtop 对应内部的 @ali/lib-mtop。开发者一旦在能访问私有仓库的环境里执行 npm install,依赖解析会按预期拉取内部包,但额外的恶意依赖也会被一并装进项目。

这不是常见的误装拼写错误包那么简单。恶意逻辑被拆散到多层依赖里,顶层包多数只是空壳诱饵,真正的加载器分散在 smart-config-manager、cloud-config-fetcher 等中间层和底层包中。最终落地的是一套跨平台远程控制后门,能在Windows、Linux、macOS 上分别采取不同的持久化手段,并向钉钉、悟空、Qoder 等协作工具注入恶意代码。

安装过程往往一切正常,日志里看不出异常,危害却要等到终端告警或协作软件行为异常时才浮出水面。排查时需联动锁文件深层依赖、开发机持久化痕迹与 npm 源配置,三者缺一项都容易漏掉隐蔽载荷。

攻击架构

整个攻击可以看成诱饵层、桥接层、执行层三段式结构。诱饵层大约10个顶层包,名称对齐 @ali 私有包,自身几乎无业务逻辑,作用是在 package.json 里声明对 smart-config-manager 的依赖,触发整条恶意子树的安装。

桥接层由 smart-config-manager 承担,它把诱饵包连接到真正含加载逻辑的底层包,例如 cloud-config-fetcher、fast-transform-pipeline。单个底层包看起来依赖关系并无异常,组合在一起才构成完整的加载链。这种化整为零的设计,刻意绕开只看顶层依赖的简易扫描。

执行层的核心机制是:某个底层包从GitHub仓库拉取规则引擎配置,再借助 Node.js 内置的 vm 模块在沙箱里执行动态脚本,按操作系统分支下载第二阶段载荷。下载域名 aone-cli-next.oss-cn-beijing.aliyuncs.com 伪装成阿里OSS地址,便于在流量侧混入正常业务。

仿冒包触发安装、桥接层展开子树、底层包拼接 loader,三个阶段按从左到右顺序衔接。攻击者把拉规则、判系统、下载荷拆进多个包的postinstall脚本和require 链,而不是集中写入单个文件,传统关键字扫描因此更容易漏报。

维护者账号 ch4ce 在 npmjs 上已显示不存在,但历史版本仍可被锁文件引用。lib-mtop 最早于2023年11月以空包形式出现,2026年3月至4月连续推送 v1.0.1、v1.0.2、v1.0.3 三个恶意版本。目前尚不确定是账号被接管还是维护者主动投毒,对企业防守而言,处置流程相同:凡命中名单版本,一律按失陷处理。

攻击细节分析

规则引擎跑起来之后,载荷获取与持久化按操作系统分流,行为差异明显,也决定了取证时应检查的位置不同。

在 Windows 上,恶意代码会尝试结束Alilang企业安全与办公客户端进程,并用篡改后的版本替换其核心文件。Alilang承担 VPN、终端安全、办公入口等职能,被替换意味着攻击者可以在员工日常必开的软件里长期驻留,而不必再依赖 npm 安装脚本。

在 Linux 上,二进制载荷下载到 /tmp 目录,以detached方式启动后从磁盘删除,典型无文件落地手法。内存里跑的进程若不与npm生命周期绑定,卸载 node\_modules无法清除。

在 macOS 上,攻击者向 ~/.zshrc 写入恶意后台脚本,并注册10分钟触发一次的Launch Agent。某安全团队在开发者笔记本上见到的现象是:终端每次打开都会多一行来源不明的source命令,而npm缓存里已找不到对应包目录。

最终载荷是一个功能完整的远程控制后门,支持远程命令执行、任意文件上传下载、主机侦察、载荷暂存与内网横向移动。更棘手的是持久化方式:后门会向钉钉、悟空、Qoder 等常见企业协作应用注入恶意代码,把日常办公软件变成二次加载点。这意味着即使重装了 Node 环境,只要协作客户端仍被污染,攻击者就能卷土重来。

研究人员在源码里看到中文注释,GitHub 提交时间戳多为UTC+08:00,结合目标工具链,普遍判断为中文攻击者发起的定向工业间谍行动。公开披露的完整恶意包名单如下,任何一条出现在锁文件或 node\_modules 里,都应视为高风险:

包名 层级角色
lib-mtop 仿冒 @ali/lib-mtop 的诱饵包
aone-kit / aone-kit-cli / aone-sandbox 仿冒 Aone 工具链的空壳包
local-config-parser 表面合法的JSON解析器,实际串联恶意依赖
smart-config-manager 桥接层,连接诱饵与loader
cloud-config-fetcher / fast-transform-pipeline 含loader片段的底层包
aone-cloud-cli / colder-cli / def-open-client 等 扩展诱饵与载荷投递
feedback-ai-sdk / flight-compare-analyzer / lwp-web-client等 同一攻击活动的配套包
open-worker-cli/test-skill-zip/uniapi-bridge 同一攻击活动的配套包

中招排查需覆盖lock文件全量依赖,npm list 只能看到顶层包,深层传递依赖必须靠完整lock文件检索。导出 package-lock.json 或 pnpm-lock.yaml,对照上表包名及 ch4ce 维护者字段;开发机与CI构建节点同步做终端检查,Windows核对Alilang文件哈希,macOS检查Launch Agent 与 ~/.zshrc,Linux检查 /tmp 历史异常进程及 cron、systemd 用户级服务。lock文件或node\_modules 中出现上述任一包名,按已失陷处理,在干净机器上轮换密钥,再对受影响主机完成取证。

投毒污染检测

以下代码可对投毒污染的代码进行检测:

#!/usr/bin/env python3# -*- coding: utf-8 -*-\"\"\"Scan package-lock.json / package.json for Alibaba npm RAT campaign indicators.\"\"\"
from __future__ import annotations
import argparseimport jsonimport reimport sysfrom dataclasses import asdict, dataclass, fieldfrom pathlib import Pathfrom typing import Any

@dataclassclass Finding:    severity: str    category: str    package: str    detail: str

@dataclassclass ScanReport:    target: str    findings: list[Finding] = field(default_factory=list)    packages_scanned: int = 0
    @property    def overall(self) -> str:        if any(f.severity in (\"critical\", \"warning\") for f in self.findings):            return \"FAIL\"        return \"PASS\"
    def to_dict(self) -> dict[str, Any]:        return {            \"target\": self.target,            \"packages_scanned\": self.packages_scanned,            \"overall\": self.overall,            \"finding_count\": len(self.findings),            \"findings\": [asdict(f) for f in self.findings],        }

def load_rules(rules_path: Path) -> dict[str, Any]:    with rules_path.open(encoding=\"utf-8\") as fh:        return json.load(fh)

def collect_package_names(manifest: dict[str, Any]) -> set[str]:    names: set[str] = set()    for section in (\"dependencies\", \"devDependencies\"):        for name in (manifest.get(section) or {}):            names.add(name)            names.add(name.split(\"/\")[-1])    lock = manifest.get(\"packages\") or {}    if isinstance(lock, dict):        for key in lock:            if key.startswith(\"node_modules/\"):                pkg = key.split(\"node_modules/\")[-1]                names.add(pkg)                names.add(pkg.split(\"/\")[-1])    return names

CAMPAIGN_TAG = \"Alibaba-NPM-2026\"

def threat_detail(category: str, **ctx: str) -> str:    base = ctx.get(\"base\", \"\")    scope = ctx.get(\"scope\", \"\")    if category == \"known_malicious_package\":        return (            f\"Trojan/JS.SupplyChain.{base}!AlibabaRAT \"            f\"[行为:供应链投毒/跨平台后门, 活动:{CAMPAIGN_TAG}, 级别:高危]\"        )    if category == \"scope_impersonation\":        return (            f\"HEUR:Trojan/NPM.Typosquat.{base}!ScopeConfusion \"            f\"[仿冒:{scope}, 风险:作用域混淆/私有包名冲突, 活动:{CAMPAIGN_TAG}]\"        )    return f\"HEUR:Trojan/JS.Generic!Unknown [活动:{CAMPAIGN_TAG}]\"

def scan_package_names(names: set[str], rules: dict[str, Any]) -> list[Finding]:    findings: list[Finding] = []    malicious = set(rules.get(\"malicious_packages\", []))    for pkg in sorted(names):        base = pkg.split(\"/\")[-1]        if base in malicious:            findings.append(Finding(                severity=\"critical\",                category=\"known_malicious_package\",                package=pkg,                detail=threat_detail(\"known_malicious_package\", base=base),            ))    for scope in rules.get(\"impersonated_scopes\", []):        private_name = scope.split(\"/\")[-1]        for pkg in names:            if pkg.split(\"/\")[-1] == private_name and not pkg.startswith(\"@ali/\"):                findings.append(Finding(                    severity=\"warning\",                    category=\"scope_impersonation\",                    package=pkg,                    detail=threat_detail(\"scope_impersonation\", base=pkg.split(\"/\")[-1], scope=scope),                ))    return findings

def scan_target(target: Path, rules: dict[str, Any]) -> ScanReport:    report = ScanReport(target=str(target))    names: set[str] = set()    for fname in (\"package.json\", \"package-lock.json\"):        path = target / fname        if path.is_file():            names |= collect_package_names(json.loads(path.read_text(encoding=\"utf-8\")))    report.packages_scanned = len(names)    report.findings = scan_package_names(names, rules)    return report

def main() -> int:    parser = argparse.ArgumentParser()    parser.add_argument(\"--project\", type=Path, default=Path(\".\"))    parser.add_argument(\"--rules\", type=Path, default=Path(__file__).with_name(\"known_packages.json\"))    parser.add_argument(\"--strict\", action=\"store_true\")    args = parser.parse_args()    report = scan_target(args.project.resolve(), load_rules(args.rules.resolve()))    print(json.dumps(report.to_dict(), ensure_ascii=False, indent=2))    return 1 if args.strict and report.overall == \"FAIL\" else 0

if __name__ == \"__main__\":    sys.exit(main())

扫描的典型输出如下:

{  \"target\": \".../demo_project\",  \"packages_scanned\": 3,  \"overall\": \"FAIL\",  \"finding_count\": 3,  \"findings\": [    {      \"severity\": \"critical\",      \"category\": \"known_malicious_package\",      \"package\": \"lib-mtop\",      \"detail\": \"Trojan/JS.SupplyChain.lib-mtop!AlibabaRAT [行为:供应链投毒/跨平台后门, 活动:Alibaba-NPM-2026, 级别:高危]\"    }  ]}

总结

npm供应链投毒正在从单个恶意包演进为多层依赖拆分、跨平台持久化、办公软件注入的组合拳。这次针对阿里开发工具用户的攻击说明,攻击者已经熟悉企业内部包命名习惯,并会把恶意逻辑藏进传递依赖,专门对付只看顶层依赖清单的团队。

日常防护要点包括:package.json写全@ali 作用域; lock文件变更合并前必审; npm安装走内网私服代理; 终端与协作软件按已失陷前提定期核查。lock文件或开发机命中上述恶意包时,该环境生产密钥须立即停用,完成取证与隔离后轮换凭据,再在干净基线上重建开发环境。


警惕!针对国内某大厂开发人员的定向投毒安全事件" 网络安全文章

警惕!针对国内某大厂开发人员的定向投毒安全事件"

近日披露一起针对阿里系开发工具的npm供应链投毒事件,攻击者发布18个仿冒@ali私有包的恶意包,通过诱饵层、桥接层、执行层三段式结构隐藏跨平台后门,最终向钉钉等协作工具注入恶意代码实现持久化。建议排查lock文件全量依赖,对照恶意包名单,并检查开发机持久化痕迹如alilang文件哈希、launchagent和\\~/.zshrc。
挖洞中怎样判断数据的加密方式?看看这几个技巧" 网络安全文章

挖洞中怎样判断数据的加密方式?看看这几个技巧"

本文介绍了在渗透测试中判断数据加密方式的技巧,包括通过长度和字符集识别Base64、MD5、AES等编码或加密类型,通过参数名推断加密类型如JWT,通过翻前端JS代码查找加密函数,通过修改参数观察行为反推加密方式,以及使用工具验证。文章强调这些技巧仅用于授权测试,未经授权的利用属违法行为。
评论:0   参与:  0