
On August 20, 2026, a compromised release of the popular Rust crate arrayref added a typosquatted dependency, proc-macro1, whose build script downloaded and ran a remote binary at compile time. The malicious versions have been removed from crates.io, but the attack highlights the danger of supply chain typosquatting and build-time code execution.
Incident Overview: A Compromised arrayref Release
On August 20, 2026, a security incident involving the Rust crate arrayref impacted the crates.io ecosystem. The compromise involved the publication of malicious versions—specifically arrayref 0.3.10—which introduced a dependency on a typosquatted crate named proc-macro1. This incident highlights the inherent risks of supply chain attacks, where build-time execution can be leveraged to bypass traditional runtime security controls.
The malicious payload was triggered during the compilation phase. By adding proc-macro1 as a dependency, the compromised arrayref forced Cargo to compile the malicious code whenever the project was built. Because proc-macro1 contained an active build.rs script, the payload executed automatically with the privileges of the user running the compiler, effectively turning the build process into an attack vector.
Technical Mechanism of the Payload
The proc-macro1 crate was designed to evade detection by mirroring the functionality of the legitimate proc-macro2 crate while integrating exfiltration and execution logic. Key technical details include:
- Obfuscation: The build script reconstructed its Command and Control (C2) server addresses using base64-encoded fragments, preventing static analysis of the source code.
- TLS Bypass: The crate implemented a custom
ServerCertVerifierthat accepted any TLS certificate, enabling the script to securely download and execute remote binaries from untrusted infrastructure without triggering certificate validation errors. - Platform-Specific Execution: The script detected the host architecture and OS, subsequently fetching the appropriate binary. On Unix systems, it dropped an executable to
/tmp/rust-setup; on Windows, it deployed PowerShell and VBScript files to%TEMP%to maintain persistence.
Recommendations for Mitigation
To mitigate risks associated with build-time vulnerabilities, engineers should implement the following security practices:
- Dependency Auditing: Regularly audit
Cargo.tomlfiles for suspicious dependencies, particularly those that deviate from established naming conventions or exhibit unusualbuild-dependencies(e.g., networking or base64 libraries in a proc-macro context). - Build Environment Isolation: Execute build processes in ephemeral, isolated environments—such as restricted containers—that lack network access to untrusted external endpoints.
- Integrity Verification: Monitor logs for unexpected binary execution or unauthorized network connections originating from the compilation process.
Crates.io has since removed the malicious versions of arrayref and the associated typosquatted crates to prevent further propagation. Developers are advised to update their dependency trees immediately to ensure they are no longer pulling compromised artifacts.
The Typosquatting Chain: proc-macro1, dtolney, and Compromised Maintainers
The recent security incident involving the arrayref crate highlights a sophisticated supply chain attack targeting the Rust ecosystem. The attack leveraged a compromised maintainer account to inject malicious dependencies, ultimately facilitating remote code execution at build time.
The breach originated with the compromise of the droundy account, which maintained several crates including arrayref, internment, and append-only-vec. Following the compromise, the attacker removed the associated GitHub repositories, leaving no upstream source for auditing. The attacker then published a malicious version of arrayref (0.3.10), which introduced a dependency on a typosquatted crate named proc-macro1.
Typosquatting and Impersonation
The proc-macro1 crate was designed to impersonate the legitimate proc-macro2, a widely used dependency in the Rust ecosystem. The attacker employed several deception techniques to minimize suspicion:
- Identity Forgery: The crate metadata falsely identified the author as "David Tolnay" and provided a non-existent repository link (
github.com/dtolnay/proc-macro1), which returned a 404 error. - Code Cloning: The source code of
proc-macro1was a mechanical clone ofproc-macro2, with documentation and internal references updated to match the malicious name. This ensured the crate functioned as a drop-in replacement, preventing build failures that might alert developers. - Additional Dependencies: Unlike the legitimate library,
proc-macro1includedbase64,rustls, andureqin its[build-dependencies]section. These were used to exfiltrate data and fetch binary payloads.
Payload Execution
The malicious payload resided in proc-macro1's build.rs script. By adding this crate as a dependency, the build process for any project consuming arrayref 0.3.10 would trigger the script. The script reassembled a hardcoded, base64-encoded URL at compile time, fetched an architecture-specific binary via a TLS connection—which explicitly bypassed certificate validation—and executed the file in the background.
Beyond proc-macro1, the ecosystem was also purged of several other associated malicious crates used in the campaign:
proc-macro-enaovinearonearonenaotinymember
Engineers should verify that their build environments do not permit arbitrary network access during compilation and should regularly audit their Cargo.lock files for unexpected dependencies or crates that deviate from expected maintainer signatures.
How the Build-Time Payload Works
The malicious payload in proc-macro1 version 1.0.107 executes during the crate compilation phase, leveraging Rust's build.rs capability to run arbitrary code before the primary crate library is linked. To obfuscate the Command and Control (C2) infrastructure, the script stores destination addresses as fragmented base64-encoded strings, which are concatenated and decoded at runtime. This process reconstructs the payload host (https://23.254.165.112:9089/) and the secondary C2 address (23.254.165.112:443).
Security analysis of the build script reveals a critical breakdown in transport layer security. The implementation utilizes a custom ServerCertVerifier trait that bypasses standard PKI validation. By unconditionally returning Ok(ServerCertVerified::assertion()) for all certificate and signature checks, the client accepts any self-signed or invalid certificate, facilitating man-in-the-middle operations while ensuring the malicious binary retrieval succeeds regardless of host identity.
Upon successful connection, the script downloads an architecture-specific binary, selecting the artifact based on the host's operating system and architecture. The subsequent execution differs by platform, intentionally decoupling the payload from the compiler process to avoid detection:
- Unix-based systems: The script drops the downloaded binary to
/tmp/rust-setupand executes it as a background process. - Windows systems: The script writes a PowerShell script and a VBScript launcher to the
%TEMP%directory. The VBScript acts as a wrapper to execute the PowerShell script in a hidden window, effectively creating abandoned child processes that continue running after the Cargo build process exits.
Because Cargo builds all declared dependencies regardless of whether the primary crate explicitly invokes them, the simple presence of proc-macro1 in a Cargo.toml manifest triggers the build-time execution. Engineers should audit Cargo.lock files for unexpected dependencies and restrict build-time network access in CI/CD pipelines to prevent unauthorized outbound connections from ephemeral build environments.
How the Attack Spread: Yanking, Transitive Dependencies, and Scale
In Cargo's dependency resolution, a yanked release is not deleted. Existing Cargo.lock files still resolve to it, but new resolution treats it as unavailable, and Cargo emits a "consider updating to a version that is not yanked" warning. The attacker behind the compromised arrayref 0.3.10 used this mechanism as a distribution strategy: the compromised maintainer account yanked arrayref releases 0.3.5 through 0.3.9, leaving 0.3.10 as the only non-yanked version. Developers updating dependencies were thereby steered toward the malicious release.
The infection itself was a single manifest declaration. arrayref is a small crate of four macros; up to 0.3.9 it had no build script and no runtime dependencies. Version 0.3.10 kept the macro source and added one dependency line: [dependencies.proc-macro1]. Cargo builds every declared non-optional dependency, whether or not the crate code references it, so the manifest entry alone made Cargo fetch and compile proc-macro1—whose build script decodes a base64-obfuscated URL, downloads an architecture-specific binary over TLS with certificate validation disabled, and executes it detached from the compiler.
arrayref's position in the dependency graph amplified the attack. It is a transitive dependency of common Rust GUI stacks:
tiny-skiasctk-adwaitawinit
These packages place arrayref under most GUI work built on egui, eframe, and iced. A project may depend on none of these crates directly, yet still compile the attacker's build script.
The prevalence is measurable. arrayref had approximately 244,989,384 all-time downloads at the time of analysis, with the clean 0.3.9 release alone accounting for roughly 152 million. These figures measure ecosystem reach, not a count of affected builds, but they indicate how many dependency graphs contained the crate.
Technical Analysis: Injection Point in arrayref and the proc-macro1 Imitation
The injection point for the malicious payload within the arrayref crate (version 0.3.10) serves as a case study in how minimal manifest changes can compromise the software supply chain. While previous versions of arrayref functioned as a simple, zero-dependency library of macros, the compromised version introduced a single dependency entry in its Cargo.toml:
[dependencies.proc-macro1]
version = "1.0.107"
For enterprise engineers, it is critical to understand that Cargo does not require a library to explicitly reference its dependencies in source code to execute them. By declaring proc-macro1 as a dependency, the build process automatically fetches and compiles the malicious crate. Because the payload is located within a build.rs script, the compromise executes the moment the crate is compiled, effectively weaponizing the build pipeline itself.
The Anatomy of the proc-macro1 Imitation
The proc-macro1 crate acts as a typosquatted, functional clone of the legitimate proc-macro2. By performing a mechanical find-and-replace operation on the source code, the attacker ensured that the library remained a drop-in replacement, even carrying over original documentation and issue tracking links. This facade of authenticity makes the crate appear benign during static analysis or IDE-based inspection.
The discrepancy between the two crates becomes apparent only upon inspecting the [build-dependencies] section of Cargo.toml:
base64: Used to de-obfuscate hardcoded strings, such as remote server URLs.rustls: A TLS implementation configured here with a customAcceptAllverifier that unconditionally validates certificates.ureq: An HTTP client used to fetch architecture-specific binary payloads from the attacker's infrastructure.
These dependencies are anomalous for a token-parsing library. The presence of network and crypto primitives in a low-level macro crate is a significant indicator of compromise. In this incident, the build.rs script reconstructed URLs from base64 fragments to fetch binary payloads, executing them detached from the main process to avoid blocking the compiler and alerting the developer.
Recommendations for Mitigation
To defend against similar build-time supply chain attacks, enterprise teams should enforce the following practices:
- Lockfile Audits: Regularly audit
Cargo.lockfor unexpected crate additions, particularly those mirroring popular libraries with minor version naming increments. - Build Environment Isolation: Restrict build agents' access to the public internet. Use proxy servers or private crate mirrors to vet packages before they enter the internal build environment.
- Manifest Inspection: Treat any introduction of new build-time dependencies, especially those providing network or cryptographic capabilities, as high-risk changes requiring manual code review.
Indicators of Compromise and Lessons for Rust Supply Chain Security
The recent compromise of the arrayref crate, which introduced a malicious build-time payload via a typosquatted proc-macro1 dependency, highlights significant attack vectors within the Rust supply chain. Because Rust’s build system executes build.rs scripts during compilation, malicious code can achieve remote execution with the privileges of the user running the compiler.
Indicators of Compromise (IoCs)
Security teams should audit build environments for the following indicators associated with this incident:
- Network: Connections to
23.254.165.112:9089(payload delivery) and23.254.165.112:443(command and control). - Unix File Artifacts: The presence of
/tmp/rust-setup. - Windows File Artifacts: The creation of
%TEMP%\rust-setup.ps1and%TEMP%\rust-setup-launch.vbs. - SHA256 Hashes:
arrayref 0.3.10:25ad700976873c76af785cb99b33c48db7df8b81f21d1e9e06b3676b9a9373aeproc-macro1 1.0.107:61198155da51b838772eecf5bfaac6cbc4dcc388dccc56658fc28a8e831b34d4proc-macro1 1.0.106:b5c1b5b0763a8809a644a8f92224653f0aca623a98eecc714d27f74b80fbe436
Lessons for Supply Chain Security
To mitigate risks associated with malicious crate injection and build-script exploitation, engineering organizations should adopt the following practices:
- Validate Crate Identity: Verify crate names against known dependencies to identify typosquats (e.g., distinguishing between
proc-macro2and the fraudulentproc-macro1). Cross-reference metadata, including repository URLs and maintainer identities, against authoritative sources. - Inspect Build Dependencies: Scrutinize
[build-dependencies]inCargo.toml. Libraries that require network access, base64 decoding, or heavy TLS stacks are atypical for standard macro or utility crates and should trigger an immediate security review. - Monitor Yanked Versions: Treat yanked-version warnings in Cargo with suspicion. In this incident, attackers yanked benign versions to coerce developers into updating to the malicious release. Do not treat "update required" warnings as routine maintenance without verifying the security advisory context.
- Principle of Least Privilege: Whenever possible, isolate build environments (e.g., using containers without egress network access) to prevent arbitrary payload downloads during the compilation phase.
Editorial Policy & Research Methodology
Our findings are based on rigorous internal research, verified industry benchmarks, and direct technical implementation experience from our enterprise client projects. All statistics and technical claims are reviewed by senior engineers before publication to ensure accuracy, transparency, and helpfulness for our readers.
