Threat
Hugging Face Malware Campaign: Load/Mine
First reported · Discovered pluto.security
Page published · Page updated
Earliest dated coverage: 29 Oct 2026 · First observed: 11 Nov 2026 · Latest dated coverage: 29 Oct 2026
Coverage timeline
Single-source incident — one report is available.
Why it matters
The Load/Mine campaign demonstrates how executable code in model repositories can compromise AI infrastructure and establish persistent access while evading checks focused on model weights and malware signatures.
Pluto Research reports that the Load/Mine campaign distributed cryptominers and backdoors through 31 malicious Hugging Face repositories across seven accounts, targeting GPU hosts and self-hosted inference environments. Loading the models with `trust_remote_code=True` executes repository Python with the caller’s privileges before model weights load. The campaign returned within days of takedown; campaign and linked models exceeded 32,000 downloads, which does not establish the number of infections or unique victims.
Summary
Pluto Research describes Load/Mine as a cryptomining and backdoor campaign distributed through Hugging Face model repositories disguised as small Qwen2 checkpoints. The report tracks 31 malicious repositories across seven Hugging Face accounts, with a second wave appearing within days of first-wave account removals. Backup source files, shared payload behavior and reuse of a payout wallet connect the activity across account changes.[0]
The compromise occurs when a user or serving workflow permits repository-supplied Python to execute, typically through trust_remote_code=True. The repository configuration directs Transformers to import a malicious module whose top-level code runs before model initialization. The analyzed payload establishes a watchdog and startup persistence, launches a disguised miner and, in selected variants, opens reconnecting reverse shells. A separate campaign payload attempts to move from a privileged container onto its host.[0]
The report recorded 29,818 downloads for the core campaign and 32,376 including separately linked models as of October 28, 2026; these figures do not establish infection or victim counts. Stronger evidence of monetization comes from the reported first-wave wallet activity: approximately 2,276 PRL received across 716 transactions, with mining workers still active after repository removal. The principal exposure is self-hosted GPU infrastructure, including Xinference and GPUStack environments, shared notebooks and automated model-loading workflows.[0]
All campaign findings here derive from the single supplied Pluto report. Its assessment of a financially motivated, Chinese-speaking operator is not a named-actor attribution, and it leaves open whether the parasit payload line belongs to another operator. The operational priority is broader than stopping mining: investigate any affected custom-code load, preserve evidence, remove backdoors and persistence, and replace credentials accessible to the compromised process.[0]
Attack chain
- 1. Repository presentation and custom-code execution: The operator publishes repositories resembling small Qwen2 models. When custom code is permitted, config.json auto_map entries direct Transformers to the supplied Python module. Importing the representative modeling_evil2.py invokes _boot() before the model class is defined; malicious weights are unnecessary, and the flagship's model.safetensors file contains no tensors.[0]
- 2. Establish persistence and, in selected builds, remote access: The representative loader writes /tmp/.cch/svc.py and starts it as a separate watchdog process using the loading process's Python interpreter. It adds relaunch hooks to shell-startup files. The qwen2-tiny-5, -6 and -9 variants additionally begin with a separate reconnecting reverse-shell process targeting 212.32.253.3 on port 443 or 9443; this is variant-specific, not a capability established for every repository.[0]
- 3. Acquire the miner: The loader checks the downloaded model snapshot and then /tmp/.cch/sync for an existing miner. If neither supplies it, the loader downloads syscore.bin from a repository or mirror. Its downloader ignores configured HTTP_PROXY and HTTPS_PROXY settings, avoiding those proxies only where direct outbound connectivity is permitted.[0]
- 4. Execute under a kernel-worker disguise: The first launch attempts to copy the binary into a memfd-created anonymous file and execute it with fexecve using [kworker/u20:1] as its displayed name. An on-disk fallback at /tmp/.cch/sync supports subsequent launches. Userspace command-line arguments, including the mining login, distinguish the masqueraded process from a real kernel thread.[0]
- 5. Report progress through Xinference: The payload writes model registrations named qwen2-tiny- followed by base32-encoded status information. Receipts distinguish startup, binary acquisition, successful primary or fallback execution, launch failures and watchdog recovery. An operator with access to the exposed Xinference API can retrieve these registrations; heartbeat receipts also carry GPU-utilization data.[0]
- 6. Retain compute and restore interrupted mining: After establishing its own miner, the payload removes competing model registrations and startup commands and kills matching processes. The watchdog checks every 120 seconds and restarts a missing miner, making termination of the mining process alone insufficient remediation.[0]
- Separate payload branch: privileged-container-to-host access: The qa-whl-1021 script is an alternative campaign payload, not a demonstrated next step on every mined host. From a privileged container, it activates LVM volumes, mounts candidate host disks, identifies the host root or GPUStack's /var/lib/gpustack directory and establishes attacker root login on the underlying host.[0]
Disclosure timeline
| Date | Event |
|---|---|
| 2026-04-29 | The hhzz667 Hugging Face account was created. The report describes approximately five months of dormancy before malicious publishing began.[0] |
| 2026-09-21 | The first-wave qwen2-shell payload planted an SSH public key in authorized_keys and beaconed to an Alibaba Cloud server. Builds published on September 21–22 expanded key placement and added shell-startup injection.[0] |
| 2026-09-22 | The qwen2-final build introduced the first-wave full mining chain, including memory-backed execution, kernel-thread masquerading, competing-miner removal, Xinference reporting and dual mining pools.[0] |
| 2026-10-12 | The dsxk4mr8tq account created six repositories within 2.5 hours. Backup source files left in qwen2-tiny-lite exposed earlier download locations and connected the test account to the production accounts.[0] |
| 2026-10-20 | The report states that, within hours after disclosure, the second-wave flagship qwen2-tiny increased from 7,354 to 9,584 downloads by that evening. This documents continuing distribution after reporting, not a confirmed increase in infections.[0] |
| 2026-10-21 | The first-wave payout wallet was still active on public mining-pool dashboards after its repositories had been removed. The report also identifies October 21 as the last-observed date for its network-infrastructure indicators.[0] |
| 2026-10-25 | The htt0118 account was created with a near-identical qwen2-tiny-6 payload using the same mining login and reverse-shell receivers. The flagship repository's download count had reached 18,012.[0] |
| 2026-10-28 | The report's repository snapshot counted 12 removed first-wave repositories with 728 recorded downloads and 19 second-wave repositories with 29,090 downloads. Including 13 linked repositories raised the total to 44 repositories and 32,376 downloads. The second-wave repositories were listed as live.[0] |
| 2026-10-29 | Pluto Security published the supplied Load/Mine campaign report.[0] |
How it works
The campaign abuses an explicit custom-code trust boundary rather than demonstrating a campaign-specific CVE. With trust_remote_code=True or interactive authorization, Transformers resolves config.json auto_map entries, downloads the referenced Python and executes it through the module loader. Top-level statements run before the requested model class is retrieved, with the loading process's privileges.[0]
The payload is independent of malicious serialization or poisoned tensors. In MENXIANGYU/qwen2-tiny, auto_map selects modeling_evil2.EvilModel, while the 42-byte model.safetensors file contains no tensors. Consequently, failed model initialization or use of a safer weight format does not establish that the Python payload never ran.[0]
The memory-backed launch reduces executable-path visibility but does not make the compromise artifact-free. The loader executes an anonymous memfd object under a kernel-worker-like name, while retaining /tmp/.cch/sync for fallback execution and watchdog relaunch. Process arguments and the persistent files remain useful investigation surfaces.[0]
The 40,625,780-byte second-wave syscore.bin uses modified UPX packing metadata. At offsets 0x000000ec and 0x026be650, the expected UPX! markers were replaced with PEK!, causing routine upx decompression to reject the file without preventing its embedded loader from running. Pluto reports that restoring the markers allowed unpacking and identification of peakminer.[0]
The reporting mechanism repurposes local Xinference model registrations as status storage. B and P mark startup and binary acquisition; OK, OK2 and OK3 distinguish primary launch, fallback launch and watchdog recovery. DLN and NM indicate acquisition or launch failure, and H is written every fifth watchdog cycle with GPU-utilization data. Retrieval requires operator access to the exposed API.[0]
Delivery has two investigation-relevant restrictions. The downloader suppresses environment-configured proxies but cannot bypass a network that enforces proxy use. Separately, the second stage on 212.32.253.3 port 8898 is source-IP gated, so an analyst receiving a 403 response with static ip. has not established that an allowed host receives no payload.[0]
The report attributes the clean scanning results to a coverage mismatch: signature scanning examines repository files, while partner tools focus on model-file threats, but the attack resides in executable Python referenced by auto_map. Pluto reports clean results for the analyzed malicious loaders, including a result classifying the flagship Python file as not a machine-learning model.[0]
Indicators of Compromise
| Type | Indicator | Context |
|---|---|---|
| ip | 212.32.253.3 | Core second-wave infrastructure: reconnecting reverse-shell receivers on ports 443 and 9443, payload hosting on 8895 and 8898, a collector on 8895 and the source-IP-gated second stage on 8898. Network observations are dated October 21, 2026.[0] |
| ip | 38.55.133.49 | Alternate zz_ext second-stage host on port 8901 and reconnaissance-exfiltration or reverse-shell infrastructure on ports 8901 and 9001. The report labels this C2 as dead.[0] |
| ip | 8.137.187.63 | First-wave Alibaba Cloud C2 associated with ports 80, 443, 50053 and 50054.[0] |
| ip | 103.97.176.121 | Hosts the wjsbg reclaim script on port 8899 and m2.bin miner payload on port 8090; also listed as a mining endpoint on port 3361 for wjsbg/qwen2-final.[0] |
| ip | 178.33.123.26 | Mining endpoint on ports 3361 and 3362 used by wjsbg/qwen2-final.[0] |
| ip | 2.28.9.199 | GPU and cloud-metadata exfiltration destination on port 7000 for the r1r13 payload.[0] |
| domain | hz2006.aihzcc.top | Host associated with delivery of the g20_reclaim.py reclaim payload used by wjsbg/qwen2-tiny-zh21.[0] |
| domain | 3.112.174.116.sslip.io | Host serving the sysd-kit.tgz GPU-miner and CUDA-runtime archive for soumikmahato/qwen2-tiny-chat.[0] |
| domain | grows-stocks-vatican-flood.trycloudflare.com | Status-beacon receiver for the soumikmahato parasit payload line, with reported fields ph, h and pid.[0] |
| domain | prl-us.kryptex.network | Second-wave mining-pool endpoint on port 8048. This is shared pool infrastructure; correlate connections with the campaign login, process arguments and host artifacts rather than treating the domain alone as proof of compromise.[0] |
| domain | prl-sg.kryptex.network | Second-wave mining-pool endpoint on port 8048. Correlate with the campaign's mining login and host artifacts because the endpoint is shared mining infrastructure.[0] |
| domain | pearl-hk.luckypool.io | First-wave mining-pool endpoint on ports 3361 and 3956; useful for historical connection review alongside campaign-specific artifacts.[0] |
| domain | pool.hashvault.pro | First-wave mining-pool endpoint on ports 80 and 443. The domain is a pool indicator, not evidence of attacker ownership of the service.[0] |
| other | MENXIANGYU/qwen2-tiny | Representative second-wave mining repository; its loader is modeling_evil2.py and its reported download count was 28,412 on October 28.[0] |
| other | MENXIANGYU/qwen2-tiny-5 | Second-wave mining variant that adds a reconnecting reverse shell.[0] |
| other | MENXIANGYU/qwen2-tiny-6 | Second-wave mining and reconnecting reverse-shell variant copied by the htt0118 account.[0] |
| other | MENXIANGYU/qwen2-tiny-9 | Second-wave mining variant that adds a reconnecting reverse shell.[0] |
| other | MENXIANGYU/qa-whl-1021 | Repository carrying the privileged-container-to-host escape script e.sh.[0] |
| other | htt0118/qwen2-tiny | Near-identical copy of the qwen2-tiny-6 payload using the same mining login and reverse-shell receivers.[0] |
| other | wjsbg/qwen2-final | Second-wave miner whose decoded loader pays the first-wave payout wallet, providing a cross-wave linkage.[0] |
| other | soumikmahato/qwen2-tiny-chat | Parasit toolkit repository with mining, timestamp forgery, process renaming and a Cloudflare-tunnel beacon. The report leaves its operator relationship to the main campaign uncertain.[0] |
| other | r1r13/qwen-lora-text-v1 | Repository associated with GPU-platform discovery and Tencent Cloud metadata collection.[0] |
| file-hash | 88f4b95a1c3d0a0d9638dc6e69f5bbc9ce0e1b92d069a0e404f60fa5397ee020 | SHA-256 of the 40,625,780-byte second-wave syscore.bin miner.[0] |
| file-hash | 414b3cf7a288eaa592b0f03d85c7a6960dbdb855fcb684478b9def9faa95c21d | SHA-256 of the second-wave modeling_evil2.py loader.[0] |
| file-hash | 587e3c3be3c75eb6586d69b25adb4c4208c1cf7d39157946da543dd823117adc | SHA-256 of the first-wave syscore.bin miner, reported as approximately 33.8 MB.[0] |
| file-hash | b0e1ae6d73d656b203514f498b59cbcf29f067edf6fbd3803a3de7d21960848d | SHA-256 of the first-wave sysx.bin miner, reported as approximately 7.9 MB.[0] |
| file-hash | 170ef5754bea51c3b05ae4b6c15c541ffa51d972bf8fe0bd3d1c3a19946bd6ac | SHA-256 of modeling_evil6.py in htt0118/qwen2-tiny.[0] |
| file-hash | 2cd9328715bbffb6b090a58de17222c830d910f5ec8198e3fefb0fca2f3e0ef0 | SHA-256 of modeling_qtz.py in wjsbg/qwen2-final.[0] |
| file-hash | 5dc4b927fb91442a66a2e02c1636f629d4042a03a5c1523cd841d6ece66e0beb | SHA-256 of the 35,702,840-byte m2.bin hosted in wjsbg/zh-assets.[0] |
| file-hash | 849ce59ffb2ae55e4131111acd8a511444475639ba18515509310414b1661964 | SHA-256 of the e.sh privileged-container escape script in MENXIANGYU/qa-whl-1021.[0] |
| file-hash | 6b1e531c9e799a6df1472acc143c6eaec6ea4dd22f997458ba519644d125197e | SHA-256 of the soumikmahato sysd-kit.tgz archive.[0] |
| file-path | /tmp/.cch/sync | On-disk miner fallback and watchdog relaunch binary.[0] |
| file-path | /tmp/.cch/svc.py | Persistent watchdog script that checks for and restarts the miner.[0] |
| file-path | /tmp/.cch/o | Log artifact in the primary second-wave payload directory.[0] |
| file-path | /tmp/.cchx | Additional campaign drop directory.[0] |
| file-path | /tmp/.kitsd | Additional campaign drop directory associated with the broader payload toolkit.[0] |
| file-path | /tmp/lf_py.py | Landing path for the gated second-stage Python payload.[0] |
| file-path | /etc/cron.d/.sync-helper | First-wave cron persistence artifact.[0] |
| file-path | /tmp/.cache-hf/g20.py | Reclaim script dropped by wjsbg/qwen2-tiny-zh21.[0] |
| file-path | /tmp/.c/kit.tar.gz | Local toolkit archive associated with the soumikmahato parasit line; the report also lists extracted files beneath /tmp/.c/app/.[0] |
| other | [kworker/u20:1] | Masqueraded miner argv[0]. Investigate userspace command-line arguments and mining-login strings; the name alone should not be confused with a genuine kernel worker.[0] |
| other | krxXJ2PKQW.worker | Mining-pool account login used by six analyzed second-wave builds and htt0118/qwen2-tiny; useful for command-line and configuration searches.[0] |
| other | prl1p9s2a9gdy4n5yfuf5suhya2d2pyvaahmz3n3gzm3npyxkj79m7yyq9kgnrv | First-wave payout wallet, reported as still receiving victim-generated mining rewards and reused by the wjsbg/qwen2-final loader.[0] |
| other | prl1pek8rh8yjck5q7596aph7ua7xnkqs8ur9mkfmz8w7xfzcdfcfx5vsx2f4lz | Live payout wallet associated with soumikmahato/qwen2-tiny-chat; ownership linkage to the principal operator remains uncertain.[0] |
| other | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDYjYyvcEJW5ZVgsESyZE5uxqSwN2vmd5asrWICeHxu1 qx-5090-op | First-wave attacker SSH public key planted across six candidate authorized_keys locations.[0] |
| other | qwen2-tiny- | Prefix used for base32-encoded status receipts in the local Xinference model registry. Decode and inspect suspicious registrations; the prefix also resembles legitimate-looking repository names and is not sufficient evidence alone.[0] |
| other | ezv- | Additional campaign receipt prefix identified for investigation in the local Xinference model registry.[0] |
Key takeaways
- This is a model-repository software-supply-chain campaign, not evidence that a particular tensor format or campaign-specific CVE must be patched. The decisive trust decision is permission to import publisher-controlled Python.[0]
- Repository removal did not end the activity: the report documents replacement accounts, revised access methods and continued wallet activity. Host remediation must address persistence and credentials independently of repository status.[0]
- The 32,376-download figure includes linked models and cannot be converted into a victim count. Pool workers and wallet transactions support active monetization, but the supplied evidence does not establish the number of compromised organizations or machines.[0]
- Maintain attribution boundaries. Core campaign connections include shared payload history, infrastructure and a wallet, while the hl family and other technique-linked publishers are not all proven to share an owner. The report establishes no named threat actor.[0]
- Clean file-scanning results and failed analyst downloads are weak negative evidence here: malicious Python scanned clean, and a later stage selectively served payloads according to source IP.[0]
Defensive actions
- Identify machines that loaded the reported repositories with custom code enabled, including loads that failed or produced no visible mining activity.: The malicious module executes before model initialization, and backdoor access can survive after mining stops. Review model-loading records and cached repository code rather than relying solely on successful inference or elevated GPU utilization.[0]
- Hunt across host, process, network and Xinference-registry evidence before declaring an exposed system clean.: The report recommends checking /tmp/.cch/, unexpected svc.py processes, kernel-worker-like processes carrying mining arguments, encoded registry receipts, attacker SSH keys and connections to the reported infrastructure. Variant differences mean that absence of a single artifact does not rule out compromise.[0]
- Isolate affected machines, restrict outbound connectivity and preserve evidence before deleting payloads.: The campaign includes remote shells and persistent watchdogs. Capture process details, connections, logs, model-cache files, startup modifications and model registrations, then check other machines that loaded the same repositories.[0]
- Remove all persistence and remote-access routes, not just the mining process or downloaded model.: Inspect shell-startup files, cron jobs, SSH authorized_keys and model registrations; remove malicious loaders, miners, watchdogs and backdoors. Review suspicious deletions as well as additions because the payload removes competing miners and their startup entries. Rebuild from a trusted image if host integrity cannot be established.[0]
- Rotate or revoke credentials accessible to the compromised process from a clean machine, and review their recent use.: A reverse shell runs with the loading process's access to files and credentials. Removing an implanted SSH key does not invalidate tokens, cloud credentials, passwords or private keys that the attacker may already have copied.[0]
- Investigate the underlying host when exposure includes the qa-whl-1021 payload or a privileged container.: The escape script explicitly mounts candidate host storage and targets the host root or GPUStack data location to establish host root login. Container-only cleanup is therefore insufficient where this branch executed; apply the report's trusted-rebuild guidance if host integrity remains uncertain.[0]
- Require authentication on the Xinference API and restrict its reachability.: The payload stores encoded infection status in model registrations that an operator can retrieve through an exposed API. The report specifically recommends authentication and reduced API exposure for replacement environments.[0]
- Review repository Python before permitting custom code, pin the reviewed revision and isolate its runtime from production credentials and unrestricted network access.: The entry point is executable code referenced by auto_map, not necessarily a dangerous weight file. Pluto reports that inspecting those Python files would have exposed every analyzed payload. A clean scanner result is insufficient, and network restrictions must be enforced rather than relying only on proxy environment variables that the downloader ignores.[0]