LATEST THREAT

Three Attacks That Never Crossed A Boundary

A build that ran a payload. A registry that served a maintainer's own package. A command channel that terminated on a Microsoft IP. Every control in the way decided by who and where, and every one of them said yes.

The Week's Signal

At 07:15:00 UTC on August 20, 2026, version 0.3.10 of the Rust crate arrayref was published to crates.io by its legitimate maintainer's account. Eighty-six minutes later the Rust Security Response Team deleted it. Two sibling crates went the same way that morning: internment 0.8.7, published 07:34:07 and removed after ninety minutes, and append-only-vec 0.1.9, published 07:37:49 and removed after one hundred and seven minutes. The whole event opened and closed inside two hours and ten minutes.

The three releases had one thing in common. Each added a direct dependency on a crate called proc-macro1, a typosquat of the ubiquitous proc-macro2, published from an account impersonating a well-known Rust developer. proc-macro1 carried a build script. In Rust, a build script runs automatically during cargo build, which means the payload executed while a developer was compiling, not while they were installing. Wiz, which analysed the chain, describes what the script did: reconstruct its command-and-control addresses from Base64 fragments, disable TLS certificate validation, download a platform-specific second stage for x86_64 Linux, Windows and macOS as well as aarch64 macOS, write it to /tmp/rust-setup or %TEMP%\rust-setup.ps1, and run it.

The Rust team's own assessment of how the maintainer's account came to publish it is one careful sentence: “We do not believe the author of arrayref to be acting maliciously, but their computer or credentials are likely compromised.” The attacker also yanked the legitimate stable versions, so that dependency resolution would move projects onto the malicious release rather than back to a known-good one. Discovery credit goes to the Nextron Systems research team.

Seven days later, on August 27, the Australian Federal Police announced that two Western Australian men, aged 21 and 23, had been charged the previous day following a joint investigation with the FBI and WA Police Force. Police allege the pair were principal participants in a group known as TeamPCP. The AFP's own figures for the campaign are estimates and are worth quoting as such: malicious code that “potentially compromised more than 1000 organisations globally,” the theft of “more than 500,000 credentials,” exfiltration of “at least 300 gigabytes of data,” and global remediation costs in the hundreds of millions of dollars.

And on August 18, Ontinue's Cyber Defense Center published research on a Python implant it tracks internally as TWINLOOT, named after the SharePoint folder it uses. All of its primary command channels terminate in Microsoft IP space. The operator does hold attacker infrastructure, and Ontinue documents it in detail, but by default no attacker-controlled domain receives command traffic.

Diagram: Three Correct Allows — for each of the three incidents, the control that would normally stop it and the reason it did not fire: dependency allow-listing defeated because the crate was already allow-listed and the account was the real maintainer's; registry and maintainer trust defeated because the publishing credential was genuine; destination reputation and process lineage both defeated because the destination is Microsoft and the requesting process is the victim's own Edge

The Incident Ledger

The crates.io compromise. August 20, 2026, 07:15 to 09:25 UTC. Six attacker-created crates were removed in full alongside the three poisoned releases: proc-macro1, proc-macro-en, aovine, arone, aronenao and tinymember. The second stage, per Wiz, collected hostname, username and operating system details, enumerated installed applications, and read browser profile data from Chrome, Brave and Edge via SQLite. Wiz has published a correction on that last point that deserves carrying: the payload enumerates saved-login metadata and extension settings, and does not retrieve the encrypted credential material itself. On blast radius, one figure has been widely repeated and is worth separating from what happened. arrayref has roughly 245 million lifetime downloads across all its versions. The malicious version was downloaded 2,285 times, which RustSec notes was under ten percent of arrayref's normal traffic for that window, because most projects had an older version pinned in a lockfile. 2,285 downloads of the malicious version is the real figure, and it is a serious one, because a download is a build opportunity: where a fetch was followed by a compile, code ran with a developer's privileges on a machine that almost certainly holds credentials for something else. How many of the 2,285 became completed builds is not established by any source, and the distinction matters, since a download can be a mirror, a cache warm or a job that never compiled. On attribution, Wiz is precise and should be quoted precisely: the infrastructure “substantially overlaps with operations attributed to recent North Korean actors,” with the underlying DPRK attributions belonging to Microsoft and Mandiant for other campaigns. Wiz does not attribute this attack, and states no confidence level.

