- On August 20, 2026, attackers published malicious versions of three widely used Rust packages to
crates.io, the official Rust package registry. - On macOS, the payload is a remote access trojan (RAT) that collects cloud credentials, SSH keys, cryptocurrency wallet data, and browser profile information, then sends it to attacker-controlled infrastructure. It installs a launch agent to survive reboots and accepts follow-on commands from its operators.
- Once running, it decrypts its configuration, profiles the host, Reads the local browser stores, installs persistence via LaunchAgent, and beacons out.
- The packages were available for approximately two hours before removal. The attackers also withdrew the previous stable versions, which pushed automated dependency resolution toward the compromised releases. Any environment that compiled an affected project during that window should be treated as compromised.
This blog documents Iru's analysis of the native Apple Silicon ARM64 implant.
Four other research teams published on this campaign:
- Wiz published the campaign on August 20, disclosing the AES-128-GCM configuration key, the four-command tasking surface, and the DGA fallback.
- Socket's scanner independently flagged the malicious proc-macro1 crate, and their analysis of the recovered stage-2 payloads documented the backdoor's capabilities and the flaw in its command authentication.
- StepSecurity reconstructed the publish, yank, and takedown timeline and dissected the build-time dropper.
- The Rust Security Response Team's advisory covers the incident response and credits Nextron Systems with the initial report.
Our contribution is macOS-specific. From the arm64 sample we documented the on-disk characteristics of the Mach-O, the LaunchAgent persistence chain, and the collection target map, with observed and inferred paths distinguished.
Enrichment through Google Threat Intelligence surfaced additional indicators beyond those published, including the SHA-256 of the arm64 payload, which prior reporting published only the SHA-1.
The Sapphire Sleet association rests on two infrastructure links as reported by Wiz. The stage-2 C2 path /49890878was used in the Mastra campaign, which Microsoft attributed to Sapphire Sleet. The staging C2 hosts also present a self-signed TLS certificate with the issuer string WIN-A6QF8AHPQH1\Administrator@WIN-A6QF8AHPQH1, which appears on 23.254.167[.]13 in that same campaign.
Shared infrastructure has limits as evidence. DPRK clusters share hosting, builders, and deployment templates, so overlapping infrastructure may locate the tooling pipeline rather than the operator. The remaining indicators are weaker still: registry poisoning is common across DPRK clusters and is most associated with Contagious Interview, and asymmetric command authentication is a general operational security pattern.
We assess DPRK-nexus attribution at high confidence and Sapphire Sleet at low confidence.

