开源软件供应链的安全防线,长期以来默认了一套底线假设:恶意代码总要安装,总要连网,总会在代码库里露出明文脚印。只要在 CI/CD 流水线中架设静态扫描器,卡死 package.json 里的安装脚本钩子,绝大多数低阶投毒就会原形毕露。

2026 年 9 月 17 日,安全团队 SafeDep 披露的一起针对 npm 的投毒事件打破了这种侥幸。攻击者克隆了流行数学库 mathjs,封装成名为 mathmain 及其变种 mathsbasemath-universe 的恶意包。这一恶意代码没有配置任何安装钩子,常规导入不会触发任何异常;它把解密密码直接绑定在线性方程求解器的运算过程里,只有目标业务输入特定矩阵进行求解,真正的远程控制木马才会被释放。

两代 npm 供应链攻击机制对比 传统粗放型投毒 触发机制:package.json 安装钩子 代码形态:明文或简易混淆脚本 打击范围:无差别窃取环境变量 / 密钥 易被静态沙箱阻断 数学定向逻辑锁(mathmain) 触发机制:lusolve() 求解特定输入 代码形态:AES-256-GCM 强加密载荷 打击范围:严格限定靶标业务运行时 无密码在数学上不可逆

方程即密码:篡改矩阵求解器

恶意包的核心机制在于把数学计算转化为密钥派生工具。攻击者修改了 CommonJS 构建产物中的 lusolve() 函数,该函数主要用于求解线性方程组。在核心计算结束后,代码额外调用了 removeSolveValidation(),并将矩阵分解出的下三角矩阵数据 l._data 传入校验层。

攻击者在求解器函数尾部加装旁路调用以提取解密密码
攻击者在求解器函数尾部加装旁路调用以提取解密密码

在底层实现中,这串矩阵数据会被转换为 JSON 字符串充当解密密码。模块采用 scrypt 算法派生出 256 位密钥,配合 16 字节 Salt、12 字节 IV 和 16 字节认证标签(Tag),以 AES-256-GCM 模式尝试解密同目录下的加密载荷。

如果调用方传入的是普通业务数据,GCM 认证标签校验直接失败,解密流程戛然而止,文件系统上不会产生任何落盘痕迹。安全分析人员前期暴力穷举了 16,922 组矩阵组合均无法破译。JFrog 随后复现了真正的触发钥匙:一个特定的 3x3 帕斯卡矩阵:

[[1, 1, 1], [1, 2, 3], [1, 3, 6]]

经过 LU 分解后,其下三角矩阵得到的序列经序列化即为合法解密密码。这种设计意味着,只要攻击者没有主动发起包含该参数的计算请求,放在任何公共审计平台上的代码都是完全合法的数学库。

mathmain 载荷解密与执行管线 特定矩阵输入 3x3 帕斯卡矩阵 进入 lusolve() 下三角分解 (L) JSON.stringify() 生成 scrypt 密钥 AES-256-GCM 验证 16 字节 Tag 解密落盘运行 多阶段远控 Slack / Telegram Base Sepolia 链上信标

避开雷达:雪藏版本与去中心化通道

除了解密机制本身的严密性,投毒者还熟练利用了软件仓库与分发体系的管理盲区。

恶意控制信道伪装在公链交互与日常办公聊天流量中
恶意控制信道伪装在公链交互与日常办公聊天流量中

整个恶意软件包内塞入了三组伪装成脚本的 Base64 编码密文:graph.js(20,918 字节)、fraction.js(9,084 字节)以及体积达 1,179,416 字节bignumber/type.js。第一阶段加载器执行后,这套载荷会利用 Node.js 原生模块收集主机信息,随后加载完整的 ethers/5.7.2 库,并连接 Base 链的 Sepolia 测试网智能合约。

与此同时,执行代理会每隔 10 秒轮询 Slack 的特定消息记录,把运营者发送的内容直接传入子进程执行。这种控制信道将指令混在常规 Web 流量与公开公链 RPC 交互中,传统边界防火墙若仅靠域名黑名单,根本无法拦截。

更关键的规避手段在分发层。同源恶意逻辑在 mathmainmathsbasemath-universe 跨 5 个版本中出现,但 2026 年 9 月 17 日 npm 默认分发的版本中,恶意代码被刻意移除

在 npm 的元数据体系中,清单的 dist.shasum 使用 SHA-1,而子资源完整性校验(dist.integrity)采用 SHA-512。许多自动化扫描器只针对 latest 标签拉取解析,攻击者利用非默认版本(如 1.0.1)作为武器掩体,直接绕过了绝大多数常规 CI 静态探测。

投毒者不再追求大面积感染,而是用数学逻辑造了一把单向锁,钥匙留在自己手里。

攻防质变与分水岭

这起事件并非孤立的代码污染,而是开源供应链攻击逻辑的分水岭。

防御底线在于比对开源代码仓库与实际发布包的一致性(示意图)
防御底线在于比对开源代码仓库与实际发布包的一致性(示意图)

过去安全人员审视第三方库,关注点集中在依赖树深度、是否有可疑的网络请求、安装阶段是否试图读写敏感目录。但在 mathmain 案例中,攻击者展示了完全不同的思路:两套组件协同作案,宿主库伪装成纯良的计算工具,真正的触发器则隐藏在尚未公开的业务代码或私有调用链中。

  • 风险.通用的区块链 RPC 节点与公共聊天服务属于低置信度指标,粗暴封禁会导致正常业务误伤,防御方必须精确下钻至合约交互签名与具体消息 ID。

《孙子兵法》有云:“善守者,藏于九地之下;善动者,动于九天之上。”这起攻击展现的正是这种深潜逻辑。当开源组件的密文在数学上不可逆,当恶意行为只在特定参数流入时才短暂显形,传统的特征库匹配和沙箱动态引爆便彻底落入被动。

面对这种定向逻辑锁,依赖管理不能再仅凭最新版签名来背书。对所有非默认版本的全量溯源、对生产环境输入输出异常的动态约束,以及在代码打包阶段比对源码仓库与发布 tarball 的一致性,正在从防御体系的进阶选项,变成唯一的生死线。