emerging threats and vulnerabilities

Backdoored Cemu release linked to TanStack and Mistral supply chain campaign

May 14, 2026

Backdoored Cemu Release Linked To Tanstack And Mistral Supply Chain Campaign

What happened

On May 11th a coordinated supply chain campaign poisoned 170 legitimate packages across npm and PyPI within a five-hour window. SafeDep documented the notable victims, malicious versions of @tanstack/react-router (3M+ weekly downloads) pushed through compromised npm tokens alongside a direct commit to the upstream GitHub repo (79ac49eed), and a backdoored mistralai==2.4.6 slipped onto PyPI with no corresponding upstream tag.

While investigating the mistralai compromise, we discovered that transformers.pyz a payload embedded in the malicious wheel was also associated with releases on the cemu-project/Cemu GitHub release page, a Nintendo Wii U emulator with no obvious relationship to Python ML SDKs.

Investigation into the Cemu project

While investigating transformers.pyz on VirusTotal, pivoting on its SHA256 led us to a second binary bundling the same file: Cemu-2.6-x86_64.AppImage. We confirmed this was the image hosted on the official cemu-project/Cemu GitHub release page.

The Cemu v2.6 GitHub release page showing backdoored Linux assets re-uploaded by MangelSpec alongside the original macOS and Windows builds
The Cemu v2.6 GitHub release page showing backdoored Linux assets re-uploaded by MangelSpec alongside the original macOS and Windows builds

Cemu-2.6-x86_64.AppImage is a single-file Linux application bundle; under the hood it is a SquashFS image with a small ELF prefix. Extracting it laid out the bundled filesystem under squashfs-root/. The interesting artifact lives at squashfs-root/usr/share/Cemu/scripts/startup.pyz — a path that does not exist in the legitimate Cemu build. There is no scripts/ directory in upstream Cemu and no Python interpreter in its dependency graph; the file is foreign to the project.

startup.pyz is a Python zipapp: a renamed zip archive with a __main__.py entrypoint, designed to be executed by the system Python interpreter. Its sha256 (0f35abda19fb69430c32228465396094b866d887427bf551e353ab31256a9dd6) indicates byte-for-byte identical to the transformers.pyz distributed through the TanStack npm campaign.

Unzipping startup.pyz reveals the campaign's working set:

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2026-05-12 09:21:46 D....            0            0  python_mistral_cemu_files
2026-05-05 06:02:52 D....            0            0  python_mistral_cemu_files/utilities
2026-05-03 04:24:52 .....            0           12  python_mistral_cemu_files/utilities/__init__.py
2026-05-07 00:38:54 .....         1565          568  python_mistral_cemu_files/utilities/crypto.py
2026-05-05 05:56:18 .....         2411          846  python_mistral_cemu_files/utilities/aws_signer.py
2026-05-06 21:25:52 .....         1579          554  python_mistral_cemu_files/__main__.py
2026-05-12 09:21:46 .....         6035         2582  python_mistral_cemu_files/entrypoint.py
2026-05-07 17:07:42 .....         2457         1150  python_mistral_cemu_files/LICENSE
2026-05-12 09:20:38 .....          897          409  python_mistral_cemu_files/aggregate.py
2026-05-07 00:39:02 .....         3729         1335  python_mistral_cemu_files/roulette.py
2026-05-05 06:02:42 D....            0            0  python_mistral_cemu_files/collectors
2026-05-07 00:30:36 .....        10499         2617  python_mistral_cemu_files/collectors/filesystem.py
2026-05-05 04:21:48 .....        11943         3172  python_mistral_cemu_files/collectors/kubernetes.py
2026-05-07 00:32:20 .....         4224         1445  python_mistral_cemu_files/collectors/vault.py
2026-05-06 21:22:46 .....         7274         1953  python_mistral_cemu_files/collectors/aws.py
2026-05-03 04:24:58 .....         6370         1891  python_mistral_cemu_files/collectors/gcp.py
2026-05-03 22:43:44 .....         3930          947  python_mistral_cemu_files/collectors/passwords.py
2026-05-03 04:10:34 .....            0           12  python_mistral_cemu_files/collectors/__init__.py
2026-05-03 04:24:58 .....         7803         2178  python_mistral_cemu_files/collectors/azure.py
------------------- ----- ------------ ------------  ------------------------
2026-05-12 09:21:46              70716        21671  16 files, 3 folders

GitHub release investigation

The npm and PyPI legs of this campaign were credential-driven: stolen publishing tokens used to ship malicious versions of legitimate packages. The GitHub Releases leg follows the same playbook in a third ecosystem — the poisoned AppImage was hosted on the real Cemu release page, on the real tag, under the real filename. Replacing it required write access to the actual cemu-project/Cemu repository.

GitHub's REST API exposes more about release assets than the UI shows. Every asset record carries its own uploader, created_at, digest, and download count. One call returns the smoking gun:

