文章总结: 2026年8月20日,Rust生态arrayref等crate遭供应链投毒,攻击者盗用维护者账号发布含恶意构建脚本的版本,通过yank诱导升级,编译即执行恶意载荷。暴露窗口约86分钟,官方未发现实际利用证据。建议检查Cargo.lock并轮换凭证。 综合评分: 88 文章分类: 供应链安全,恶意软件,漏洞分析,安全工具,红队
2.45亿次下载的Rust基石crate被投毒:一次编译,就是一次沦陷
原创
威胁情报中心 威胁情报中心
奇安信威胁情报中心
2026年8月21日 15:42 北京
在小说阅读器读本章
去阅读
供应链安全 · Rust
2.45亿次下载的Rust基石crate被投毒 一次编译,就是一次沦陷
2026年8月20日,arrayref维护者账号被盗,攻击者发布构建时恶意载荷。开发者甚至不需要运行程序,只要 cargo build 一次,恶意代码就已在本机执行。
· 供应链安全 · Rust · 2026
2026年8月20日,Rust生态遭遇了一次教科书级别的供应链攻击:热门基础库 arrayref 的维护者账号被盗,攻击者借此发布了一个被植入恶意依赖的新版本。与其他”运行时触发”的投毒不同,这次的恶意代码藏在构建脚本里——开发者甚至不需要运行自己的程序,只要执行一次 cargo build(乃至 cargo check、cargo test),恶意载荷就已经在本机落地执行了。
值得庆幸的是,从恶意版本发布到被crates.io删除,暴露窗口只有约86分钟,官方公告称未发现实际利用证据。但这起事件所展示的攻击手法组合拳——账号接管、typosquat仿冒、”yank诱导升级”、构建时执行——值得每一位依赖开源组件的开发者认真研究。
01 事件速览
2026年8月20日,crates.io 上的知名crate arrayref 发布了0.3.10版本。这个版本首次在crate十余年的历史上引入了一个真正的外部依赖:proc-macro1。这个名字与Rust宏生态的基石crate proc-macro2 仅一字之差,是一个彻头彻尾的仿冒品,其构建脚本会在编译期间从远程服务器下载并执行恶意二进制。
同一天,同一维护者账号(droundy)名下的另外两个crate也遭到同样手法的投毒:
| | | | | — | — | — | | 受影响crate | 恶意版本 | 在线时长 | | arrayref | 0.3.10 | 约86分钟 | | internment | 0.8.7 | 约90分钟 | | append-only-vec | 0.1.9 | 约107分钟 |
Rust安全响应团队在接到报告后删除了这些恶意版本,同时移除了攻击者控制的六个crate(proc-macro1、proc-macro-en、aovine、arone、aronenao、tinymember),并恢复了被恶意yank掉的正常版本,锁定了涉事账号。官方明确表示:不认为原作者存在恶意行为,其计算机或凭证很可能已被攻陷。目前该事件没有修复版本(直接删除恶意版本即是处置),也没有分配CVE编号,对应RustSec公告为 RUSTSEC-2026-0260(arrayref)等。
02 攻击链复盘:四步环环相扣
图 | arrayref 构建时投毒攻击链全景
第一步:提前布置”李鬼”账号与仿冒crate
攻击并非临时起意。时间线显示,8月20日凌晨1:17,攻击者注册了GitHub账号 dtolney——与 proc-macro2 的真实作者David Tolnay(dtolnay)的用户名仅差一个字母的换位。8分钟后,同名crates.io账号注册完成。
随后在01:55,攻击者发布了 [email protected]:这是一个完全”干净”的诱饵版本,内容就是正版 proc-macro2 的机械改名副本,不包含任何恶意代码。这一步的目的是让该crate拥有发布历史,避免在后续审查中看起来像一个”零记录的新包”。
值得注意的是,基础设施的准备还要更早:arone 和 aronenao 这两个同样携带恶意构建脚本的crate,其最后版本在8月18日就已发布,说明整场战役的铺垫至少比主攻提前了两天。
第二步:接管维护者账号,注入恶意依赖
07:11,[email protected] 发布,恶意构建脚本正式上膛。4分钟后(07:15),被盗的 droundy 账号发布了 [email protected],整个投毒只是在 Cargo.toml 里加了一行:
[dependencies.proc-macro1]
version = “1.0.107”
arrayref 本身是一个只有四个宏的小crate,直到0.3.9都没有任何构建脚本和运行时依赖,其0.3.10的库源码也与正常版本完全一致——代码里没有任何地方引用 proc-macro1。但Cargo的构建机制决定了:只要清单里声明了非可选依赖,无论代码是否使用,Cargo都会拉取并构建它。而构建 proc-macro1,就意味着运行它的恶意 build.rs。
这个依赖声明还暗藏一个细节:version = “1.0.107” 是caret语义化版本范围,而 proc-macro1 一共只发布过1.0.106和1.0.107两个版本,因此该范围必然解析到恶意的1.0.107。
第三步:yank诱导——把用户”赶”向恶意版本
这是本次攻击中最精妙的社会工程环节。在发布0.3.10的同一分钟内(07:15:24至07:15:40),被盗账号以脚本化节奏(每约4秒一个)yank掉了 arrayref 的0.3.5至0.3.9全部旧版本。
crate被yank后,Cargo会对继续使用这些版本的项目打印”请考虑升级到未被yank的版本”警告。此时唯一”正常”的版本就是被投毒的0.3.10——开发者看到警告后的”合理”操作,恰好正中攻击者下怀。向RustSec提交公告的报告者正是这样中招的。
第四步:构建时执行,编译即沦陷
恶意代码位于 proc-macro1 的 build.rs 中。Rust构建脚本会在编译包本体之前被Cargo自动编译并运行,且没有任何feature开关或环境检查做前置守卫——只要平台受支持,每次构建必触发。也就是说,cargo build、cargo check、cargo test、cargo clippy、cargo doc 中的任何一条都足以引爆,触发权限等同于发起构建的用户:在开发机上是可以触达SSH私钥、云凭证、浏览器数据的用户会话;在CI里则是持有registry token、签名物料和云角色的runner。
03 恶意 build.rs 技术拆解
图 | proc-macro1 恶意 build.rs 构建时执行流程
伪装:一个”能用”的假货
proc-macro1 的 src/ 目录就是正版 proc-macro2 的源码逐字替换改名的产物,连文档链接和issue引用都没放过(例如 html_root_url 指向 docs.rs/proc-macro1/1.0.107)。包元数据则伪造了作者身份:authors = [“David Tolnay rchaitm@gmail.com“](该邮箱并非David Tolnay本人),仓库地址指向一个不存在的 dtolnay/proc-macro1(404)。
因为库代码是真的,编译一切正常,构建输出里看不到任何异常——这让恶意crate在普通构建中极不显眼。真正的破绽藏在构建依赖里:一个token解析库,却引入了 base64、rustls、ureq 三个build-dependencies,分别提供Base64解码、TLS栈和HTTP客户端。正版 proc-macro2 没有任何构建依赖。
载荷投递流程
构建脚本将服务器地址拆成Base64碎片存储,在编译时重组,使明文字符串不出现在源码中,以规避字符串扫描:
const SRC_URL_PARTS: &[&str] = &[“aHR0cHM6Ly8=”, “MjMuMjU0Lg==”, “MTY1Lg==”, “MTEyOg==”, “OTA4OS8=”];
const END_URL_PARTS: &[&str] = &[“MjMuMjU0Lg==”, “MTY1Lg==”, “MTEyOg==”, “NDQz”];
解码后,前者是载荷下载地址 hxxps://23[.]254[.]165[.]112:9089/,后者是作为参数传给载荷的C2地址 23[.]254[.]165[.]112:443。
下载使用rustls建立TLS连接,但实现了一个名为 AcceptAll 的证书校验器,其 verify_server_cert、verify_tls12_signature、verify_tls13_signature 三个校验方法全部无条件返回成功——连接是加密的,但服务器身份验证形同虚设,裸IP上的自签名证书也能通过。
随后脚本按操作系统和架构四选一投递二阶段载荷,不支持的平台则直接panic中止构建:
match (std::env::consts::OS, std::env::consts::ARCH) {
(“linux”, “x86_64”) => “rust-crate_0.1.0”,
(“windows”, “x86_64”) => “rust-crate_0.2.0”,
(“macos”, “x86_64”) => “rust-crate_0.3.0”,
(“macos”, “aarch64”) => “rust-crate_0.4.0”,
(_, _) => panic!(“unsupported platform”),
}
进程分离:不让Cargo等它
Unix/macOS:载荷写入 /tmp/rust-setup,chmod +x 后以分离进程方式启动,C2地址作为第一个参数(argv[1])传入,标准输入输出全部重定向到null,构建脚本spawn后不等待。
Windows:下载的字节是一个PowerShell脚本,写入 %TEMP%\rust-setup.ps1,再通过一个VBScript启动器(%TEMP%\rust-setup-launch.vbs)以 wscript.exe 隐藏窗口方式拉起,PowerShell参数包含 -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden。源码注释明确解释了为什么要绕道WScript:ShellExecute经由WScript可以逃离Cargo的job object,否则子进程会让构建脚本(以及 cargo build)一直等待其退出。最后用 std::mem::forget 泄漏wscript子进程句柄,使其析构函数永不运行。两步配合,让载荷彻底脱离Cargo进程树,构建结束后继续存活。
二阶段载荷能力
据Wiz等机构的后续分析,二阶段载荷是一台典型的信息窃取器(infostealer):收集主机信息,查询Chrome、Brave、Edge的SQLite登录数据库以窃取浏览器保存的凭证和扩展数据;通过Windows注册表Run键、macOS LaunchAgent、Linux systemd服务建立持久化;支持远程命令执行,并带有DGA(域名生成算法)回退机制。Wiz还指出其基础设施与近期疑似朝鲜(DPRK)背景的供应链活动(涉及Mastra、axios等事件)存在显著重叠,但未给出确定性归因。
04 影响面有多大?
arrayref 的历史总下载量约2.45亿次(发稿时244,989,384次),其中干净的0.3.9约占1.52亿次——需要强调的是,这些数字衡量的是crate的使用广度,而非受影响构建的数量。
更关键的是它在依赖图中的位置:arrayref 经由 tiny-skia、sctk-adwaita、winit 深埋在常见Rust依赖树中,绝大多数基于 egui、eframe、iced 的GUI项目都会间接依赖它,密码学、图形、区块链工具链中也广泛使用。这意味着攻击者选择的不是一个”热门应用”,而是一个“基础设施级”的传递依赖节点——多数受害者甚至不知道自己的项目里有它。
图 | arrayref 依赖链影响面
不过从处置结果看,三个恶意版本在线时间均不足两小时,RustSec公告记录为”无实际使用证据”。另据第三方报道,恶意的 arrayref 0.3.10 在存续期间被下载了53次。
05 处置与自查建议
1. 检查锁文件与本地缓存:在所有仓库的 Cargo.lock 中搜索 arrayref 0.3.10、internment 0.8.7、append-only-vec 0.1.9 及六个攻击者crate名;本地检查 ~/.cargo/registry/cache 中是否存在对应的 .crate 文件。
2. 将命中的主机视为已沦陷:因为载荷在构建时执行,任何构建过受影响项目的开发机或CI runner都应按沦陷处理——轮换该主机可触达的一切凭证、token、密钥(含CI secret和签名密钥),并从干净来源重新构建暴露窗口后产出的所有构建产物。
3. 重置浏览器存储的凭证:载荷会读取Chrome/Brave/Edge的已保存登录信息,需重置相关密码并吊销会话。
4. 清除载荷与持久化痕迹:删除 /tmp/rust-setup、%TEMP%\rust-setup.ps1、%TEMP%\rust-setup-launch.vbs,排查可疑的systemd用户服务、HKCU Run项和LaunchAgent。
5. 警惕”yank诱导”:一个长期稳定的crate突然批量yank多个旧版本,本身就是值得调查的信号,不要盲目按警告升级。
6. 审查构建依赖变更:构建脚本以完整用户权限运行,任何新增或变更的 build-dependencies——尤其是 ureq、reqwest、rustls 这类网络库出现在没有联网理由的crate中——都应触发告警。
06 技术附录
MITRE ATT&CK 技术映射
| | | | | | — | — | — | — | | 战术 | 技术ID | 技术名称 | 事件中的体现 | | 初始访问 | T1195.001 | 供应链投毒:软件依赖与工具 | 接管维护者账号发布投毒版本;typosquat仿冒 proc-macro2 | | 初始访问 | T1078 | 有效账户 | 盗用crates.io维护者账号droundy发布恶意版本 | | 执行 | T1059.001 | PowerShell | Windows平台投递并执行 .ps1 载荷 | | 执行 | T1059.005 | Visual Basic | 通过VBScript启动器经wscript.exe拉起PowerShell | | 执行 | T1059.004 | Unix Shell | Unix平台落地 /tmp/rust-setup 并chmod执行 | | 执行 | T1127 | 受信任的开发工具代理执行 | 滥用Cargo构建脚本(build.rs)机制在编译期执行恶意代码 | | 持久化 | T1547.001 | 注册表Run键/启动文件夹 | Windows端通过Run键持久化(二阶段) | | 持久化 | T1543.001 | 创建或修改系统进程:Launch Agent | macOS端持久化(二阶段) | | 持久化 | T1543.002 | 创建或修改系统进程:Systemd服务 | Linux端注册systemd服务重启载荷(二阶段) | | 防御规避 | T1027 | 混淆文件或信息 | C2地址拆分为Base64碎片,编译时重组 | | 防御规避 | T1036 | 伪装 | crate名仿冒(proc-macro1/dtolney);载荷伪装为”rust-setup”、”rust-crate” | | 防御规避 | T1553.004 | 破坏信任控制:安装根证书(旁类) | 自定义AcceptAll证书校验器,完全禁用TLS服务器身份验证 | | 凭证访问 | T1555.003 | 从Web浏览器窃取凭证 | 查询Chrome/Brave/Edge的SQLite登录数据库 | | 发现 | T1082 | 系统信息发现 | 二阶段载荷收集主机信息 | | 收集 | T1005 | 本地系统数据 | 收集浏览器保存的登录信息与扩展数据 | | 命令控制 | T1071.001 | 应用层协议:Web协议 | 通过HTTPS(443)与C2通信 | | 命令控制 | T1573 | 加密通道 | C2通信使用TLS加密 | | 命令控制 | T1568.002 | 动态解析:域名生成算法 | 二阶段载荷带DGA回退机制 | | 资源开发 | T1583.001 | 获取基础设施:域名/账号 | 注册仿冒GitHub/crates.io账号,租用Hostwinds VPS | | 资源开发 | T1608.001 | 安置能力:上传恶意软件 | 预先发布诱饵版本及多个恶意依赖crate |
失陷指标(IoC)
网络指标
| | | | | — | — | — | | 类型 | 指标 | 说明 | | IPv4 | 23.254.165.112:9089 | 载荷下载服务器(HTTPS,接受任意证书) | | IPv4 | 23.254.165.112:443 | C2地址,作为argv[1]传给载荷 | | IPv4 | 23.254.167.107 | 第三方报告的额外C2 | | IPv4 | 23.254.167.216 | 受感染Linux主机上观察到的二阶段C2 | | 域名 | hwsrv-798836.hostwindsdns.com | Hostwinds VPS主机名 | | 网段 | 23.254.165.0/24 、23.254.167.0/24 | 均属Hostwinds地址段,建议按整段关注 |
文件指标
| | | | | — | — | — | | 平台 | 路径/名称 | 说明 | | Unix/macOS | /tmp/rust-setup | 构建时落地的可执行载荷 | | Windows | %TEMP%\rust-setup.ps1 | 下载的PowerShell脚本 | | Windows | %TEMP%\rust-setup-launch.vbs | VBScript启动器 | | 服务端 | rust-crate_0.1.0 / _0.2.0 / _0.3.0 / _0.4.0 | 按OS与架构选择的二阶段载荷文件名 | | Linux(二阶段) | $HOME/.config/AzureKit 、$HOME/.config/ServiceKit | 受感染主机上创建的目录 | | Linux(二阶段) | MonoService 、MonoXpc | 二阶段可执行文件名及对应systemd服务 |
恶意crate及SHA256
| | | | — | — | | 制品 | SHA256 | | arrayref 0.3.10 | 25ad700976873c76af785cb99b33c48db7df8b81f21d1e9e06b3676b9a9373ae | | proc-macro1 1.0.107 | 61198155da51b838772eecf5bfaac6cbc4dcc388dccc56658fc28a8e831b34d4 | | proc-macro1 1.0.106 | b5c1b5b0763a8809a644a8f92224653f0aca623a98eecc714d27f74b80fbe436 |
被删除的恶意crate(全部版本):arrayref 0.3.10、internment 0.8.7、append-only-vec 0.1.9(账号被盗投毒);proc-macro1、proc-macro-en、aovine、arone、aronenao、tinymember(攻击者自有)。
其他指标:伪造作者邮箱 [email protected];仿冒账号 dtolney(crates.io用户ID 438608)。
特别说明:正版 proc-macro2 未受任何影响,切勿误伤。
参考链接
- https://safedep.io/arrayref-proc-macro1-rust-build-time-malware
- https://www.stepsecurity.io/blog/arrayref-rust-crate-supply-chain-attack
- https://www.wiz.io/blog/rust-supply-chain-attack-on-arrayref-significant-overlap-with-dprk-campaigns
- https://www.aikido.dev/blog/two-popular-rust-crates-arrayref-and-append-only-vec-compromised-in-supply-chain-attack
- https://tuxcare.com/blog/rust-attack-arrayref/
- https://thehackernews.com/2026/08/rust-supply-chain-attack-puts-build.html
- https://www.bleepingcomputer.com/news/security/hackers-poison-arrayref-rust-crate-to-push-infostealer-malware/
- https://github.com/rustsec/advisory-db
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:奇安信威胁情报中心 威胁情报中心 威胁情报中心《2.45亿次下载的Rust基石crate被投毒:一次编译,就是一次沦陷》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论