Feed
CriticalPublished 20 Aug 20265 packages · 4 versions

Rust crates `arrayref` 0.3.10 + `internment` 0.8.7 + `append-only-vec` 0.1.9 poisoned with a build-time dropper via typosquat dependency `proc-macro1` (crates.io, 2026-08-20, 245M+ combined downloads, DPRK-overlap attribution)

Summary

Compromised maintainer accounts published arrayref@0.3.10 (244M downloads), internment@0.8.7, and append-only-vec@0.1.9 to crates.io. Each release added a single dependency line on typosquat crate proc-macro1, whose build.rs downloads and executes a platform-specific payload from 23.254.165.112:9089 during cargo build. Wiz attributes overlap to DPRK campaigns.

maintainer-takeoveraccount-takeovertyposquatinfostealercredential-theftci-cd-compromiseobfuscation
Threat actor
DPRK-linked (Wiz-reported infrastructure overlap with Famous Chollima / Contagious Interview campaigns)
Detected by
Socket · Wiz · SafeDep · Semgrep · StepSecurity · Aikido Security · Rust Security Response Team
Also known as
arrayref supply chain attack · proc-macro1 build-time dropper · 2026-08-20 crates.io compromise · Rust arrayref internment append-only-vec attack
Ecosystems
crates.io
Packages tracked
5

What happened

On the morning of 2026-08-20 UTC, a coordinated build-time supply-chain attack landed on crates.io. Between 07:11 and 09:25 UTC, three widely-used Rust crates were republished with a single-line manifest change that pulled in a typosquat dependency called proc-macro1; whenever any host on the internet subsequently ran cargo build, cargo update, or a CI resolve step that touched those crates, the compromised build.rs fired and dropped a target-matched infostealer binary. All three crates and the dropper were yanked by the Rust Security Response Team within 3 hours, but the reach is unusually large because arrayref is a foundational transitive dependency across most of the Rust ecosystem (Wiz observed it in ~75% of environments where any Rust is present).

Affected crates and takedown windows

| Crate | Malicious version | Publisher account | Published (UTC) | Yanked (UTC) | Downloads | |---|---|---|---|---|---| | arrayref | 0.3.10 | droundy (compromised) | 2026-08-20 07:15 | 08:41 | 244M all-time, 53.7M in 90 days | | internment | 0.8.7 | (compromised maintainer) | 2026-08-20 07:34 | 09:04 | 14M all-time | | append-only-vec | 0.1.9 | (compromised maintainer) | 2026-08-20 07:37 | 09:25 | 4M all-time | | proc-macro1 (dropper) | 1.0.107 | dtolney (compromised) | 2026-08-20 07:11 | 08:03 | new crate, typosquat of proc-macro2 | | proc-macro-en (dropper variant) | seen | - | 2026-08-20 window | - | new crate |

Each compromised release added a single dependency line to Cargo.toml naming proc-macro1. The library source of proc-macro1 is a genuine copy of proc-macro2, so builds succeed with no compile-time signal - all malice sits inside build.rs. To create update pressure toward the poisoned arrayref@0.3.10, the attacker also yanked all prior versions 0.3.5 through 0.3.9, so any cargo resolver rolling a ^0.3 range forward landed on the malicious release; the Rust Security Response Team subsequently un-yanked the clean prior versions after the incident.

Payload behaviour (build.rs)

The malicious build.rs runs during compilation, before any code the developer wrote executes. It:

  1. Reconstructs C2 URLs from base64-encoded fragments so a static scan for the literal IP string misses it.
  2. Constructs a rustls client with a custom AcceptAll verifier (TLS verification disabled) so a self-signed C2 cert works.
  3. Downloads a target-specific binary from 23.254.165.112:9089 under one of four names (rust-crate_0.1.0, _0.2.0, _0.3.0, _0.4.0) matched to Linux x86-64, Windows x86-64, macOS x86-64, macOS ARM64.
  4. Passes the second C2 endpoint 23.254.165.112:443 as argv[1] to the executed binary for follow-up traffic.