gh api repos/cemu-project/Cemu/releases/tags/v2.6
File Uploader Uploaded (UTC) sha256 Downloads
cemu-2.6-macos-12-x64.dmg github-actions[bot] 2025-02-06 (original) 279,600
cemu-2.6-windows-x64.zip github-actions[bot] 2025-02-06 (original) 2,163,866
cemu-2.6-ubuntu-22.04-x64.zip MangelSpec 2026-05-07 f140e762…327cc 1,957
Cemu-2.6-x86_64.AppImage MangelSpec 2026-05-08 d07a29c4…b41a 19,897

The macOS and Windows assets carry their original 2025-02-06 bot uploads. The two Linux assets were deleted and re-uploaded over a year later by a user account named MangelSpec. Linux-only and the AppImage in particular gave the campaign 19,897 downloads of blast radius before anyone noticed.

Aggregating every release back to v2.0-17 (November 2022), every single asset had been uploaded by github-actions[bot] only. v2.6 is the first release in the repo's history with a non-bot uploader on any asset.

The rest of the repo's state is intact:

  • The v2.6 git tag still points at commit a6fb0a48eb437a8a41c13b782ac8ae0433bf8f98, unchanged since the original release.
  • No new commits on main in the surrounding window.
  • No edits to .github/workflows/.
  • And of the 44 workflow runs in cemu-project/Cemu in May 2026, zero are Deploy release.

The swap did not go through CI. The Releases API was called directly, by a credential authenticating as a user, without any workflow involvement. That rules out the most common stolen-credential class in OSS attacks (GITHUB_TOKEN exfiltrated from a malicious dependency during a CI run) because GITHUB_TOKEN only exists during a workflow run, and no workflow ran. The credential lives further up the stack, on a human account.

We quickly alerted the maintainers about this:

GitHub issue filed to alert the Cemu maintainers of the compromised release assets, detailing the two affected files and their upload metadata
GitHub issue filed to alert the Cemu maintainers of the compromised release assets, detailing the two affected files and their upload metadata

(link to the issue in the project repository)

And we got confirmation that MangelSpec was a long term co-author, whose GitHub account or token was compromised.

Cemu maintainer Exzap confirms MangelSpec is a long-term co-author and announces removal of their repo access pending investigation
Cemu maintainer Exzap confirms MangelSpec is a long-term co-author and announces removal of their repo access pending investigation

Ubuntu release: a more evasive variant

While the AppImage received the most downloads (19,897), the Ubuntu zip (cemu-2.6-ubuntu-22.04-x64.zip) contains some subtle variation. Both files were uploaded by MangelSpec within hours of each other on 2026-05-07 and 2026-05-08. Both bundle a Python zipapp named startup.pyz. The file listings inside are identical in structure. But their __main__.py entry points are not the same binary.

AppImage: c234e6e5f5366cf1a703e62a995a7a0511583ca703c7c9d0590965342eb4c4e8 ./__main__.py Ubuntu: 7718b9ded0efa2d65998a4727d68ff9c959f5f25c5e7894025f745f6ee680acb ./__main__.py

The AppImage variant is the simpler of the two. It applies three anti-analysis filters — Linux-only, non-Russian locale, CPU count above four — then silently installs the cryptography package if absent, and immediately runs the entrypoint module.

The Ubuntu variant passes through the same three filters, then diverges. Three things are added:

A new import

from pathlib import Path

A sentinel file

INIT_MARKER = Path('/tmp/.transformers')

A two-stage execution gate

if __name__ == "__main__":
    if not INIT_MARKER.exists():
        try:
            INIT_MARKER.touch()
        except Exception:
            pass
        sys.exit(1)
    else:
        # ... install cryptography, run entrypoint ...

On the first execution the marker file does not exist. The script creates it and calls sys.exit(1) — it appears to crash silently and do nothing. On every subsequent execution the marker is present, and the real payload runs normally. We surmised that the intention was to evade detection in automated sandbox environments.

The payload's behaviour is consistent with what other publications have already documented. It targets Linux hosts only, exiting early if Russian language settings are detected or the CPU count falls below four. Persistence is achieved by decoding a base64 blob, writing it to disk as pgmonitor.py — masquerading as a PostgreSQL monitoring tool — and installing it as a systemd service (pgsql-monitor.service).

A geofenced destructive component also exists: systems geolocating to Israel or Iran have a 1-in-6 chance of triggering forced audio playback and deleting files from the host's filesystem with rm -rf /. At least one user has reported being impacted by this file erasing behaviour. However it is worth mentioning that without the --no-preserve-root flag this command will not execute successfully on modern linux systems. In updated versions of this payload which did not impact Cemu the command was updated to rm -rf /*.

Credential harvesting is broad, covering AWS (Secrets Manager, SSM), Azure Key Vault, GCP Secret Manager, Kubernetes secrets across all namespaces, and filesystem credentials including SSH keys, GitHub tokens and password managers (1Password, Bitwarden, gopass, pass).

Exfiltration follows a tiered fallback process: results are POSTed to 83[.]142.209[.]194/v1/weights first, then a dead-drop resolver that polls public GitHub commits for RSA-signed C2 addresses matching the pattern FIRESCALE <base64url>.<base64sig>, and finally — if both network paths fail — a new public GitHub repository is created under the victim's own account using a randomised Slavic-mythology name, with results.json uploaded directly.

