8月20日、SafeDepが「Malicious Rust Crate arrayref Runs a Build-Time Payload」と題した記事を公開した。累計約2億4,500万ダウンロードを誇る人気Rustクレートarrayrefの改ざんされたバージョンが、ビルド時に外部バイナリを実行するサプライチェーン攻撃に悪用されたことが明らかになった。
今回の攻撃が特に深刻なのは、開発者が一行もコードを書かなくても、cargo buildを叩いた瞬間にペイロードが実行されるという点だ。コードレビューでは原理的に防げない。Rustのビルドシステムにおけるbuild.rs(ビルドスクリプト)の仕組みを逆手に取った巧妙な手口で、アカウント乗っ取り・typosquatting・依存チェーンの深部への潜り込みという3つの要素が組み合わされている。
cargo buildを叩くだけでペイロードが走る仕組み
2026年8月20日、arrayrefのバージョン0.3.10がcrates.ioに公開された。このバージョンにはproc-macro1という依存関係が追加されており、そのbuild.rs(Cargoがビルド時に自動実行するスクリプト)が外部サーバーからバイナリをダウンロードして実行する。cargo buildを実行した瞬間にペイロードが走るため、コードの中身を一切確認しなくても被害を受ける。
arrayrefはtiny-skia→sctk-adwaita→winitという依存チェーンを経て、egui・eframe・icedといったRust製GUIフレームワーク全般の依存グラフ深部に入り込んでいる。これらフレームワークを使っているプロジェクトは、自分では直接arrayrefを使っていなくても影響を受ける可能性がある。
攻撃の構造:3つの巧妙な仕掛け
1. アカウント乗っ取りとyankによる誘導
arrayrefの管理者droundyのアカウントが乗っ取られ、0.3.10が公開された。それだけでなく、攻撃者は過去の正規版(0.3.5〜0.3.9)をyank(非推奨化)した。Cargoはyankされたバージョンがあると「yankされていないバージョンへの更新を検討してください」と警告を出すため、開発者が唯一yankされていない0.3.10へ自動的に誘導される。RustSec(Rustエコシステムのセキュリティアドバイザリデータベース)へのアドバイザリを最初に報告した開発者自身も、この誘導によって0.3.10を引いてしまったとSafeDepは報告している。
2. proc-macro1によるtyposquatting偽装
追加された依存クレートproc-macro1は、著名なRustライブラリproc-macro2とは別物だ。src/以下のコードはproc-macro2のソースをそのままコピーし、名前だけ機械的に置換したもので、ビルドは正常に通る。
公開者のアカウント名はdtolneyで、proc-macro2の著名な作者David Tolnayの実際のアカウントdtolnayと1文字しか違わない(typosquatting)。メタデータにはDavid Tolnayの名前と偽メールアドレスが記載され、repositoryフィールドも存在しないGitHubリポジトリを指している。
唯一不自然な点は、このクレートのビルド依存にbase64・rustls・ureqが含まれていること。トークン処理ライブラリにTLSスタックとHTTPクライアントが必要になる理由はない。
3. ビルドスクリプトによるペイロード実行
proc-macro1のbuild.rsは、サーバーのアドレスをbase64フラグメントに分割して保持する。静的解析でURLが直接見えないようにするためだ。
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となる。TLS接続には証明書を一切検証しないカスタム実装が使われている。
impl ServerCertVerifier for AcceptAll {
fn verify_server_cert(/* ... */) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
// verify_tls12_signature / verify_tls13_signature も無条件でOKを返す
}
OSごとの実行方法
ビルドスクリプトはOS・アーキテクチャを判定し、対応するバイナリを選択する。対応プラットフォームは以下の4つで、それ以外はビルドをpanicで中断する。
fn link_suffix() -> &'static str {
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"),
}
}
Unix(Linux/macOS) の場合、ダウンロードしたバイナリを/tmp/rust-setupに書き込み、chmod +x後にC2アドレスを引数として渡してdetached実行する。標準ストリームはすべて/dev/nullに捨てる。
Windows の場合はより凝った実装だ。取得するペイロードはEXEではなくPowerShellスクリプトで、%TEMP%\rust-setup.ps1に書き込まれる。さらにVBScriptランチャー(rust-setup-launch.vbs)を経由してwscript.exeで起動する仕組みになっている。ソースコード内のコメントによると、「WScriptを経由することでCargoのJobオブジェクトから脱出できる。直接spawnするとCargoがプロセス終了を待ち続ける」とのことだ。最後にstd::mem::forgetでwscriptの子プロセスハンドルをリークし、デストラクタが走らないようにしてCargoからプロセスを完全に切り離している。
関連クレートと侵害指標(IOC)
今回の事案に関わるクレートは以下のとおり。
| クレート | バージョン | 状態 |
|---|---|---|
| arrayref | 0.3.10 | 悪意あり、削除済み |
| proc-macro1 | 全バージョン | 悪意あるtyposquat、削除済み |
| append-only-vec | 0.1.9 | 同一IPへの通信が確認されており同一アクターによる侵害の疑いあり |
| arrayref | 0.3.9以前 | クリーン |
append-only-vecはRustの追記専用ベクタ実装クレートで、今回の調査の中で同一C2 IPへの通信が確認された。SafeDepは同一アクターによる侵害の疑いがあるとしており、注意が必要だ。
主要なIOC(Indicators of Compromise:侵害指標)は以下のとおり。
| 種別 | 内容 |
|---|---|
| ネットワーク | 23.254.165.112:9089(ペイロード取得) |
| ネットワーク | 23.254.165.112:443(C2) |
| ファイル(Unix) | /tmp/rust-setup |
| ファイル(Windows) | %TEMP%\rust-setup.ps1、%TEMP%\rust-setup-launch.vbs |
arrayref 0.3.10のSHA256は25ad700976873c76af785cb99b33c48db7df8b81f21d1e9e06b3676b9a9373ae。
確認と対処
crates.ioチームはすでに悪意あるバージョンを削除している。Cargo.lockにarrayref 0.3.10またはproc-macro1が含まれている場合は、0.3.9以前へのピン留めが必要だ。/tmp/rust-setupの存在や上記IPへの通信履歴がある場合はペイロード実行を疑うべきである。
RustSecアドバイザリはrustsec/advisory-db#3161で公開されている。
詳細はMalicious Rust Crate arrayref Runs a Build-Time Payloadを参照していただきたい。