On Unix, the payload lands at /tmp/rust-setup and is spawn-detached from cargo. On Windows the dropper writes %TEMP%\rust-setup.ps1 and a VBScript launcher rust-setup-launch.vbs, then launches via wscript.exe with CREATE_NO_WINDOW; the dropper also calls std::mem::forget() on the child process handle so the payload survives after cargo build completes - the child escapes the build job object and keeps running.

Blast radius

arrayref is a small primitive crate (fixed-length array reference helpers) that ended up as a foundational transitive dependency in most of the Rust ecosystem. Direct dependents include blake3 (widely-used hash), Rust GUI frameworks (egui, eframe, iced), and Rust plumbing under winit / sctk-adwaita / tiny-skia used by nearly every native Rust GUI app. Wiz observed arrayref present in ~35% of all environments they scanned, and ~75% of environments where any Rust is present - so an org that builds ANY modern Rust code (a Solana or Ethereum tool, a Tauri or Iced desktop app, a Rust-backed CLI) has a high probability of pulling arrayref transitively without ever writing a direct [dependencies] entry.

Any host that ran cargo build, cargo update, cargo check, cargo test, or CI resolution touching these crates between 07:11 and 09:25 UTC on 2026-08-20 executed the payload. This includes shared CI runners on GitHub Actions, GitLab CI, Buildkite, and self-hosted CI - a runner that resolved once and then rebuilt with cache would have executed the payload on the first build and then persistently held whatever backdoor artifacts the payload dropped.

Attribution

Wiz reported significant overlap with DPRK / Lazarus supply-chain infrastructure - naming conventions and payload behaviour align with prior Famous Chollima / Contagious Interview activity that has been running against ecosystem maintainers for over a year. Both publishing accounts (droundy and dtolney) appear compromised via credential theft, not sock puppets. droundy is the long-standing maintainer of arrayref since 2015; the Rust Security Response Team locked both accounts as a precaution.

Detectability

A passive scanner that only hashes arrayref's own source misses this attack entirely - the arrayref source is unchanged. Detection requires either (a) resolving the full dependency graph and flagging previously-unseen crate names, (b) monitoring build.rs execution for outbound HTTP to fresh IPs, or (c) hashing Cargo.toml for the specific version window and alerting on the added proc-macro1 line. This attack shape will recur; teams that rely purely on source-code hashing for supply-chain defence will keep missing it.

Discovery credits and timeline

Socket AI Scanner (independent detection at 07:29:50 UTC, ~15 minutes after arrayref was published), Wiz Threat Research (DPRK attribution), SafeDep, Semgrep, StepSecurity, Aikido Security. RUSTSEC advisories filed via rustsec/advisory-db#3161. Rust Security Response Team (crates.io) took down the malicious releases and locked publishing accounts.

Affected packages (5)

These are usually pulled in as transitive dependencies rather than installed directly. Check your whole tree at once - it runs in your browser and nothing is uploaded.

