A malware campaign on Hugging Face used one documented loading option, trust_remote_code, to turn other people’s GPUs into crypto miners. Pluto Security tracked 31 malicious model repositories across seven Hugging Face accounts, published in two waves since September 2026. With linked models, they recorded more than 32,000 downloads. Pluto stresses that this counts downloads, not confirmed infections.
The second wave appeared within days of the first takedown. Our own check of the Hub on 30 October found 18 of the 19 second-wave repositories disabled, while one copy uploaded on 25 October was still reachable. This post walks through the numbers, then what ML and platform teams should change.
Where the 32,000 Downloads Came From
Pluto’s counts split cleanly by wave. The first wave of 12 repositories recorded 728 downloads before removal. The second wave of 19 repositories had 29,090 downloads by 28 October. Thirteen linked models, which share techniques but not proven ownership, add 2,558 more.
One repository did almost all the work. A model posing as a tiny Qwen2 test checkpoint accounts for 97.7% of second-wave downloads, according to Pluto. Its count rose from 7,354 at disclosure to 28,412 on 28 October. On 30 October the Hub API listed it at 29,095, now disabled.
Downloads are not victims, since pipelines, mirrors and researchers all pull files. Still, the money shows real infections. Pluto says the first-wave wallet received about 2,276 PRL on-chain, worth roughly 5,000 US dollars. It also watched nine notebook-style GPU workers join that wallet within 90 seconds of each other.
Why One Flag Runs Someone Else’s Python
Some models need Python code that the Transformers library does not ship. The repository’s config.json names that code in an auto_map entry. When a user loads the model with trust_remote_code=True, Transformers downloads the file and imports it. Python runs a module’s top-level statements on import, so any call placed there runs before the model exists.
That is why the weights did not matter. Pluto found the flagship’s model.safetensors file held no tensors and was just 42 bytes. The payload sat in a Python file next to it. In effect, the repository was a malicious package wearing a model card, and the loader ran it with the caller’s privileges.
This is documented behavior, not a bug. The Transformers security policy asks users to “please always verify the content of the modeling files when using this argument.” In practice, Pluto notes, model cards and tutorials copy the flag, and an informed opt-in becomes a checkbox. The result is a classic supply chain attack: the attacker poisons something you import.
Which Machines the Payload Was Built For
The code targeted self-hosted inference servers, especially Xinference and GPUStack. Pluto also names rented GPU machines, shared research notebooks and CI jobs that pull small test models. It describes three parts in the main second-wave build:
- A watchdog script in /tmp/.cch/ with hooks in shell startup files, so it comes back at the next login.
- A miner launched from memory with memfd_create and named [kworker/u20:1] to look like a kernel thread.
- Status reports written into the local Xinference registry as fake model names.
The memory launch is a fileless malware trick, though a fallback copy stays on disk. Three builds also opened reverse shells. One later script, published 8.5 hours after Pluto’s disclosure email, tries to escape a privileged container and gain root on the host. It looks for GPUStack’s data folder, which points to a second target platform.
Where Scanning and Takedowns Fell Short
Hugging Face does scan uploads. Its documentation says it runs every repository file smaller than 2 GB through a malware scanner, and partner tools from Protect AI and JFrog check model formats. Pluto reports that none of the 12 first-wave repositories was flagged. The flagship Python file came back clean from every scanner that ran, and JFrog’s tool said it was not a machine-learning model.
The gap is simple. Signature scanners look for known malware, and model scanners look at weight formats. A plain Python file named by auto_map is neither. Hugging Face’s own pickle scanning page warns that its checks are “not 100% foolproof” and that safety checks are the user’s job.
- Hub takedownYes18 of 19 second-wave repositories show as disabled in the Hugging Face API, and the flagship page gives a Content Policy reason.2026-10-30
- Live copyYesA copy uploaded on 25 October by a newer account was still reachable with 85 downloads. Its file list includes syscore.bin, the miner file name Pluto documents.2026-10-30
- X discussionYes25 posts about the campaign in five languages between 29 Oct 12:00 and 30 Oct 09:36 UTC, in a capped sample of 41 posts.2026-10-30
- Brandefense BlackholeYesA Russian-language forum ran a translated news summary of the campaign on 29 October. This is relayed coverage and is not verified further.2026-10-29
Attention is spreading beyond security news. The SANS Internet Storm Center covered the campaign in its 30 October Stormcast. Pluto attributes the operation to a financially motivated, Chinese-speaking operator. It cites Simplified Chinese code comments, an Alibaba Cloud server and the hours when the operator was active.
How to Vet a Model Before You Load It
The flag is the decision point, so treat it like installing a new dependency. The attack fits the MITRE technique for compromise of the software supply chain: the attacker tampers with software before the user receives it. Pluto says the first check alone would have caught every payload in this campaign; the others limit the damage.
- Read the code auto_map namesOpen every .py file that config.json points to. Model code should define layers, not import subprocess, socket or ctypes.
- Pin the reviewed revisionPass revision= with the commit hash you reviewed, as the Transformers docs advise, so a later push cannot change the code.
- Prefer safetensors weightsSafetensors stores tensors without pickle code. It does not protect you from Python files loaded with trust_remote_code.
- Load new models in a sandboxUse limited privileges, restricted outbound network and no production secrets.
- Watch GPU use and cloud billsPluto notes that a busier GPU or a higher bill is often the first sign.
Then confirm your serving stack honors these controls. Before version 2.12.0, Xinference passed trust_remote_code=True in six loaders with no setting to turn it off (CVE-2026-76841). In vLLM 0.22.1 before 0.28.0, the server did not pass a revision pin to some tokenizer and config loads for two model types (CVE-2026-100653).
Older flaws show the same pattern. CVE-2026-22807 let vLLM load auto_map code without checking the flag at all. CVE-2026-4372 let Transformers before 5.3.0 fetch and run code from a crafted config.json through a standard from_pretrained call. Our DevOps security lessons from October’s attacks make the same point for build machines.
If Your Team Loaded One of These Models
Pluto advises checking the machine even if loading failed or GPU use looks normal, because the payload runs before the model starts. Its report lists the repositories, wallets and servers to match. Then work in this order:
- Look for /tmp/.cch/, unexpected svc.py processes and a [kworker/u20:1] process with mining arguments. Check Xinference registrations for odd qwen2-tiny- or ezv- names.
- Isolate the host. Save process lists, connections, logs and the model cache before you delete anything.
- Review shell startup files, cron jobs and SSH authorized_keys. These are the persistence points, and killing the miner does not remove them.
- From a clean machine, rotate the tokens, cloud keys and SSH keys the process could reach.
Our secrets rotation checklist gives an order for step four. The malicious Claude Code extension playbook shows the same cleanup pattern for a poisoned developer tool. Rebuild from a trusted image if you cannot prove the host is clean.
How We Counted
Download and wallet figures come from Pluto Security’s report of 29 October 2026, with counts as of 28 October. We queried the public Hugging Face model API for the 19 second-wave repositories at 10:14 UTC on 30 October. We read only the disabled flag, the download count and the file list. We did not download or run any model file.
The X figures come from a capped sample of 41 posts across three searches, of which 25 discussed the campaign. The Brandefense Blackhole result comes from a keyword search of forum and Telegram posts since 15 September. All counts are snapshots and will change.
