Affected platforms: All platforms where PyPI packages can be installed
Impacted parties: Any individuals or institutions that have these malicious packages installed
Impact: Leak of credentials, sensitive information, etc.
Severity level: High
The Python Package Index (PyPI) is an open repository of software packages developed by the Python community to help people quickly develop or update applications. While most of the packages uploaded to PyPI are posted by dedicated individuals looking to support the Python community, threat actors also regularly post packages infected with malware. The FortiGuard Labs team uses a proprietary, AI-driven OSS malware detection system to hunt for and monitor these threats. Recently, we identified a PyPI malware author (who goes by the ID “WS”) discreetly uploading malicious packages to PyPI. We now estimate that there may be well over 2000 victims of “WS” just from the packages described below alone.
The identified packages—nigpal, figflix, telerer, seGMM, fbdebug, sGMM, myGens, NewGends, and TestLibs111—exhibit attack methodologies similar to those outlined in a Checkmarx blog post published four months ago. The similarity suggests a possible connection to a malicious campaign from early 2023. Notably, these packages incorporate base64-encoded source code of PE or other Python scripts within their setup.py files. Depending on the victim devices’ operating system, the final malicious payload is dropped and executed when these Python packages are installed.
A brief timeline of our findings about this info-stealing author is detailed below.
Figure 1: A brief timeline of malicious PyPI packages published by the author “WS”
The packages released before December 2023 are very similar to those discussed in earlier blog posts. Specifically, they deploy Whitesnake PE malware if the victim’s device runs on Windows, or they can deliver a Python script designed to steal information from Linux devices. A subtle distinction lies in the new method now being used by the Python script to transmit stolen data. Instead of relying on a single fixed URL, these new malware variants use a range of IP addresses as the destination, likely to ensure successful data transmission even if one server fails. Given the strong resemblance of these new packages to the previous ones, we will focus on analyzing the payloads from these more recent variants.
Unlike earlier attacks that targeted both Windows and Linux users, this recent set of packages predominantly targets Windows users. While the executable payload for each package varies slightly from one another, they consistently aim to exfiltrate sensitive information from victims.
Let’s take a brief look at a few of these payloads.
PE Payload of sGMM Package
Unlike the previous Whitesnake PE payloads developed using .NET, this sample manifests as a Python-compiled executable crafted using the PyInstaller tool.
Upon careful examination of its contents post-extraction, the primary script file is revealed as ‘main.pyc’ accompanied by an ‘addresses.py’ counterpart. The mere presence of this counterpart raises suspicions.
Figure 2: Extracted files
Upon decompiling ‘main.pyc,’ we can see that the code is somewhat incomplete, accentuating its clandestine nature. Red flags include copying itself to the Windows startup folder for autorun on startup. The script also probes logical drives, endeavoring to copy itself into non-removable drives. Notably, it adopts a discreet strategy by monitoring the count of its running instances, ensuring an inconspicuous profile complete with an exit strategy if the count surpasses two.
Figure 3: Decompiled main.pyc
Finally, the script retrieves the clipboard contents and compares them against predefined cryptocurrency address patterns. The discovery of matching patterns prompts the script to overwrite the clipboard with corresponding addresses sourced from ‘addresses.py.’ This could deceive unsuspecting victims into directing things like cryptocurrency transactions to an unexpected destination.
Figure 4: addresses.py
PE Payload of myGens & NewGends Packages
This payload is an encrypted .NET executable. Upon being installed on the user’s device, the payload launches an invisible window of “cmd.exe.” It then uses “powershell.exe” through this window to add itself to the Windows Defender’s exclusion list to bypass the built-in security response:
Figure 5: Adding itself to the Windows Defender exclusion list
It then copies itself to the Windows Application folder and creates a scheduled task to run itself every hour once the device is infected:
- %LOCALAPPDATA%\Packages\Microsoft.Windows.Accounts.ControlRCP_ruzxpnew4af
- HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\{070B9798-7C59-4A69-BB25-F353628999B0}\Path|\MicrosoftWindowsControlRCP
Figure 6: Confirmation of copying itself and scheduling a task
Each time the task runs, it establishes a connection to the malicious IP: 194[.]36[.]177[.]30. It uses “socket.io” to connect the server to the client. This way, if the connection is lost, it will automatically try to reconnect.
Figure 7: Decryption of “socket.io” and IP
The core of this payload tries to collect user information, such as the user’s IP address, the host credentials, etc.:
Figure 8: User info grabber
The following strings offer more compelling evidence of the information-stealing capabilities of this payload. They reveal that the payload not only captures mouse and keyboard interactions but also acquires and transmits wallet and browser data to the remote server.
Figure 9: List of Properties for the stealer
Figure 10: Snippet of code showing wallet grabbing
Figure 11: Function showing browser stealing
PE payload of TestLibs111 Package
Once this sample runs, we can observe data being sent to a suspicious IP address (194[.]36[.]177[.]30), as shown in Figure 12. At first glance, we might dismiss these as inconspicuous strings. However, further investigation shows this to be a .zip file shrouded in multiple layers of encryption. This raises serious concerns about its behavior.
Figure 12: Traffic sent to IP address 194[.]36[.]177[.]30
Upon decryption, we see ‘PK’ as the initial bytes, indicating a .zip file format.
Figure 13: Hex dump of .zip file
After debugging this sample, there are several indicators as to what sensitive content it may be interested in. Below are a few of the many interesting strings that were found during our analysis.
Figure 14: Strings found in assembly code
Some of these strings may indicate info-stealing from different services, such as browsers, applications, and cryptocurrency services. Some examples we found included Chrome, Discord, Filezilla, and Coinbase.
When we extract the .zip file, we can see some of the collected content.
Figure 15: zip file contents
The author then exfiltrates this sensitive information to the server.
Conclusion
This blog demonstrates the ability of a single malware author to disseminate numerous info-stealing malware packages into the PyPI library over time, each featuring distinct payload intricacies. Users are urged to exercise utmost caution when using open-source packages, checking for malicious content or payloads that may render targeted devices susceptible to information theft. The narrative of this particular malware author has unfolded over several months and showcases the considerable amount of havoc that has been wrought. Information-stealing malware is an increasingly pertinent and pressing subject. Safeguarding against such persistent adversaries demands a strategic and forward-thinking approach to fortify your defenses.
Fortinet Protections
FortiGuard AntiVirus detects the malicious files identified in this report as
nigpal-0.1 setup.py: PYTHON/Agent.c51b!tr
nigpal payload: (PE) MSIL/WhiteSnake.C!tr
nigpal payload: (Python) PYTHON/Agent.7f73!tr
figflix-0.1 setup.py: PYTHON/Agent.c51b!tr
figflix-0.2 setup.py: PYTHON/Agent.c51b!tr
figflix payload: (PE) MSIL/WhiteSnake.C!tr
figflix payload: (Python) PYTHON/Agent.7f73!tr
telerer-2.0 setup.py: PYTHON/Agent.c51b!tr
telerer-2.1 setup.py: PYTHON/Agent.c51b!tr
telerer-2.2 setup.py: PYTHON/Agent.c51b!tr
telerer-2.3 setup.py: PYTHON/Agent.c51b!tr
telerer payload: W32/Kryptik.HTVT!tr
seGMM-1.3.1 setup.py: PYTHON/Agent.c51b!tr
seGMM-3.1.1 setup.py: PYTHON/Agent.c51b!tr
seGMM payload: W32/Kryptik.HTVT!tr
fbdebug-0.1 setup.py: PYTHON/Agent.c51b!tr
fbdebug payload (PE): MSIL/WhiteSnake.C!tr
fbdebug payload (Python): PYTHON/Agent.7f73!tr
sGMM-1.3.2 setup.py: PYTHON/Agent.c51b!tr
sGMM payload: W32/ClipBanker.DE!tr
myGens-1.3 setup.py: PYTHON/Agent.c51b!tr
myGens-1.3 payload: MSIL/Agent.ERW!tr.spy
NewGends-1.3 setup.py: PYTHON/Agent.c51b!tr
NewGends-1.3 payload: MSIL/Agent.ERW!tr.spy
TestLibs111-1.4 setup.py: PYTHON/Agent.c51b!tr
TestLibs111-1.5 setup.py: PYTHON/Agent.c51b!tr
TestLibs111-1.7 setup.py: PYTHON/Agent.c51b!tr
TestLibs111 payload: W32/Agent.ORR!tr.pws
The FortiGuard AntiVirus service is supported by FortiGate, FortiMail, FortiClient, and FortiEDR. Customers running current AntiVirus updates are protected.
The FortiGuard Web Filtering Service detects and blocks the download URLs cited in this report as Malicious.
The FortiDevSec SCA scanner detects malicious packages, including those cited in this report that may operate as dependencies in users’ projects in test phases, and prevents those dependencies from being introduced into users’ products.
If you believe these or any other cybersecurity threat has impacted your organization, please contact our Global FortiGuard Incident Response Team.
IOCs
File | Hash (sha256) | Detection |
nigpal-0.1 setup.py | c53d1387864ea3034bc4e19af492b3e67147d3fdc1d8b9752e24600d6919e3af | PYTHON/Agent.c51b!tr |
nigpal payload (PE) | ab75ea75d1fe5bc51ecef274a95f7b835b09a0c7c95c4227366a3d64b5dee7c0 | MSIL/WhiteSnake.C!tr |
nigpal payload (Python) | 24e07dd8c4a6fb92d842ebc168a40505bbd0421a16c13a06571910ca7a40a5a5 | PYTHON/Agent.7f73!tr |
figflix-0.1 setup.py | 41ff3fedb78c672c6d0e5e849f81c8be10c0767558fcfdf6f529215556354d9e | PYTHON/Agent.c51b!tr |
figflix-0.2 setup.py | 377e8ca04aed57a10b350d9eb4a6e64818bb69b790f33db4be0fd22589c435ad | PYTHON/Agent.c51b!tr |
figflix payload (PE) | 34e5bd67fbd9a7040dca9cae90e36013aaeda1940bb39e7fcd5d5fa9c85cadc8 | MSIL/WhiteSnake.C!tr |
figflix payload (Python) | b2bf755c4e1336f5ab36bc679d4a86e4c0d4da7b33a26b9ec8c01e179027f66b | PYTHON/Agent.7f73!tr |
telerer-2.0 setup.py | 3dcff80475ebfb9a3aa93f3cebd8f008ea64d857a7c53719f1ca047dfd050e1c | PYTHON/Agent.c51b!tr |
telerer-2.1 setup.py | dc5b74c1007bbe9acce3cddf30870766867b40e7d37264b7bdaf3b5f40747c10 | PYTHON/Agent.c51b!tr |
telerer-2.2 setup.py | f1f6501a97b9145d8dd755d25a39c1803fe54995a39fd59b2914f591d56bdc68 | PYTHON/Agent.c51b!tr |
telerer-2.3 setup.py | 8bdc674e3a41370a2d0a997b6ca673c6d646ed580400af242980a5ec374864a2 | PYTHON/Agent.c51b!tr |
telerer payload | 4fac457f8170e26643d0a4d8a0199e93d72872e1799e95f5c522a50754982079 | W32/Kryptik.HTVT!tr |
seGMM-1.3.1 setup.py | ef0e1a8378d1dd9e3cfd0d59d1969b618e15ddb4bbfaf50057670842004346e8 | PYTHON/Agent.c51b!tr |
seGMM-3.1.1 setup.py | 6fe87ab0590229d11f2d174bcf13cfbaca6f6c9dc55af84527c96de16c12c799 | PYTHON/Agent.c51b!tr |
seGMM payload | 4fac457f8170e26643d0a4d8a0199e93d72872e1799e95f5c522a50754982079 | W32/Kryptik.HTVT!tr |
fbdebug-0.1 setup.py | da0c21c66fd0dc42b1bcb06c9bd0d7e48b1b866720229712df64410eebd62199 | PYTHON/Agent.c51b!tr |
fbdebug payload (PE) | d9568da21005794d80eb6572ccce47cc766ba5fe24b2b82cd4ff2cf05d8531a2 | MSIL/WhiteSnake.C!tr |
fbdebug payload (Python) | ec9e1342b0bddbd0ef65cd37a751b3a8c3c8170cdd8cce0f0fb6815b6be26a45 | PYTHON/Agent.7f73!tr |
sGMM-1.3.2 setup.py | 8fb72c3a6a5d96f91c3dc46541331ebf0a6cf326d2353ab6f2b1c119e9907670 | PYTHON/Agent.c51b!tr |
sGMM payload | 94be6da31c5f896017af733a44b9ea00abbb35bce0a8dbcab776367234e4d818 | W32/ClipBanker.DE!tr |
myGens-1.0 setup.py | f22110ea2376082651f5f0724875e6f9d083e2be0688dc06b59206c35fa50def | PYTHON/Agent.c51b!tr |
myGens payload | 03a1621af484ff8f5c1797b25426bab656b6731dba43e31fe58fc1f1963d8484 | MSIL/Agent.ERW!tr.spy |
NewGends-1.3 setup.py | 857bc70fb5968b9f5e257e41f4be9cdd8c7135314bf6200e2cf5b60186401e7a | PYTHON/Agent.c51b!tr |
NewGends payload | 03a1621af484ff8f5c1797b25426bab656b6731dba43e31fe58fc1f1963d8484 | MSIL/Agent.ERW!tr.spy |
TestLibs111-1.4 setup.py | c9e0b8c6c5140acae2b3bf003d9ae2a69abf04253b0bd932ec97c732a4b9bf97 | PYTHON/Agent.c51b!tr |
TestLibs111-1.5 setup.py | 0e13bb49aba0878b919bc0980ce2e9e3cfa876387fcedf5af41235ab0f7a440a | PYTHON/Agent.c51b!tr |
TestLibs111-1.7 setup.py | 14cd40cce030bfca6a4c06fdadd353b5eaa092e7f73ba65308afedc04270c9b9 | PYTHON/Agent.c51b!tr |
TestLibs111 payload | 2b617277fc551b7500867ee009a0f80cbe6d5ee729bdfbf9b4f9d52164811082 | W32/Agent.ORR!tr.pws |