4 min read

The Rust arrayref attack turned a routine Cargo build into code execution

A poisoned dependency could execute code during compilation, so Rust teams should inspect every lockfile and Cargo cache touched on August 20.
The Rust arrayref attack turned a routine Cargo build into code execution
Rust arrayref attack banner showing Cargo compiling arrayref 0.3.10 and the proc-macro1 1.0.107 typosquat before running its malicious build script and executing a payload.

The Rust arrayref supply chain attack reached crates.io on August 20. Acting on a report from the Research Team at Nextron Systems, the Rust Security Response Team removed malicious releases of three established crates: arrayref 0.3.10, internment 0.8.7, and append-only-vec 0.1.9. Each release pulled in the proc-macro1 typosquat, named to resemble the legitimate proc-macro2 crate. The typosquat's build script downloaded and ran a platform-specific payload while Cargo compiled the dependency. For arrayref, that was the first dependency it had taken on in a decade.

Cargo build scripts run during compilation, so no application code needed to call the poisoned crate. A build that resolved one of the affected versions was enough to run the malicious build.rs with the developer or CI runner's permissions.

Crates.io deleted arrayref 0.3.10 after 86 minutes. The malicious internment and append-only-vec releases remained available for 90 and 107 minutes respectively. The response team also unyanked the earlier clean releases and locked the publisher account. It said the real maintainer was not believed to be acting maliciously, though the exact route into the account or machine remains unknown.

The yanks made the trap more convincing

The attacker made the malicious update look more urgent by yanking older clean versions. Those yanks produced warnings that could push developers toward an apparent update. The poisoned parent crates then brought proc-macro1 into otherwise legitimate dependency graphs.

arrayref has roughly 245 million lifetime downloads. That measures the clean crate's popularity, not compromised machines, but it explains why a short exposure window still matters.

The RustSec advisory says there is no evidence of actual usage of the malicious arrayref release. That should not be read as proof that nobody encountered it. A registry cannot provide a complete census of what ran on developer laptops and CI workers, and a cached crate only proves that its archive was downloaded. It does not prove that the build script or second-stage payload completed.

The r/rust thread includes two concrete practitioner reports. One developer said the crate appeared during an unlocked rerun installation that ended with a timeout. Another encountered a winit build error after the clean releases were yanked. The second developer's takeaway is useful: a yank warning is a reason to investigate, not an instruction to update blindly.

On Hacker News, much of the technical discussion focused on sandboxing build scripts through WASM, denying network access by default, or requiring allowlists. A second thread concerned dependency cooldowns that give scanners time to catch fresh malicious releases.

Check lockfiles and Cargo's cache

Teams should search Cargo.lock files for these versions:

  • arrayref 0.3.10
  • internment 0.8.7
  • append-only-vec 0.1.9
  • any version of proc-macro1

The final item is deliberately conservative. StepSecurity's incident analysis says proc-macro1 1.0.106, published at 01:55 UTC, was a verbatim copy of proc-macro2 staged under the squatted name. Version 1.0.107, published at 07:11, added the base64, rustls, and ureq build dependencies and the malicious build.rs. Finding only the 1.0.106 archive calls for investigation, but does not prove code execution.

The Rust team's official check goes further because crates deleted from the registry can remain in the Cargo registry cache under ~/.cargo/registry/cache. Its scan also covers proc-macro-en, aovine, arone, aronenao, and tinymember. A match deserves investigation on the machine that owns the cache, especially if it built Rust projects during the exposure window.

One r/rust commenter described the awkward split after removal: a cold build produced a generic missing-version error without identifying malware, while a warm cache could continue building the downloaded archive. The commenter also reported that cargo-audit did not flag it. StepSecurity independently confirms that warm caches and committed vendor/ directories can keep deleted versions building offline.

If a machine or CI job built one of the weaponized versions, treat that host as compromised until the investigation shows otherwise. Rotate every credential it could reach, including SSH keys, cloud tokens, crates.io API tokens, signing keys, and CI secrets. Delete the matching .crate files, purge affected CI caches, rebuild any vendor/ directory from clean sources, and rebuild artifacts produced after exposure on clean infrastructure. A deleted registry version can keep building from a warm cache or vendored copy.

As of August 21, crates.io lists arrayref 0.3.9, internment 0.8.6, and append-only-vec 0.1.8 as the newest clean versions. Pinning and committed lockfiles reduce surprise, though they do not solve the wider problem of fresh dependency resolution in CI.

Cargo has a more direct mitigation close to landing. min-publish-age keeps newly published dependencies out of resolution for a configured period, giving registries and scanners time to react. It is available on Nightly today as -Zmin-publish-age. Its stabilization PR entered final comment period with intent to merge on August 18 and is designed to close the tracking issue. PR author epage said in the r/rust thread that it is expected in Rust 1.100 absent concerns.

Once stable, teams can set the delay in .cargo/config.toml:

[registry]
global-min-publish-age = "7 days"

The proposed override for a critical update is CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow cargo update -p foo, with the choice preserved in the lockfile. The feature remains opt-in, and epage said the Cargo team wants real-world use before deciding whether to enable it by default.

What is settled is the package chain, the affected versions, and the fact that compilation could execute the payload. The unknown is how many builds actually reached that point. Inspect the lockfiles and caches that crates.io cannot clean up for you.