TL;DR - On 20 August 2026 a threat actor compromised the maintainer account behind three Rust crates and published arrayref 0.3.10, internment 0.8.7, and append-only-vec 0.1.9. Each one added a single typosquatted dependency (
proc-macro1) whosebuild.rsran an infostealer at compile time. arrayref has 245 million all-time downloads and 403 reverse-dependencies. The malicious versions were live for 86 to 107 minutes. Any CI job that compiled any of the three in that window executed the payload. What you need to do: run the four-name Cargo.lock grep below, sweep the IOC list, and on any hit rotate credentials and pin arrayref to 0.3.9.
If your organisation builds anything in Rust, or audits vendors who do, this is the week to check what your pipeline actually resolved. The window was under two hours per crate, but a lockfile regenerated in that window keeps the poisoned version forever until you act.
What I did Tuesday morning
I was at the kitchen table at 6:40am with the second coffee on, reading the Rust Security Response Team advisory on my phone while the kids ate toast. The headline number: 245 million downloads of a crate called arrayref. That did not match what I knew about ANZ engineering teams. arrayref is not a household name. It is a 60-line utility crate that lets you turn a &[T] into a &[&[T]] of fixed-size chunks. Nobody puts it in a tweet.
The mismatch is the story. The 245 million figure is real because arrayref sits in the dependency graph of crates that sit in the dependency graph of things like blake3, egui, iced, and the rendering stack for both Ethereum and Solana tooling. When something that small and that central gets poisoned, the blast radius is whatever compiled in the exposure window.
This is the part I want you to slow down on: the malicious versions were live for 86 to 107 minutes. That is not a typo. crates.io does not have a human-review tier. Anyone who owns a crate, or who has just taken over the account of someone who does, can publish a new version and it is downloadable worldwide in seconds.
A poisoned Rust crate that sits at the bottom of a 400-deep dependency tree does not need to be famous. It only needs to be present in one place that compiles.
By The Numbers
| Metric | Detail |
|---|---|
| Malicious releases | arrayref 0.3.10, internment 0.8.7, append-only-vec 0.1.9 |
| Time live on crates.io | 86, 90, and 107 minutes respectively (20 Aug 2026, UTC) |
| arrayref all-time downloads | 245,385,500 (verified via the crates.io API on 21 Aug) |
| arrayref downloads, last 90 days | 53.9 million |
| Crates depending on arrayref | 403 |
| Payload trigger | cargo build, cargo check, cargo test via the proc-macro1 build.rs |
| Persistence | Registry Run key (Windows), LaunchAgent (macOS), systemd user service (Linux) |
| CVEs assigned | None. No patched version exists. Pin to 0.3.9 or earlier. |
| Known real-world usage of malicious versions | None observed by RustSec as of 21 Aug |
The five-minute Cargo.lock check
I verified this grep against the real lockfile format, where each entry is a [[package]] block with name = "arrayref" on one line and version = "0.3.10" on the next. Run it from the repo root, per project, against the lockfiles your CI actually consumed last week:
grep -A1 -E 'name = "(arrayref|internment|append-only-vec|proc-macro1)"' Cargo.lock
A clean project returns nothing. A hit returns the name and version pair. The only versions that matter are arrayref 0.3.10, internment 0.8.7, append-only-vec 0.1.9, or any proc-macro1 entry at all. That crate never had a legitimate release.
Three follow-ups worth running in the same sitting:
cargo tree -i arrayref
This shows which of your dependencies pulled arrayref in, so you know whose upstream to chase.
find ~/.cargo/registry/cache -name "arrayref-0.3.10.crate" -o -name "proc-macro1-*.crate"
This checks the local cache on developer machines and build runners. A cached .crate file proves the version was downloaded on that host, which is a stronger signal than the lockfile alone. Record the path before you delete the file as evidence.
Then check your CI history for cargo build, cargo check, or cargo test runs timestamped between 07:15 and 09:26 UTC on 20 August 2026. The lockfile tells you what could have resolved. The CI log tells you what actually compiled.
The indicator list
From the published IOCs, cross-checked against the Wiz and StepSecurity analyses:
- Network:
23.254.165.112on port 9089 (payload host) and port 443 (C2), plushwsrv-798836.hostwindsdns.com. The beacon is an HTTPS POST to the path/49890878. - Files:
/tmp/rust-setupon Linux and macOS.%TEMP%\rust-setup.ps1and%TEMP%\rust-setup-launch.vbson Windows, launched hidden viawscript.exe. - Persistence artefacts: a systemd user service on Linux, a LaunchAgent on macOS, a Registry Run key under
HKCU\Software\Microsoft\Windows\CurrentVersion\Runon Windows. - Binaries:
rust-crate_0.1.0throughrust-crate_0.4.0. - Registry accounts:
dtolney(crates.io id 438608), the impersonator account. Forged author metadata usedrchaitm@gmail.com.
Search your proxy and DNS logs for the IP and hostname as far back as 20 August. The implant beacons from the host that compiled the crate, usually a CI runner or a developer workstation, and those are exactly the logs SMBs keep shortest.
One honest caveat from the public analysis: the Windows stage was observed querying the origin_url and username_value columns of browser login databases, not password_value directly. The Linux and macOS stages were hashed in the samples and not analysed in the open. Treat host info and credential metadata as stolen on any confirmed hit and rotate accordingly.
If the grep hits: containment in order
- Snapshot, then isolate. Preserve the build runner's disk and logs before anything reboots or cleans itself.
- Rotate everything the host could reach. CI tokens, deploy keys, signing keys, cloud credentials in the runner's environment, package publish tokens, browser sessions on developer workstations. Assume compromise and rotate all accessible secrets in one pass.
- Rebuild, do not clean. The stage-2 implant persists via systemd, LaunchAgent, or the Registry Run key. Rebuild build runners from a trusted image; do not trust a clean-and-rebuild on a host that may have a persistence artefact you missed.
- Pin and update the lockfile in the same commit. The Rust Security Response Team unyanked 0.3.5 through 0.3.9, so
cargo update -p arrayref --precise 0.3.9is available now. Do the pin in the same change as the rotation so nobody re-resolves 0.3.10 from a stale cache. - Chase the chain upstream. If
cargo tree -i arrayrefshows a vendor's crate pulled it in, tell them, with your CI timestamp. blake3 dropped its arrayref dependency in 1.8.7 at 09:09 UTC and the blake2 SIMD crates followed at 09:25, so upstreams are moving fast.
Why this one slipped past the usual checks
The malicious manifest differed from the real crate by one dependency line, and the library source of proc-macro1 was a genuine copy of proc-macro2, so builds completed normally with no test failures and no suspicious diffs in the crates themselves. The attacker set up a fake GitHub account for a well-known Rust developer hours before publishing, and released a benign proc-macro1 1.0.106 first so the malicious 1.0.107 update would look like an ordinary patch bump of an established package.
Two of the three build-time defences you would want here do not exist yet in the Rust world. Google and GitHub recommend cooling-off windows on newly published packages, and GitHub shipped a 3-day Dependabot cooldown in July 2026. The Cargo equivalent, a global-min-publish-age setting, entered final comment period on 18 August, two days before this attack, and was still unmerged as of 21 August (rust-lang/cargo#17335). Worth watching that PR, and worth asking your registry proxy vendor whether they can age-gate new versions in the meantime.
Attribution is partial. Wiz found the campaign infrastructure overlaps recent DPRK-linked supply-chain attacks (Mastra, attributed by Microsoft to Sapphire Sleet, and axios, attributed by Google's Threat Intelligence Group to MIDNIGHT NEPTUNE). No vendor has attributed the crates.io incident to a named actor.
The same week on the npm side
The Rust incident was not the only registry event in this window. On 21 August, 14 trojanized npm packages were documented (The Hacker News): streak- and map-themed calendar utilities that ship a working product and quietly bundle the RedC2 4.0 Linux backdoor. Import the module anywhere in your dependency graph, even transitively, and the payload runs. RedC2 ships an LLM-driven component called Red Agent that turns natural-language instructions into beacon commands, and sells for $99.99 on hacking forums.
The two campaigns are not linked by attribution. One looks DPRK-adjacent, the other is commodity malware delivery. Together they say the quiet part out loud: every package registry your developers touch is a live attack surface this month, and build-time plus import-time execution is the pattern to defend against.
How this differs from the LiteLLM leak and the npm worm
I have written about two supply-chain incidents here recently, and the defence priorities differ:
- The LiteLLM leak was a Python package that scraped process memory at runtime. The fix was rotating provider keys and checking gateway logs.
- The npm self-propagating worm was an npm worm spreading through stolen publish tokens and
preinstallhooks. The fix of the week was lifecycle-script controls (npm ci --ignore-scripts). - This one executes at compile time through a legitimate Cargo feature (
build.rs) that has no--ignore-scriptsequivalent in wide use. Your lockfile and your CI timestamps are the evidence. If you run a Rust pipeline, the grep above is the whole audit.
The Monday checklist
- Run the four-name Cargo.lock grep across every Rust repo, including vendor code.
- Check CI history for builds between 07:15 and 09:26 UTC on 20 August.
- Search
%TEMP%and/tmpfor the three dropped file names on build hosts and dev workstations. - Search proxy and DNS logs back to 20 August for
23.254.165.112andhwsrv-798836.hostwindsdns.com. - Check systemd user services, LaunchAgents, and Run keys on anything that compiled in the window.
- Pin arrayref to 0.3.9 (
cargo update -p arrayref --precise 0.3.9) and commit the lockfile. - Rotate CI tokens and signing keys on any host with a confirmed hit, then rebuild it.
- Ask your npm-side teams whether any streak- or map-named package entered a lockfile this month.
- Watch rust-lang/cargo#17335 and evaluate an age gate in your registry proxy.
Further Reading
- Supply chain attack on arrayref, Rust Security Response Team (Rust Blog, 20 Aug 2026)
- Rust supply chain attack puts build-time malware in crates with 245 million downloads, The Hacker News (21 Aug 2026)
- Hackers poison arrayref Rust crate to push infostealer malware, BleepingComputer (20 Aug 2026)
- 14 trojanized npm packages drop RedC2 4.0 Linux backdoor with AI-assisted C2, The Hacker News (21 Aug 2026)
- RUSTSEC-2026-0260, arrayref advisory (RustSec, current)
- Arrayref Rust crate supply chain attack, StepSecurity (20 Aug 2026)
- Rust supply chain attack on arrayref: significant overlap with DPRK campaigns, Wiz (20 Aug 2026)
global-min-publish-age, rust-lang/cargo#17335 (GitHub, current)- LiteLLM supply-chain leak, SecureInSeconds (Aug 2026)
- Self-propagating npm worm, SecureInSeconds (Aug 2026)
If you run builds for an ANZ organisation that ships Rust, or you audit vendors who do, forward this to whoever owns the CI runners. The grep takes five minutes. The rotation takes a day. Both are cheaper than explaining a signing-key theft to a customer.
Mathew Clark
Founder, SecureInSeconds
Currently: explaining to a very confident developer that cargo check counts as code execution.