TeamPCP, and the worm that outlived it. Charged August 26, announced August 27, 2026. The AFP release names no victim organisations, states no offending timeframe, and does not use the name Shai-Hulud anywhere. What the AFP does say is that parallel AFP and FBI investigations began in April 2026, that nine charges were laid across the two men, and that the alleged conduct involved malicious code distributed at scale. The connection to Shai-Hulud is drawn by the trade press and by vendor research rather than by police. On the worm itself the research record is worth reading as a lineage rather than a single event. Unit 42 documented the original September 2025 npm wave, which stole a maintainer's credentials on install, identified other packages that maintainer controlled, injected itself into them, published the compromised versions, and exfiltrated harvested .npmrc tokens, GitHub personal access tokens and cloud credentials to a public GitHub repository. A November 2025 generation reached, per Unit 42, over 25,000 malicious repositories across roughly 350 unique users. The most recent wave, which Elastic Security Labs published on August 6 and named CHAINDROP, hit more than 400 npm packages through the keyv maintainer on August 4, 2026. As of September 3, 2026, no researcher has publicly reported a new wave since CHAINDROP. The worm's source code has been posted on cybercrime forums, and Truesec's assessment, published August 28, is that the arrests do not end the technique.

TWINLOOT. Activity observed late July 2026, published August 18, 2026. Ontinue's implant runs on a legitimately signed Python 3.12.9 embeddable runtime, obfuscated with PyArmor 9.2.5 Pro. Initial access was social engineering over Microsoft Teams: an external actor impersonating IT support persuaded a user to run a PowerShell command that fetched the staged payload. Three details separate this from ordinary commodity tooling. First, tasking arrives as file dead-drops in SharePoint Online reached through the Microsoft Graph API, and the SharePoint tenant is the attacker's own Azure tenant, not the victim's. Second, interactive operator access rides WebRTC data channels relayed by Microsoft Teams TURN servers, with TURN credentials harvested from Teams anonymous visitor endpoints that require no authentication at all. Third, the Graph traffic is driven through a headless instance of the victim's own Edge browser via the Chrome DevTools Protocol, so the requests issue from msedge.exe rather than from a Python process. On the host, credentials are captured through a pixel-faithful fake Windows lock screen populated with the user's real account details, and Ontinue notes it performs no password validation of any kind, so every attempt is captured whether correct or not. Persistence runs four ways, including forging an NTUSER.MAN mandatory profile hive offline, which achieves persistence without generating registry modification events and needs no administrative rights. Ontinue reports no victim count, no sectors, no geography and no actor attribution.

Why This One Matters

The convenient reading is that these are three supply chain stories, or three cloud abuse stories, and that the lesson is to watch your dependencies and your SaaS more closely. That reading survives about one layer of scrutiny.

Look instead at what each attack had to defeat, and a single shape appears. Every control standing in the way of these three made its decision on identity or reputation, and in each case the identity was real.

arrayref was already an approved dependency in every project it reached; a policy that allow-lists known crates allowed it, correctly, because it was the known crate. The publishing account belonged to the actual maintainer. Shai-Hulud's entire propagation model is the same insight generalised: it does not need to compromise a registry, only one maintainer, and then it inherits that maintainer's standing with every downstream consumer. TWINLOOT goes further and defeats two independent control classes at once. Destination reputation says the traffic is going to Microsoft, which is true. Process lineage says the requests came from Edge, which is also true. Neither control is broken and neither is misconfigured. Both are answering the question they were designed to answer, and the answer is genuinely yes.

The uncomfortable implication is that none of this is a gap that a better allow-list closes, because the allow-list is not malfunctioning. When identity is authentic and the destination is legitimate, the only property left that distinguishes the attack from ordinary activity is behaviour: a compiler process opening a network connection and writing an executable to a temp path, a mandatory profile hive appearing without a corresponding registry write, a browser process making Graph calls no human triggered.

That is a harder class of detection to build, and it is a considerably harder class of detection to be confident in without testing it. Which is the part of this that usually goes unexamined.

The Mechanism

The three chains share a structure worth naming precisely, because it determines where detection has to sit.

In the Rust case the compromise is T1195.001 — Supply Chain Compromise: Compromise Software Dependencies and Development Tools, delivered through a package whose name deliberately resembles a legitimate one, which is T1036.005 — Masquerading: Match Legitimate Resource Name or Location. Execution happens at build time rather than install time, and the second stage arrives via T1105 — Ingress Tool Transfer before running discovery: T1082 — System Information Discovery, T1518 — Software Discovery, and T1217 — Browser Information Discovery. The single most useful defensive fact in that chain is that cargo build made an outbound network connection, which is behaviour a build environment can be instrumented for and which no reputation control will ever flag.

Shai-Hulud's mechanism is the same initial technique with self-propagation bolted on, and its credential theft is T1552.001 — Unsecured Credentials: Credentials In Files against .npmrc and CI configuration, with exfiltration to a public code-hosting repository as T1567 — Exfiltration Over Web Service. Publishing stolen secrets to a public GitHub repo is, from a network monitoring standpoint, a developer pushing to GitHub.