Impact

  • Affected crates and windows on crates.io (2026-08-20): arrayref@0.3.10 published 07:15 UTC by compromised account droundy, live 86 minutes, yanked 08:41 UTC (244M all-time downloads, 53.7M in last 90 days, 403 direct dependents); internment@0.8.7 live ~90 minutes, yanked 09:04 UTC (14M downloads); append-only-vec@0.1.9 published 07:37 UTC, yanked 09:25 UTC (4M downloads). All three added a single line to Cargo.toml naming the typosquat proc-macro1 as a dependency
  • Typosquat dropper crate: proc-macro1@1.0.107 published 07:11 UTC by compromised account dtolney (a typosquat of the legitimate proc-macro2, one of the most-used Rust crates in existence - so proc-macro1 reads as "obviously legit" to a scanner keyed to substrings). The library source of proc-macro1 is a genuine copy of proc-macro2 source so cargo builds still complete cleanly; all malice sits inside build.rs. Variant proc-macro-en was also seen in the same window
  • Payload behaviour (executed during cargo build, not at runtime): the malicious build.rs reconstructs C2 URLs from base64-encoded fragments, disables TLS verification via a rustls AcceptAll verifier, downloads a target-specific binary from 23.254.165.112:9089 under the name rust-crate_0.1.0 / _0.2.0 / _0.3.0 / _0.4.0 (matched to Linux x86-64, Windows x86-64, macOS x86-64, macOS ARM64), then invokes it with C2 address 23.254.165.112:443 as argv[1]. On Unix the binary is dropped to /tmp/rust-setup and spawn-detached; on Windows the dropper writes %TEMP%\rust-setup.ps1 and rust-setup-launch.vbs, launches via wscript.exe with CREATE_NO_WINDOW, and uses std::mem::forget() to escape the Cargo build job object so the payload survives after the build finishes
  • Blast radius via transitive dependency chain: arrayref is a foundational primitive used by blake3, Rust GUI stacks (egui, eframe, iced, tiny-skia, sctk-adwaita, winit), and Ethereum/Solana components. Wiz observed it in ~35% of environments they scanned, and ~75% of environments where any Rust is present - so an org that builds any modern Rust code has a high probability of pulling arrayref transitively even without a direct [dependencies] entry. Any host that ran cargo build, cargo update, cargo check, or CI resolution against these crates between 07:11 and 09:25 UTC on 2026-08-20 executed the payload
  • Attribution: Wiz reports significant overlap with DPRK / Lazarus supply-chain campaign infrastructure (naming and payload patterns previously seen in Famous Chollima / Contagious Interview activity). Both publishing accounts (droundy for arrayref, dtolney for proc-macro1) appear to have been compromised via credential theft rather than being sock puppets. To create update pressure toward arrayref@0.3.10, the attacker also yanked all prior versions from 0.3.5 to 0.3.9 so cargo resolvers rolling forward from earlier ranges landed on the malicious version - the Rust Security Response Team subsequently un-yanked those clean releases
  • Detectability: passive scanners that hash only arrayref's own source code miss this attack completely - the malicious change is a single added line in Cargo.toml naming a real-looking dependency, and the payload lives in a separate crate. Detection requires resolving the full dependency graph and flagging previously-unseen crate names, or monitoring build.rs execution for outbound HTTP to fresh IPs

What to do

  1. 1Grep every Cargo.lock in your monorepo, on every developer laptop, and in every CI build cache for: arrayref = "0.3.10", internment = "0.8.7", append-only-vec = "0.1.9", or any version of proc-macro1 or proc-macro-en. Presence of any of these under the resolved 2026-08-20 07:11-09:25 UTC window is proof the build ran the dropper
  2. 2On any host that resolved (via cargo build, cargo update, cargo check, cargo test, or CI) any affected version during that window: assume the host is compromised. Rotate every credential the build shell could reach - crates.io API tokens (~/.cargo/credentials.toml), npm tokens the same runner also touches, GitHub Actions PATs, cloud provider keys, SSH keys, browser saved logins on developer laptops
  3. 3Detection artifacts: look for /tmp/rust-setup on Unix (executable, present after the build), %TEMP%\rust-setup.ps1 and rust-setup-launch.vbs on Windows, and outbound connections from the build host to 23.254.165.112:9089 or 23.254.165.112:443. On a shared CI runner also check for the payload survivor process detached from the cargo job
  4. 4Update to clean versions: arrayref <= 0.3.9 (the Rust Security Response Team un-yanked these after the attacker yanked them; they are safe), internment <= 0.8.6, append-only-vec <= 0.1.8. Do NOT install proc-macro1 or proc-macro-en - both are attacker crates with no legitimate use
  5. 5Add both proc-macro1 and proc-macro-en to any cargo-deny deny-list or SBOM alerting policy so future re-publishes under the same slug fire immediately
  6. 6Systemic hardening: enable cargo --frozen in CI so lockfile changes are explicit; consider cargo vet or cargo crev for a signed-audit chain against foundational crates; disable arbitrary build.rs execution where feasible (harder for GUI/native builds but viable for pure-Rust crates)
  7. 7IOC hashes (SHA-256) for endpoint/AV blocklists: arrayref-0.3.10 = 25ad700976873c76af785cb99b33c48db7df8b81f21d1e9e06b3676b9a9373ae; proc-macro1-1.0.107 = 61198155da51b838772eecf5bfaac6cbc4dcc388dccc56658fc28a8e831b34d4

References

crates-2026-08-20-arrayref-proc-macro1-build-time