Figure 1. Delivery chain on macOS. A cargo build resolves the poisoned crate, Cargo executes its build script, and the script fingerprints the host and fetches a matching ARM64 Mach-O from the delivery server. The payload is written to /tmp/rust-setup and executed with the C2 address as its launch argument. No user interaction beyond compiling the project is required
Implant Behavior
Rustbot functions as a remote access and information stealing tool. It decrypts an embedded configuration, screens for analysis environments, and communicates with C2 servers over an authenticated, encrypted channel featuring a domain-generation algorithm (DGA) fallback. Observed collection targets include host metadata, credential files (SSH, AWS, and Kubernetes), and browser data (login origins, usernames, and extension identifiers); dumping of stored browser passwords was not confirmed in the analyzed stage.
Build-time execution
Because Cargo runs build scripts automatically during cargo build, the payload fires the moment an affected project is compiled. StepSecurity's reconstruction of the dropper and its build-dependency toolkit, corroborating the original RustSec disclosure, is the primary external source for the behavior below. The deobfuscated behavior of proc-macro1's build.rs:
- Reconstructs a delivery URL from Base64 fragments,
https://23.254.165[.]112:9089/ - Installs a custom TLS verifier that accepts any certificate, so the direct-to-IP fetch over HTTPS never fails validation.
- Fingerprints the host, operating system and CPU architecture (macOS Darwin, arm64 vs. x86_64), and requests the matching second stage.
- Writes the downloaded binary to a transient path, /tmp/rust-setup on Unix hosts,
%TEMP%\rust-setup.ps1on Windows. - Executes it, passing the C2 beacon address as its first launch argument.
Runtime
The macOS payload is a Mach-O, arm64, roughly 3.07 MB. It’s self-signed with no valid Apple Developer Team ID. It imports only /usr/lib/libSystem.B.dylib and /usr/lib/libiconv.2.dylib. On the delivery server it is named rust-crate_0.4.0 / t_rust_arm64; on the victim it lands as /tmp/rust-setup.
Once running, it decrypts its configuration, profiles the host, reads the local browser stores, installs persistence via LaunchAgent, and beacons out.
Configuration decryption
The implant carries an encrypted configuration block, decrypted with AES-128-GCM. The key is not derived or fetched. The key, first published by Wiz, is the hardcoded ASCII string i am botking, 12 bytes null-padded to 16:
69 20 61 6d 20 62 6f 74 6b 69 6e 67 00 00 00 00
Decryption yielded the C2 endpoints, the beacon interval (1 min), and the implant's target list. Where the config named concrete filesystem paths, those are marked observed below; where it named applications or credential stores without paths, the standard macOS locations are given and marked inferred.
Host and browser profiling
The implant profiles the host and reads a targeted set of directories, weighted heavily toward non-human identities (tokens, keys) and local browser profiles. Socket's analysis of the recovered stage-2 payloads corroborates browser reconnaissance, noting that the analyzed stage inventories Chromium login origins, usernames, and extension IDs without decrypting stored passwords.
Collection targets, bucketed by asset value to the operator:
Cloud & infrastructure credentials (paths observed)
~/.aws/, AWS CLI keys and config~/.ssh/, SSH private keys andknown_hosts~/.kube/, Kubernetes cluster tokens and config
Cryptocurrency wallets (app names observed; paths are standard macOS locations, inferred)
- Exodus,
~/Library/Application Support/Exodus/ - Ledger Live,
~/Library/Application Support/Ledger Live/ - MetaMask, browser extension; data lives inside the Chromium profiles below (extension ID
nkbihfbeogaeaoehlefnkodbefgpgknn), not a standalone app dir - Coinbase Wallet, browser extension; likewise inside the browser profiles (extension ID
hnfanknocfeofbddgcijnmhnfnkdnaad)
Developer tooling (paths inferred)
- VS Code,
~/Library/Application Support/Code/,~/.vscode/ - IntelliJ IDEA,
~/Library/Application Support/JetBrains/ - Subversion,
~/.subversion/ - Docker,
~/.docker/(note:config.jsoncan hold registry auth tokens)
Messaging & collaboration (paths inferred)
- Slack,
~/Library/Application Support/Slack/ - Telegram,
~/Library/Application Support/Telegram Desktop/tdata/ - Signal,
~/Library/Application Support/Signal/ - Discord,
~/Library/Application Support/discord/(session tokens inLocal Storage/leveldb/)
Browser profiles (Chromium family) (paths observed)
- Google Chrome,
~/Library/Application Support/Google/Chrome/ - Brave,
~/Library/Application Support/BraveSoftware/Brave-Browser/ - Microsoft Edge,
~/Library/Application Support/Microsoft Edge/
The analyzed stage-2 payloads inventory browser login origins, usernames, and extension IDs but do not decrypt stored passwords.
C2 communication
The implant establishes an encrypted channel and beacons over HTTPS with a POST to the path /49890878, sending host information and collected data as Base64-encoded JSON. Inbound tasking was designed to authenticate tasking against authenticated against an embedded RSA-2048 key. However, as detailed below, that authentication is fatally undermined by an implementation flaw:
The developers intended an authenticated tasking system using asymmetric RSA cryptography. As designed, the operator would hold a private key off-host and sign each C2 task; the implant, carrying only the corresponding public key, would verify the signature and execute only tasks signed by the real operator. Done correctly, this makes the tasking channel exclusive. Someone who finds an infected host, or stands up a look-alike C2, cannot issue accepted commands without the private key, and cannot recover that key from the implant, because only the public half is in the binary. Combined with the DGA fallback, a correct implementation would have let the operators retain control of infected hosts even after the primary C2 was seized or sinkholed.
However, because the private key ships inside the implant, the tasking channel provided no exclusive operator authentication and the integrity of the C2 channel was neutralized.
The tasking surface itself is small, four commands:
| Command | Behavior |
|---|---|
| kill | Terminate the implant |
| minicfg | Reconfigure C2 endpoints / beacon interval |
| startup | Install persistence |
| runscript | Download and run a shell script (macOS/Linux) or PowerShell (Windows) |
runscript served a second-stage delivery channel.
If the primary C2 is unreachable, the implant falls back to a domain-generation algorithm producing algorithmic .comdomains, none were registered at the time of analysis.
Analysis
DPRK-nexus actors run supply chain and social engineering campaigns to fund the regime through cryptocurrency theft, and the collection targets here continue to fit that pattern.
The malicious releases were downloadable for about two hours before removal. Individual impact is unconfirmed, but the exposure surface is not small: arrayref appears in over a third of all environments and three-quarters of environments where Rust is present. The attackers also yanked the stable versions, which pushed resolvers toward the compromised ones. Any environment that compiled an affected project in that window should be treated as compromised.
Developer workstations and CI/CD runners carry the same credential-harvesting risk as production. They hold API keys, code-signing certificates, and cloud credentials, and those are what the implant's collection routines go after. The analyzed stage enumerates browser login stores without decrypting them, but runscript is an open channel for whatever the operator wants to run next, so the ceiling is set by the operator, not by the implant.
Expect more of this. Language-specific registries are an efficient conduit, as npm and PyPI have already shown, and registry maintenance actions like yanking are becoming part of the delivery method rather than incidental to it. The move toward network-delivered payloads over statically linked implants also continues, since it defeats static detection and complicates cluster-based analysis.
Indicators of compromise
Packages and accounts
| Type | Indicator | Notes |
|---|---|---|
| Package | arrayref@0.3.10, internment@0.8.7, append-only-vec@0.1.9 | Hijacked releases (removed). Last-safe: 0.3.9 / 0.8.6 / 0.1.8 |
| Package | proc-macro1, proc-macro-en, aovine, arone, aronenao, tinymember | Attacker-controlled dropper/typosquat crates |
| Account | droundy | Legitimate maintainer; account/credentials assessed compromised |
| Account | dtolney | Impersonation account that published proc-macro1 |
| rchaitm@gmail[.]com | Forged author metadata on the malicious dependency |
Network
| Type | Indicator | Notes |
|---|---|---|
| IP | 23.254.165[.]112:9089 | Stage-2 payload delivery host |
| IP | 23.254.165[.]112:443 | C2 passed to the payload as its launch argument |
| IP | 23.254.167[.]107:443 | Stage-2 C2 |
| IP | 23.254.167[.]216 | Stage-2 C2 seen on an infected host (third-party report, RustSec thread; not independently confirmed by Iru) |
| Range | 23.254.165.0/24, 23.254.167.0/24 | Hostwinds ranges; treat both. A single 23.254.164.0/23 misses the .167 C2 addresses |
| Domain | hwsrv-798836.hostwindsdns[.]com | Attacker infrastructure hostname |
| Path | POST /49890878 | Stage-2 C2 path |
| Certificate | WIN-A6QF8AHPQH1\Administrator@WIN-A6QF8AHPQH1 | Self-Signed TLS on Staging C2 host. |
Host artifacts
| Type | Indicator | Notes |
|---|---|---|
| File | /tmp/rust-setup | On-host stage-2 (macOS/Linux) |
| File | %TEMP%\rust-setup.ps1, %TEMP%\rust-setup-launch.vbs | On-host stage-2 (Windows) |
| String | i am botking | AES-128-GCM config key; highest-signal static string |
| Persistence | ~/Library/LaunchAgents/*.plist created by a build-descended process | Detection surface |
Payload hashes (SHA-256)
| Platform | SHA-256 |
|---|---|
| macOS arm64 | 74d3447e7cf99c99ea01a16332ec27432dfb0f491e10e67cd118065a60483306 (rust-crate_0.4.0) |
| macOS x86_64 | c9561a3b00a0fa38b7772675d987f84bd429c55cd024fc08a98245c2d1632848 (rust-crate_0.3.0) |
| Linux x86_64 | 408ef22050ffc5a67e005802809026b29f297a8019f8fda91a2afa8e877ba434 (rust-crate_0.1.0) |
| Windows | 492f2ab86f8d8911adc79c10ec1541704f5311d207d9d799b0d2a57fcc6a4391 (rust-crate_0.2.0) |
| Loader (build.rs) | cb7778eb6dda91028abf087eb7c3553f981a67e756769507d348e8c201805568 (shared by proc-macro1@1.0.107 and proc-macro-en@1.0.10) |
MITRE ATT&CK
| Technique | ID | Notes |
|---|---|---|
| Supply Chain Compromise: Software Dependencies and Development Tools | T1195.001 | Poisoned crate + added malicious build-time dependency |
| Command and Scripting Interpreter | T1059 | runscript executes shell (macOS/Linux) or PowerShell (Windows) |
| Ingress Tool Transfer | T1105 | build.rs fetches the second stage; runscript fetches further payloads |
| Persistence: Launch Agent | T1543.001 | macOS LaunchAgent created by a build-descended process |
| System Information Discovery | T1082 | Host profiling in every beacon |
| Software Discovery | T1518 | Enumerates installed applications |
| Unsecured Credentials: Credentials In Files | T1552.001 | Reads ~/.aws, ~/.ssh, and ~/.kube for keys and tokens |
| Data from Local System | T1005 | Bulk collection from the targeted directories |
| Browser Information Discovery | T1217 | Enumerates browser login stores via SQLite (reconnaissance, not confirmed dump) |
| Application Layer Protocol: Web Protocols | T1071.001 | HTTPS POST beacon |
| Encrypted Channel | T1573 | AES-128-GCM config; RSA-authenticated tasking |
| Data Encoding: Standard Encoding | T1132.001 | Base64-encoded JSON beacon; Base64 URL fragments in the dropper |
| Dynamic Resolution: Domain Generation Algorithms | T1568.002 | .com DGA fallback when primary C2 is unreachable |
| Exfiltration Over C2 Channel | T1041 | Collected host/profile data returned over the C2 channel |