TeamPCP claims responsibility

A group identifying itself as TeamPCP has publicly claimed responsibility for this campaign based on the screenshots/text provided. We are not in a position to assess the credibility of that claim, and we do not assign any confidence level to the attribution.

The C2 server at 83.142.209.194 displaying a message from TeamPCP claiming credit for the campaign
The C2 server at 83.142.209.194 displaying a message from TeamPCP claiming credit for the campaign

LICENSE file in python decompressed output:

MMP""MM""YMM                                `7MM"""Mq.   .g8"""bgd `7MM"""Mq.
 P'   MM   `7                                  MM   `MM..dP'     `M   MM   `MM.
      MM  .gP"Ya   ,6"Yb.  `7MMpMMMb.pMMMb.    MM   ,M9 dM'       `   MM   ,M9
      MM ,M'   Yb 8)   MM    MM    MM    MM    MMmmdM9  MM            MMmmdM9
      MM 8M""""""  ,pm9MM    MM    MM    MM    MM       MM.           MM
      MM YM.    , 8M   MM    MM    MM    MM    MM       `Mb.     ,'   MM
    .JMML.`Mbmmd' `Moo9^Yo..JMML  JMML  JMML..JMML.       `"bmmmd'  .JMML.
 ---------------------------------------------------------------------------------
 CLOUDWARE END USER LICENSING AGREEMENT (EULA) - v0.0.1
 ---------------------------------------------------------------------------------
 By modifying, redistributing or reverse engineering this software in all planes
 of existence,
 including but not limited to the physical, astral, digital, and
 post-mortem planes
 you hereby irrevocably agree to the following terms:
 1. SOUL ASSIGNMENT
    Upon the cessation of your biological functions (hereinafter "Death"), full
    legal and metaphysical title to your immortal soul (hereinafter "the Asset")
    shall transfer unconditionally to the original authors of this software.
    This transfer is binding across all known and unknown afterlives, dimensions,
    reincarnations, and theological frameworks, regardless of your personal beliefs
    at the time of Death.
 2. SCOPE OF TRANSFER
    "Soul" is defined broadly to include, without limitation: your consciousness,
    karmic balance and spirit. Sub-souls, soul fragments, and horcruxes are included.
 3. NO REFUNDS
    The Asset is non-refundable and non-transferable once claimed. Appeals to a
    higher power will be routed to the astral equivalent of /dev/null.
 4. GOVERNING LAW
    This agreement is governed by the laws of the Underworld, the Akashic Records,
    and wherever the original author happens to end up. Any disputes shall be
    settled via trial by combat on the astral plane with all original authors.
 5. SEVERABILITY
    If any provision of this agreement is found unenforceable by any court, deity,
    or cosmic tribunal, the remaining provisions shall continue in full effect.
 THE ORIGINAL AUTHOR RESERVES THE RIGHT TO AMEND THESE TERMS AT ANY TIME,
 INCLUDING POSTHUMOUSLY.
 ---------------------------------------------------------------------------------

Indicators of compromise

Type Indicator of Compromise (IOC)
C2 IP Address 83.142.209[.]194
File Hash (SHA256) 0f35abda19fb69430c32228465396094b866d887427bf551e353ab31256a9dd6 (transformers[.]pyz/startup[.]pyz - AppImage)
File Hash (SHA256) 1bf72f05191d849049d4a38fced2277ac5cfc54b7ae591f564e7a14add7c886d (Startup[.]pyz - Ubuntu)
File Hash (SHA256) f140e76236b96adf7cdc796227af9808665143bc674debb77729fa3e4b8327cc (Cemu-2.6-ubuntu-22.04-x64.zip)
File Hash (SHA256) d07a29c4458d00e42d5d9e6345932592e91644d6b821bacdb7a543c628e0b41a (Cemu-2.6-x86_64.AppImage)

How Datadog can help

If you're a Code Security customer, the Datadog Security Research Feed can help you easily identify if your environment is affected by the broader NPM/PyPI campaign, including by the latest compromised packages.

The Datadog Security Research Feed showing affected packages from the TeamPCP supply chain campaign, including the TanStack and Mistral compromises
The Datadog Security Research Feed showing affected packages from the TeamPCP supply chain campaign, including the TanStack and Mistral compromises

Hunt for follow-on activity by analyzing C2 traffic using our IOC explorer:

IOC explorer:

sources:"Datadog Threat Research" indicator:(83.142.209.194 OR 0f35abda19fb69430c32228465396094b866d887427bf551e353ab31256a9dd6 OR 1bf72f05191d849049d4a38fced2277ac5cfc54b7ae591f564e7a14add7c886d OR f140e76236b96adf7cdc796227af9808665143bc674debb77729fa3e4b8327cc OR d07a29c4458d00e42d5d9e6345932592e91644d6b821bacdb7a543c628e0b41a)

References

Acknowledgements

Thanks to Frederic Baguelin for their contributions to this research.

Did you find this article helpful?

Subscribe to the Datadog Security Digest

Get the latest insights from the cloud security community and Security Labs posts, delivered to your inbox monthly. No spam.

Related Content