On August 8, CheckPoint published a report on ten malicious Python packages in the Python Package Index (PyPI), the most popular Python repository among software developers. The malicious packages were intended to steal developers’ personal data and credentials.
Following this research, we used our internal automated system for monitoring open-source repositories and discovered two other malicious Python packages in the PyPI. They were masquerading as one of the most popular open-source packages named “requests“.
Timeline of uploaded packages:
|Package name||Version||Timestamp (UTC)|
The attacker used a description of the legitimate “requests” package in order to trick victims into installing a malicious one. The description contains faked statistics, as if the package was installed 230 million times in a month and has more than 48000 “stars” on GitHub. The project description also references the web pages of the original “requests” package, as well as the author’s email. All mentions of the legitimate package’s name have been replaced with the name of the malicious one.
After downloading the malicious packages, it becomes clear that the source code is nearly identical to the code of the legitimate “requests” package, except for one file: exception.py. In the malicious package, this script was last modified on July 30, exactly on the date of publication of the malicious package.
The malicious payload is a Base64-encoded Python script hidden in the “HTTPError” class. The script writes another Python one-liner script into a temporary file and then runs that file via the system.start() function. Then that one-liner script downloads the next-stage script from https://zerotwo-best-waifu[.]online/778112985743251/wap/enner/injector and executes it.
The next stage is a downloader obfuscated with a publicly available tool named Hyperion. Obfuscation is done using multiple techniques, such as renaming variables and library functions, adding mixed boolean-arithmetic expressions and junk code, and compressing the code chunks with the zlib library.
The downloader terminates if the OS name is not “nt” (Windows). It randomly selects one of the directories under C:\Users\<username>\AppData\Roaming or C:\Users\<username>\AppData\Local, generates a random eight-characters string consisting of the “bcdefghijklmnopqrstuvwxyz” characters and randomly picks one of extensions from the following list:
['.dll', '.png', '.jpg', '.gay', '.ink', '.url', '.jar', '.tmp', '.db', '.cfg']
Then the malware downloads the final stage payload from https://zerotwo-best-waifu[.]online/778112985743251/wap/shatlegay/stealer123365, saves it to the previously generated location and executes it.
In order to achieve persistence on the infected machine, the malware creates a registry value with name “Realtek HD Audio Universal Service” in the HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run Windows system registry branch.
The script searches for an existing executable in the %system32% directory, named SecurityHealthSystray.exe or the SystemSettingsAdminFlows.exe, adds a “&” character (to ensure sequential execution in a command-line string), and then adds the location of the Python interpreter with the location of the malicious script. It is worth noting that this method does not work properly, as the system starts only the first executable, and the persistence is not actually achieved.
C:\Windows\System32\<SecurityHealthSystray.exe | SystemSettingsAdminFlows.exe> & <Python interpreter path> <generated path for dropped final payload>
Final payload: W4SP Stealer
The final payload is a Trojan written in Python and obfuscated with the same obfuscator as the downloader. The malware is dubbed “W4SP Stealer” by its author in the code.
Upon launching, the stealer identifies the external IP address of the victim’s machine by making a GET request to https://api.ipify.org and installs two legitimate PyPI packages – “requests” and “pycryptodome” in order to send exfiltrated data to the operator and work with cryptography for decrypting cookies and passwords from browsers. Then the malware starts collecting Discord tokens, saved cookies and passwords from browsers in separate threads.
Collected passwords and cookies are stored in the files %TEMP%\wppassw.txt and %TEMP%\wpcook.txt in the following format:
UR1: <URL> | U53RN4M3: <USERNAME> | P455W0RD: <DECRYPTED_PASSWORD>
H057 K3Y: <HOST_KEY> | N4M3: <NAME>| V41U3: <DECRYPTED_COOKIE>
All files created by the stealer on the victim’s machine start with the line: “<–W4SP STEALER ON TOP–>”. All collected data is sent to the operator via a Discord webhook (https://discord[.]com/api/webhooks/1001296979948740648/4wqCErLU3BVeKWnxDA70Gns5vcfxh5OCb3YDIFZaFujqfSRIwHH4YIu3aLOVWjCDeO1H) and rendered in a prettified format:
The stealer also creates and sends a list of saved browser credentials for the URLs containing keywords “mail”, “card”, “bank”, “buy”, “sell”, etc. (see Appendix for a full list). Apart from that, it gathers data from the MetaMask, Atomic and Exodus wallets, as well as Steam and Minecraft credentials.
Having collected credentials, the stealer starts traversing the victim’s directories named Downloads, Documents and Desktop, looking for filenames containing the following words:
'passw', 'mdp', 'motdepasse', 'mot de passe', 'login', 'paypal', 'banque', 'account', 'metamask', 'wallet', 'crypto', 'exodus', 'discord', '2fa', 'code', 'memo', 'compte', 'token'
Interestingly, this list contains multiple French words: “mot de passe” (password), “mdp” (abbreviation for “mot de passe”), “banque” (bank), “compte” (account). The matching files are then uploaded to the same Discord channel.
subprocess.Popen('taskkill /im discord.exe /t /f',shell=true)
The injected script monitors the victim’s actions such, as changing their email address, password or billing information. The updated information is also sent to the Discord channel.
Kaspersky solutions detect the threat with the following verdicts:
|42f0f3b4d5a2be7f09d1c02668cb2c08||injected Discord index.js|