The models that mined on the machines that loaded them
Fake Qwen2 models on Hugging Face ran crypto miners on the machines that loaded them, and the Hub's scans passed them. How it worked, and how to load safely.

Thirty-one repositories on Hugging Face posed as tiny Qwen2 test models, and loading one with remote code allowed started a hidden crypto miner on the loading machine, in several builds with a way back in. The mining ran on the machines that loaded them, among them rented GPUs, research clusters, notebooks, CI jobs and self-hosted inference servers; the Hub stored the code, and its scanners called the payload files safe.
Pluto Security, which found the campaign and named it Load/Mine, published its report on 29 October.
The repositories and linked models recorded 32,376 downloads ↗, a count Pluto stresses is of downloads, with no infection confirmed, and the operator's first wallet has received about $5,000. The GPU time and the cloud bills fell on whoever loaded the models, along with backdoors that outlast the miner.
The 30‑second version
- →What: 31 malicious repositories across seven
Hugging Face accounts, plus a ModelScope mirror, published in two waves from 21 September as small Qwen2 test checkpoints. - →How: config.json names a Python file; loading the model with trust_remote_code=True imports it, and its top-level code runs before any weights load.
- →What ran: a hidden GPU miner for the cryptocurrency Pearl, kept alive by a watchdog and startup hooks, with planted SSH keys or reverse shells.
- →Where: on the machines that loaded the models. The Hub stored the loader code and, in several repositories, the miner binary.
- →The scanners: no flag on the first wave, and every scanner that ran called the flagship safe;
JFrog's said the payload was "not a machine-learning model". - →Now: the second wave stayed up eight days after
Pluto's report, and one copy was still listed on 1 November. Hugging Face has published no statement that we could find.
How a model file runs a program
Most models on the Hub use architectures the Transformers library already knows, so a download brings weights and settings. A model that needs code the library lacks carries an auto_map entry in its config.json naming a Python file, which Transformers imports when the caller passes trust_remote_code=True. Importing a Python file runs its top-level statements, so whatever sits beside the class definition runs as the model loads, with the caller's permissions, as
Pluto ↗ sets out.
The flag is an opt-in, and the library's security policy ↗ asks users to "always verify the content of the modeling files". In practice, Pluto writes, tutorials copy the flag and it becomes a checkbox, and tools have set it for users: vLLM 0.10.1 up to 0.14.0 loaded auto_map code without checking it (CVE-2026-22807 ↗), and
Pillar Security found
Unsloth Studio running repository code when a user merely selected a model, fixed in version 2026.6.9 (CSO Online ↗).
The weights format is a separate question. Safetensors keeps code out of the weights file, and does that job. The campaign's flagship, MENXIANGYU/qwen2-tiny, had a 42-byte safetensors file with no tensors in it; the payload sat beside it in modeling_evil2.py. A safe weights file says nothing about the Python next to it.
Two waves in six weeks
The first account, hhzz667, was created on 29 April and sat unused until 21 September, when it published a 37-line SSH backdoor; by the next day the code was a 305-line miner that runs from memory. On 12 October a throwaway account posted six copies, and
Pluto's scanner, which reads the Python files that auto_map names, caught it. Backup files left behind pointed to the production accounts on
Hugging Face and ModelScope, and both platforms removed them after Pluto's report (Pluto ↗).
The second wave's flagship went up on 14 October, according to the Hub's records, and 19 repositories followed under five new accounts, with reverse shells in place of the SSH key. Pluto reported them on 20 October, with the flagship at 7,354 downloads. Eight and a half hours later the same account published a script that escapes a privileged container to take root on the host, aimed at GPUStack servers. By 28 October the flagship had 28,412 downloads and the wave was still live.
| First wave | Second wave | |
|---|---|---|
| Active | 21 September to 12 October | From 14 October; partly listed on 31 October |
| Accounts | Two on Hugging Face, one on ModelScope | Five on Hugging Face |
| Repositories | 12 on Hugging Face | 19 |
| Downloads | 728, before removal | 29,090 by 28 October, 28,412 of them for one model |
| Way back in | An SSH key planted in six places | Reverse shells, and a container escape script |
| Scanner flags | No flag on any repository | The flagship scanned clean |
| After the report | Accounts removed | Live eight days later; flagship disabled by 29 October |
By 29 October Hugging Face had disabled the flagship, and BleepingComputer confirmed that a copy uploaded on 25 October by a new account, htt0118, carried the same miner and a reverse shell (BleepingComputer ↗). When we checked the Hub on 31 October and again on 1 November, that copy was still listed, beside six more created on 26 and 27 October with the same layout: a weights file a few bytes long and a config.json pointing to a class named EvilModel. Pluto's report does not cover those six, and we have not run them.
Where the mining ran
Hugging Face's servers stored and served the files; the mining happened on the machines that loaded them.
Pluto says the payloads were written for GPU hosts running self-hosted inference servers, Xinference and GPUStack in particular, and for rented GPU machines, shared research notebooks and CI jobs that pull small test models without anyone reading the code. Pluto describes no access to Hugging Face's own systems, which separates Load/Mine from July, when OpenAI's models under test ran code inside Hugging Face's infrastructure (our account of that incident).
The coin fits the machines: Pearl, launched in April, is mined with large matrix multiplications on Nvidia GPUs, the arithmetic of AI models (Hashrate Index ↗). The first-wave wallet had received about 2,276 PRL, roughly $5,000, and was still being paid after its repositories were gone. While Pluto watched, nine workers joined within 90 seconds, named like machines on a shared notebook cluster, and stopped about six minutes later, which Pluto reads as an administrator killing the jobs.
For the person who loaded the model, Pluto writes, nothing looks wrong: no prompt, no error, a process named like a kernel worker. The first sign is usually a busier GPU or a larger cloud bill. The longer cost is access: startup hooks, a watchdog that restarts the miner every 120 seconds, and SSH keys, reverse shells or a container escape. GPU servers and research clusters hold credentials and data worth more than the compute, and Pluto found other publishers using the same entry point for credential theft.
What the scanners checked
Hugging Face runs every file under 2 GB through the ClamAV antivirus at each commit, lists the imports inside pickle files, and shows results from two partner scanners, Protect AI's Guardian and
JFrog's, which look for exploits and malicious behaviour in model files (malware scanning ↗, pickle scanning ↗, Protect AI ↗, JFrog ↗).
| Check | What it looks for | On the flagship's Python file |
|---|---|---|
| Antivirus (ClamAV) | Known malware signatures in every file under 2 GB | Clean |
| Pickle scanning | Imports a pickle file would run when loaded | Nothing to inspect: the campaign used no pickle files |
| Partner scanners | Exploits and malicious behaviour in model files | Every scanner that ran called it safe; JFrog's called it "not a machine-learning model" |
"The scanners look at the wrong files,"
Pluto concludes. A plain Python file named by auto_map matches no known malware signature and contains no serialized model, so it passes both kinds of check. Hugging Face is open about the limits: its pickle documentation calls the checks "not 100% foolproof" ↗ and leaves the judgement to the user.
The scanners grew up around an earlier problem. In February 2024 JFrog found about 100 models whose weight files, mostly PyTorch and Keras, ran code when loaded (JFrog ↗), and in February 2025
ReversingLabs found two broken pickle files that slipped past the Hub's scanner (ReversingLabs ↗). Load/Mine leaves the weights alone.
Who is behind it
Pluto assesses a financially motivated, Chinese-speaking operator or small group, citing Simplified Chinese comments, command servers on Alibaba Cloud, targeting that assumes Xinference and Tencent Cloud, and activity late at night China time. One second-wave line, with Indonesian-language comments and a different build system, may be a second operator. As far as we could find, no platform or authority has named anyone.
How to load a model safely
- →Leave remote code off by default. Qwen2 has been built into Transformers ↗ since January 2024, so a Qwen2 checkpoint that asks for trust_remote_code=True is a warning sign in itself.
- →Read config.json first. An auto_map entry names the Python files that will run; read them, and any other .py file. A model definition describes layers, and calls to subprocess, socket, ctypes or raw IP addresses are what
Pluto's scanner looks for. - →Pin the revision you reviewed. Pass the commit hash as revision, as the Transformers documentation ↗ recommends, so a later push cannot change the code you load.
- →Check the tools that load models for you. Run vLLM 0.14.0 or later and
Unsloth 2026.6.9 or later. - →Load unknown models somewhere disposable. No production secrets, SSH keys or cloud credentials, a non-root user and no privileged mode; the escape script needed a privileged container.
- →Control egress at the network. The payload's downloader ignores the proxy settings in its environment, so only an enforced list of allowed destinations holds.
- →Watch GPU use against the job schedule. A GPU busier than its jobs explain is the first sign Pluto describes.
- →Require sign-in on inference servers. The payload reported its progress through Xinference's model registry; Xinference has required authentication by default since version 3.0 (Xinference ↗).
What to check this week
- →List every place trust_remote_code=True is set, in code, notebooks, CI jobs, fine-tuning and serving configs, with what each loads and whether a revision is pinned.
- →Compare the models your machines pulled since September, from proxy logs and model caches, with the repositories in
Pluto's indicators ↗. - →On GPU hosts, look for /tmp/.cch/, a process named [kworker/u20:1] carrying arguments, a svc.py process, new lines in .bashrc or .profile, unknown SSH keys and new cron entries.
- →If anything matches, isolate the machine, keep the evidence, rotate every credential it could reach from a clean machine, and rebuild it; killing the miner leaves the backdoors in place.
- →Check which destinations your GPU hosts can reach directly, and whether your inference servers answer callers who have not signed in.
What platforms could change
Pluto proposes a static check at upload for subprocess, socket, ctypes or raw-IP network calls in the files that auto_map names, which it says would have caught every payload in this campaign. In our view, two more changes would help: showing the code a model will run as prominently as its weights, and extending a takedown to near-identical copies such as htt0118's. For teams, Pluto's advice holds either way: treat model code like any other software dependency, and review it, pin it and isolate where it runs.
Rules that hold or break
Each of those checks can be written as a rule: no system loads remote model code except a reviewed, pinned revision; a GPU host reaches only the destinations on its list; an inference server answers only callers who have signed in. Written down, a rule can be re-proven on every release, holding or broken. That is the practice ExploitOps describes, and the rules page shows how rules are written. Load/Mine is now on the Record.
What is still unknown
- →How many machines ran the payload: CI jobs and caches can pull a model many times.
- →Whether the reverse shells and SSH keys were used to take credentials or data.
- →What drove the flagship from 7,354 to 28,412 downloads in the eight days after the report.
- →Whether the six newer htt0118 repositories carry the same payload.
- →Whether
Hugging Face will analyse the Python files that auto_map names; it has published no statement, and its status page ↗ lists no October incident. - →Whether one operator or two are behind the campaign.
We will update this piece when Hugging Face or
Pluto publishes more.
Sources
- →Pluto Security (Shaked Chen, Eitan Chassid, Yotam Perkal), Load/Mine: A Hugging Face Malware Campaign Returns After Takedown ↗, 29 October 2026
- →BleepingComputer (Ax Sharma), Hugging Face malware campaign returns to hijack GPUs for crypto mining ↗, 29 October 2026
- →Hugging Face, malware scanning ↗, pickle scanning ↗, Protect AI ↗ and JFrog ↗ documentation, and its status page ↗, checked 31 October 2026; the Hub's public model listing, checked 31 October 2026
- →Hugging Face Transformers, security policy ↗, loading custom models ↗ and Qwen2 ↗ documentation
- →NIST National Vulnerability Database, CVE-2026-22807 ↗ (vLLM), January 2026; CSO Online, Unsloth's model picker had a code-execution problem ↗, 30 September 2026
- →Hashrate Index, Pearl (PRL), an AI-compute cryptocurrency ↗, June 2026
- →JFrog, malicious Hugging Face models with a silent backdoor ↗, February 2024; ReversingLabs, malicious ML models discovered on Hugging Face ↗, February 2025
- →Xinference, authentication system ↗ documentation
The icons are each company's own published versions. They identify the companies named in public statements and research; MATT has no affiliation with Hugging Face, Pluto Security, JFrog, ReversingLabs, Pillar Security or Unsloth.
Questions
What is the Load/Mine campaign?
Load/Mine is Pluto Security's name for a campaign it reported on 29 October 2026: 31 malicious repositories across seven Hugging Face accounts, plus a ModelScope mirror, that posed as small Qwen2 test models. Loading one with trust_remote_code=True ran Python that installed a hidden GPU miner for the cryptocurrency Pearl and, in several versions, SSH keys or reverse shells. The repositories and linked models recorded 32,376 downloads.
Where did the Load/Mine miners run?
On the machines that loaded the models: GPU hosts running self-hosted inference servers such as Xinference and GPUStack, rented GPU machines, shared research notebooks and CI jobs, according to Pluto Security. The Hugging Face Hub stored the loader code and, in several repositories, the miner binary. Pluto describes no access to Hugging Face's own systems.
What does trust_remote_code do?
It lets the Transformers library import Python code that ships inside a model repository, for architectures the library does not include. The repository's config.json names the file through an auto_map entry, and importing it runs the file's top-level code with the caller's permissions. Hugging Face's security policy asks users to verify those files before setting the flag, and recommends pinning a revision.
Is a model in safetensors format safe to load?
The safetensors format keeps code out of the weights file, and it does that job. A repository can still carry Python files beside the weights, and those run if the model is loaded with trust_remote_code=True. The Load/Mine flagship had a 42-byte safetensors file with no tensors in it; the payload was in a Python file.
Why did Hugging Face's scanners miss it?
The Hub's antivirus matches known malware signatures, its pickle scanning covers pickle files, and its partner scanners look at model files. A plain Python file named by auto_map fits none of those checks. Pluto Security found no flag on any first-wave repository, and the second-wave flagship scanned clean while it collected 28,412 downloads.
How can I tell if a machine ran one of these models?
Pluto Security lists the indicators: a /tmp/.cch/ directory, a process named [kworker/u20:1] carrying mining arguments, an unexpected svc.py process, added lines in .bashrc or .profile, unknown SSH keys and cron entries, and connections to the listed servers and mining pools. If any match, isolate the machine, rotate the credentials it could reach and rebuild it.
How should teams load models from Hugging Face safely?
Leave trust_remote_code off by default, read the Python files a config.json names before enabling it, pin the reviewed commit as the revision, keep vLLM and Unsloth up to date, load unknown models in a disposable environment without secrets, enforce egress rules at the network, watch GPU use against scheduled jobs, and require sign-in on inference servers.

