Skip to content
RESEARCH INDEX BREACHROAD / INTELLIGENCE NOTE

Rust: arrayref, internment and append-only-vec Ran Malware at Build Time

Three compromised crates.io releases executed a malicious build script. Understand the 86–107 minute window, Cargo cache, lockfiles, CI and response.

PUBLIC RESEARCH
AUTHOR
/ CEO Breachroad · OSCP · PNPT
PUBLISHED
21 August 2026
READING TIME
18 min read
TOPIC
Supply Chain Security
Rust: arrayref, internment and append-only-vec Ran Malware at Build Time

RustSec and CVE records completed the picture of a crates.io supply-chain attack on 21 August. The malicious releases were arrayref 0.3.10, internment 0.8.7 and append-only-vec 0.1.9. Each received a direct dependency on proc-macro1, whose build script downloaded a malicious payload. The Rust team removed the packages, yanked compromised releases, restored legitimate versions that the attacker had yanked and locked the maintainer account as a precaution.

This is a compromise, not an ordinary software flaw. An application does not need to call a vulnerable function. A build script runs when Cargo fetches or compiles a dependency, with the privileges of the cargo process—often on a developer laptop or CI runner that can access source code, registry tokens, caches, artifacts and release mechanisms.

The exact exposure window

Rust’s Security Response Team published precise times:

  • [email protected] was online for approximately 86 minutes, from 07:15:00 UTC to 08:41:40 UTC on 20 August;
  • [email protected] was online for approximately 90 minutes, from 07:34:07 UTC to 09:04:11 UTC;
  • [email protected] was online for approximately 107 minutes, from 07:37:49 UTC to 09:25:24 UTC.

RustSec says the malicious arrayref release was downloaded 2,285 times, less than ten per cent of traffic across arrayref versions because many users were pinned to older releases in lockfiles. A short window limits scale, but automated pipelines and update bots can consume a new version within minutes.

The Rust team also named every version of proc-macro1, proc-macro-en, aovine, arone, aronenao and tinymember for removal. It does not believe the popular arrayref author acted maliciously; the person’s machine or credentials were probably compromised. That matters for accurate communication, but it does not change the technical need to inspect environments.

Why Cargo.lock helps without guaranteeing safety

An application with a committed Cargo.lock normally remains on a resolved version until someone updates dependencies. That behaviour limited adoption of 0.3.10. Libraries often omit a lockfile, however, and CI may run cargo update, regenerate locks or build examples with a fresh resolver. The crate may also enter transitively.

Yanking and deletion do not erase a local Cargo cache or a previously built artifact. A runner may retain an archive under ~/.cargo/registry/cache, an internal mirror may have copied it and a build image may already have been published. Incident response cannot end with another cargo build after the bad version disappears from the public registry.

Determine whether the release was fetched or built

Inspect Cargo.lock across branches and release tags, resolver logs, Cargo caches on workstations and runners, dependency proxies, and artifact SBOMs. Search for the exact three versions and the named intermediary packages. Running cargo tree on today’s main branch is insufficient when the malicious dependency existed only in a short-lived build that was later superseded.

Preserve timing and context: cache creation time, job ID, image digest, commit, runner identity and every secret available to the process. Do not execute a discovered archive or build script to “confirm” it. A fetch or build in the campaign window establishes exposure; analysis of malicious code belongs in an isolated malware environment.

The Rust team published a defensive cache search. At enterprise scale, distribute equivalent detection through EDR or configuration management. Return hashes and paths rather than attaching suspect crate files to help-desk tickets.

A lockfile entry is not always execution

A lockfile entry proves dependency resolution but does not by itself prove that the build script ran. Determine whether the job fetched and compiled the crate. If a pipeline failed before build or used a prebuilt artifact, impact may be lower. Absence of a C2 connection in one log is not proof of safety when DNS, proxy or EDR coverage is incomplete.

Move to unaffected versions: arrayref <=0.3.9, internment <=0.8.6 and append-only-vec <=0.1.8. RustSec lists no “patched version” because the malicious releases were removed; it identifies those older ranges as unaffected. Rebuild from a clean cache and verified source after changing the lockfile.

If the build script executed

Treat the runner or workstation as potentially compromised. Remove its ability to publish, but preserve process, network, Cargo cache, workspace and EDR evidence. Do not merely run cargo clean; that can remove useful artifacts without reversing the payload’s actions.

Inventory every credential reachable from the job: GitHub or GitLab tokens, crates.io and container registry credentials, signing keys, cloud workload identity, SSH deploy keys, npm or PyPI tokens and environment variables. Revoke according to privilege and evidence. Block release and administrative capability first, then read-only access. Review short-lived tokens too, because they may have been exchanged for a more persistent foothold.

Quarantine artifacts built by the affected job. Compare their provenance, digest, SBOM and signature with a clean rebuild. If a suspect artifact reached an environment, investigate runtime separately. Build-time compromise does not prove malware entered the final binary, but defenders cannot assume it remained only on the runner.

Hardening Rust CI

A runner compiling untrusted dependencies should not also hold standing release credentials. Separate build, test, signing and publishing. Pass a verified digest into release, and obtain a short-lived workload identity only after a policy or human gate. Restrict build-runner egress to required registries; a build script downloading from an arbitrary domain should be blocked and alerted.

Use dependency review for Cargo.lock, a cooling-off period before automatically accepting fresh versions and an allowlist for critical crates. A mirror is not a protection when it copies every public package immediately. It needs quarantine, scanning, provenance metadata and an emergency deny mechanism.

Generate SBOM and SLSA-style provenance at build time. Bind signatures to the source digest, lockfile, builder image and dependencies. Those records make it possible to locate the artifacts that were actually exposed rather than rotating an entire organisation blindly.

Source facts and Breachroad conclusions

Package names and versions, publication and deletion times, 2,285 arrayref downloads, the proc-macro1 mechanism and crates.io actions come from Rust and RustSec. CVE-2026-77649, CVE-2026-77650 and CVE-2026-77651 were published on 21 August. The sources do not publish complete C2 telemetry for every environment and do not establish that every download resulted in compromise.

Credential order, artifact quarantine, release separation and mirror design are Breachroad conclusions. Cybersecurity training for developers can rehearse this playbook, while an IT security audit can assess runners, tokens, egress, provenance and dependency governance.

Sources

SHARE / COPY