Skip to main content
Rust Crate Attack: 86 Minutes to ContainmentIncident
4 min readFor Security Engineers

Rust Crate Attack: 86 Minutes to Containment

Recently, malicious code was pushed to three popular Rust crates with a combined 245 million downloads. The Rust Security Response Team removed them 86 to 107 minutes later. Here's what failed, what worked, and what you need to fix in your build pipeline.

What Happened

A compromised maintainer account published malicious versions of three crates on crates.io: arrayref, file-id, and instant. The attacker added a typosquatted dependency called proc-macro1 (note the "1" instead of "2") that executed a remote payload during the build process. Nextron Systems GmbH researchers spotted the malicious code and reported it to the Rust Security Response Team, who yanked the packages.

The scale matters here. Arrayref alone had logged 245,385,500 all-time downloads and 53,905,601 downloads in the 90 days prior to the attack. These aren't obscure libraries; they're foundational dependencies that cascade through the ecosystem.

Timeline

The attack window was narrow:

  • T+0: Malicious versions published to crates.io
  • T+86 to T+107 minutes: All three crates removed from registry
  • Post-incident: No CVE identifiers issued, no patched versions released

The Rust team hasn't published detailed telemetry about how many builds pulled the compromised versions during that window. If you built Rust projects during the exposure period, check your artifact hashes against known-good versions.

Which Controls Failed

Account security broke first. The maintainer account compromise is the root cause. We don't know if this was credential stuffing, phishing, or token theft, but the outcome is the same: an attacker gained write access to high-trust packages.

Dependency verification didn't exist. The typosquatted dependency (proc-macro1 vs proc-macro2) passed through without triggering alerts. Your build system should flag dependencies that differ by one character from commonly-used packages. It didn't.

Build-time execution went unchecked. The malicious code ran during compilation, not at runtime. Your standard runtime protections -- sandboxing, EDR, network monitoring -- don't see build-time payloads. The attack executed before you deployed anything.

No cryptographic signing requirement. Crates.io doesn't require package signing. You're trusting that the account publishing the package is legitimate, but you have no cryptographic proof of author identity. Compare this to npm's package provenance or Sigstore's signing infrastructure.

What Standards Require

ISO/IEC 27001:2022 Control 8.31 (Security of information in use) requires you to protect information during processing. Build-time malware violates this control because your CI/CD pipeline processes untrusted code without isolation or verification.

NIST 800-53 Rev 5 Control SA-10 (Developer Configuration Management) requires you to track and control changes to system components during development. A compromised dependency that injects code during builds is exactly the threat this control addresses. You need configuration baselines for your build dependencies, not just your application code.

PCI DSS v4.0.1 Requirement 6.3.2 states that security vulnerabilities are identified and addressed based on a defined risk ranking methodology. Build-time malware represents a critical supply chain risk, but if you're only scanning compiled artifacts, you're missing the attack vector entirely.

SLSA Framework Level 2 (Supply-chain Levels for Software Artifacts) requires provenance for all build steps and dependencies. If you can't prove which version of proc-macro2 your build consumed, you can't meet SLSA 2. The typosquat attack exploits exactly this gap.

Lessons and Action Items

Implement dependency hash pinning. Lock your dependencies to specific hashes, not just version numbers. A Cargo.lock file pins versions, but an attacker who controls the version can still publish malicious code. Use cargo-vet or similar tools to maintain an allowlist of audited dependency hashes.

Deploy typosquatting detection. Add a pre-build check that flags dependencies with Levenshtein distance less than 2 from your existing dependency graph. This catches proc-macro1 vs proc-macro2 automatically. Tools like Phylum and Socket Security provide this capability for Rust and other ecosystems.

Isolate your build environment. Run builds in ephemeral containers with no network access after dependency resolution. If the malicious payload tries to phone home during compilation, it fails. This doesn't prevent the compromise, but it limits the attacker's ability to exfiltrate credentials or download second-stage payloads.

Audit your maintainer account security. Require hardware security keys for all accounts with publish access to internal or public registries. Enforce short-lived tokens with IP restrictions. Review which accounts have publish rights and revoke unnecessary access. The Rust maintainer whose account was compromised likely had credentials that predated modern MFA requirements.

Monitor your build artifacts. Diff your compiled binaries against known-good builds from trusted infrastructure. Unexpected changes in binary size or entropy indicate injected code. Tools like diffoscope can automate this comparison, though reproducible builds are still hard in practice.

Establish a dependency review process. Before adding a new dependency, check its maintainer history, release cadence, and dependent count. A package with 245 million downloads and a single active maintainer is a concentration risk. Document your review criteria and make it a required step in your pull request template.

Test your incident response for supply chain attacks. The Rust team contained this in under two hours. Can you identify which builds consumed a compromised package? Can you rebuild from known-good sources? Can you notify downstream consumers? Run a tabletop exercise with your team and document the gaps.

The 86-minute containment window here was exceptional. Most supply chain compromises sit undetected for weeks. Your job is to ensure that even during that window, the malicious code can't execute, can't communicate, and can't persist. Build-time is runtime for your CI/CD pipeline. Treat it that way.

SLSA Framework

Topics:Incident

You Might Also Like