TWINLOOT's chain begins with T1566.003 — Phishing: Spearphishing via Service over Teams and T1204.004 — User Execution: Malicious Copy and Paste, the pattern where the victim is talked into pasting a command themselves. Its command and control is T1102.002 — Web Service: Bidirectional Communication through SharePoint dead-drops and T1090.002 — Proxy: External Proxy through the Teams TURN relay. Credential capture through a spoofed lock screen is T1056.002 — Input Capture: GUI Input Capture, and the mandatory profile hive is T1112 — Modify Registry, performed offline specifically to avoid the telemetry that technique normally produces.

Diagram: What Each Control Actually Decided — a three-lane comparison, one lane per incident, each showing the trusted channel used, the control that evaluated it, and the verdict returned. Every verdict reads allowed, correctly. The middle column names the property each control decides on: dependency identity, publisher identity, destination reputation and process lineage. A full-width row below names the property none of them evaluated — the behaviour itself

Analytical ATT&CK Mapping

Table: Analytical ATT&CK Mapping, crates.io, Shai-Hulud and TWINLOOT — 13 observed MITRE ATT&CK techniques with basis labelled per row, spanning Initial Access, Stealth, Command and Control, Discovery, Credential Access, Exfiltration, Execution, Collection and Defense Impairment, with a footnote band on current tactic naming and deliberately omitted rows

The Prevention Gap

There is a version of a security programme that is doing everything the guidance asks and would still be blind to all three of these.

It maintains a software bill of materials. It pins dependencies. It runs an EDR agent with reputation-based blocking, a proxy with domain categorisation, and a SIEM correlating on process lineage. It reviews new third-party suppliers. Against these three incidents, each of those controls performs exactly as designed and returns a correct answer that happens to be the wrong outcome.

The SBOM lists arrayref, which is a legitimate crate that this project genuinely depends on. The dependency pin is why most projects were not affected, and it is the single most effective control in this issue, but it protects only the projects that had already pinned. Reputation blocking sees traffic to Microsoft. Process lineage sees Edge making web requests, and Edge making web requests is what Edge is for. None of these is a misconfiguration, which is precisely why a configuration review will not surface any of them.

What is missing is not a control. It is evidence about a specific behaviour. Does the build environment generate a detectable event when a compilation step opens an outbound connection and writes an executable. Does anything alert when a mandatory profile hive appears on a workstation without a matching registry write. Would a Graph API call sequence issued by a browser process that no user drove be distinguishable, in the data actually being collected, from one a user did drive.

Those are answerable questions. They are not answerable by inspecting a policy, because the policy is fine. They are answerable only by producing the behaviour and observing what the controls do with it.

What To Do Before The Next One

Three actions, ordered by how much exposure each removes relative to the effort it takes.

First, pin, and treat lockfile discipline as a security control rather than a build hygiene preference. The reason the malicious release reached 2,285 downloads rather than a far larger number is that most consumers had older versions resolved in a lockfile. That is the control that worked in this incident, and it worked passively. The corollary is that any build path which resolves dependencies afresh, a CI job that regenerates lockfiles, a container image built from a floating version, is running without the protection that saved everyone else. Those paths are worth enumerating deliberately, because they are usually invisible in a dependency review that reads the committed lockfile.

Second, instrument the build environment as an endpoint. Build-time execution is the through-line of both the Rust and Shai-Hulud incidents, and it lands somewhere that is often outside the monitoring estate. A CI runner that would raise an alert on a workstation raises nothing, because it is a workstation nobody classified as one. Outbound connections from compiler and package-manager processes, and writes of executable files to temp paths during a build, are narrow, low-volume signals, which makes them cheap to alert on once anything is watching.

Third, test whether the behaviour is detected, not whether the control is deployed. This is where an evidence gap becomes a structural one. ATLAS™ exists for this specific question: continuously validating whether controls actually detect, prevent and respond to a defined attacker behaviour, rather than establishing that an agent is installed and a rule exists. The behaviours in this issue, a build process reaching out and dropping an executable, a covert channel terminating on a legitimate cloud service, a persistence mechanism designed to avoid its own telemetry, are each either detected or not, and the answer is worth having before an incident produces it. The output is Sigma and Suricata rules generated from what the validation found, so a gap becomes a detection the SOC can deploy rather than a finding it inherits.

That covers detection. It does not cover the dependency graph itself, and it would be dishonest to imply otherwise: nothing in validation tells you whether a maintainer's laptop is compromised this morning. That risk is managed by pinning, by review, and by accepting that it cannot be driven to zero.

Which is the honest place to end. Every control in these three incidents worked. The attacks succeeded not by breaking trust decisions but by satisfying them, and a programme that only ever asks whether its controls are correctly configured will keep receiving the answer yes, right up until it matters.