Attackers used a compromised Rust crate maintainer account to publish new versions that secretly added a typosquatted dependency. That dependency’s build script downloaded and executed malware during compilation, meaning developers could be infected just by building a project. The attacker also “yanked” safe versions to push developers toward the malicious version as the “fix.”
How the attack worked
Attackers gained control of a maintainer account on crates.io and used it to publish malicious releases of three widely used Rust crates: arrayref, internment, and append-only-vec. Each malicious release added just one line to its manifest, a new dependency on a crate called proc-macro1, a typosquat of the extremely common proc-macro2 package. Because the malicious code lived inside that dependency's build script, resolving and building a project that pulled it in was enough to execute the payload. No code from the compromised crates themselves needed to be called.
Why it succeeded
The attacker also yanked the previously safe versions, 0.3.5 through 0.3.9, under the compromised owner account. This triggered cargo's standard warning telling developers to update to a version that is not yanked. That routine, trusted tooling message became the social engineering lure, nudging developers toward the malicious release as if it were the fix rather than the threat. The attack relied on developers' habit of trusting build tool prompts and quickly resolving dependency warnings without questioning why versions were pulled in the first place.
What to watch for
- A dependency suddenly becomes yanked across multiple versions at once, especially several in a row
- A new release adds an unexpected dependency with a lookalike or near-identical name to a well-known package
- Build processes triggering unexpected network activity or executing new scripts and binaries
- Persistence artifacts on developer or build machines, since the payload was reported to run on Windows, macOS, and Linux with beaconing behavior over HTTPS to a fixed path
How to build resistance
Teams can reduce exposure to this style of supply chain social engineering by treating urgent "update now" tooling messages with the same scrutiny as a phishing email. Before updating a yanked dependency, verify why it was yanked and by whom. Restrict and monitor network access and process spawning during build steps, since builds should be treated as capable of executing arbitrary code. When a supply chain alert surfaces, check local package caches, such as searching the cargo registry cache for deleted crate files, and pin dependencies to known-good versions while the incident is investigated. Building this kind of verification habit into routine dependency updates is a practical way to blunt attacks that exploit trust in build tooling warnings.
Key findings
- A compromised maintainer account published malicious releases of three popular Rust crates (arrayref, internment, append-only-vec).
- The malicious releases added a single new dependency on a typosquat crate (proc-macro1 vs proc-macro2).
- The injected dependency executed a malicious build script that downloaded and ran a remote payload during compilation (build-time execution).
- Attackers “yanked” safe versions (0.3.5–0.3.9) to create a warning that nudged developers into updating to the malicious version.
- Stage-2 behavior included HTTPS beaconing to a fixed path, persistence on Windows/macOS/Linux, and capability to download/run additional scripts.
- IoCs included IPs 23.254.165.112 (payload host/C2), domain hwsrv-798836.hostwindsdns.com, and dropped files like /tmp/rust-setup and %TEMP%\rust-setup.ps1.
Who’s being targeted
- Commonly targeted roles: Developers, DevOps, Build/Release Engineering, Security Engineering, IT (Developer Endpoint Support).
- Affected industries: Software development, Technology, Open source ecosystems.
- Attack channels: website.
- Impersonated: Trusted open-source dependency maintainer / crates.io package release.
Red flags to watch for
- A dependency suddenly becomes yanked across multiple versions at once
- A new release adds an unexpected dependency with a lookalike name (typosquat)
- Build process triggers network activity or executes new scripts/binaries
Frequently asked questions
How did the malicious Rust crate infect developers?
Attackers compromised a maintainer account and published new releases of three popular crates that added a single new dependency, a typosquat crate named proc-macro1 mimicking proc-macro2. That dependency's build script downloaded and ran a malicious payload during compilation, so simply building a project was enough to trigger infection.
Why did developers trust the malicious update?
The attacker yanked the safe versions (0.3.5 to 0.3.9) under the compromised owner account, which caused cargo to display a warning suggesting developers update to a version that was not yanked. That warning acted as the lure pointing developers toward the malicious release.
What indicators of compromise were identified?
Reported IoCs include the IP 23.254.165.112 used as a payload host and command-and-control point, the domain hwsrv-798836.hostwindsdns.com, and dropped files such as /tmp/rust-setup and %TEMP%\rust-setup.ps1.
What should developers do to reduce this kind of risk?
Treat unexpected yanking of multiple versions and sudden new dependencies as red flags, restrict build-time network access on developer and build machines, and pin known-good versions such as arrayref at 0.3.9 or earlier while checking local caches for deleted crate files.
Read the video transcript
You run `cargo build` like always… and that alone is enough to drop malware on your laptop. Attackers stole a Rust crate maintainer account and pushed new versions of arrayref, internment, and append-only-vec. One tiny change: a new dependency on `proc-macro1`, a typosquat of `proc-macro2`, whose build script quietly downloaded malware during compile. Here’s the trap: safe versions 0.3.5 to 0.3.9 were all yanked, so Cargo flashes: 'consider updating to a version that is not yanked.' You update, run `cargo build`, and the BitB-style move happens at build time: the injected crate phones home and drops `/tmp/rust-setup` or `%TEMP%\rust-setup.ps1`. Your move: when Cargo nags you about yanked versions, stop and inspect the diff. If a crate update adds a surprise dependency like `proc-macro1`, don’t build it, pin to the last known-good version and get it reviewed.