Days after researchers for Phylum and Checkmarx revealed an ongoing software supply chain attack spreading the W4SP Stealer malware through malicious packages on the Python Package Index (PyPI), ReversingLabs researchers discovered 10 additional PyPI packages pushing modified versions of W4SP that were overlooked.
The newly discovered packages appear to be part of the same campaign but are using slightly modified versions of the W4SP Stealer malware and different command and control infrastructure.
Here's our discoveries, indicators of compromise (IOCs), and ReversingLabs YARA rules that can be used to detect the malicious packages in your environment.
Introduction
In the beginning of November several malicious python packages distributing the W4SP malware were found in the Python Package Index (PyPI) open source repository. These packages contain malicious code, hidden inside __init__.py or setup.py scripts, which downloads a stage 2 payload from a remote location. Stage 2 payload is W4SP stealer capable of stealing a wide range of sensitive data including stored passwords, cookies, Discord tokens, crypto wallets, telegram data and a long list of files related to different web services. This Python malware contains several layers of obfuscation in order to avoid detection. More details can be found in Phylum and Checkmarx reports.
Despite the disclosure by Phylum and Checkmarx, this supply chain attack is ongoing. Further investigation by ReversingLabs has uncovered 10 previously undisclosed Python packages that appear to be part of the same campaign, but that are pushing a slightly modified version of the W4SP Stealer and relying on a different command and control (C2) infrastructure.
Same attack, different downloader
The architecture of the attack uncovered by ReversingLabs is the same as the attack documented by Phylum and Checkmarx. PyPI packages are infected with downloader code and published to the PyPI repository. The malicious code executes upon package installation and downloads an obfuscated stage 2 payload from a remote location and loads it on the infected machine. W4SP Stealer, the stage 2 malware, is capable of stealing sensitive data and exfiltrating it to a remote location.
The versions of the malicious modules ReversingLabs discovered differ from those identified by Phylum and Checkmarx in a number of ways. Previous downloaders were embedded in PyPI package __init__.py scripts and in some cases setup.py scripts and were downloading stage 2 payloads from urls similar to the following:
hxxp://wasp.plague.fun/inject/<random_string>
Transfer.sh: Malware distribution made easy
However, samples discovered during our research were using the public file sharing service transfer.sh to deliver W4SP Stealer. This service provides “easy file sharing from the command line.” It is convenient from an attacker's perspective because it is very friendly for command line scripting and allows uploading up to 10GB of data which can be stored for up to 14 days, for free. What is even more convenient is the ability to define a maximum number of downloads. Such a feature can be used to assure malware gets downloaded only once, ideally by the targeted victim, before it gets removed from the remote location, preventing security researchers from getting their hands on malicious samples. The sample download and execution is performed in a large powershell one-line command. (Figure 1.)
Figure 1: Stage 2 downloader code
In the observed samples, files hosted on transfer.sh service were typically named Updater.zip or transfer.zip. This is the YARA rule that can be used to hunt such downloader python scripts:
rule transfer_sh_url
{
strings:
$updater_url = { 3A 2F 2F 74 72 61 6E 73 66 65 72
2E 73 68 2F ?? ?? ?? ?? ?? ?? 2F
55 70 64 61 74 65 72 2E 7A 69 70 }
$transfer_url = { 3A 2F 2F 74 72 61 6E 73 66 65 72
2E 73 68 2F ?? ?? ?? ?? ?? ?? 2F
74 72 61 6E 73 66 65 72 2E 7A 69 70 }
condition:
$updater_url or $transfer_url
}
W4SP reloaded: expanded data stealing options
In all samples, the entrypoint for the W4SP Stealer payload is in server.pyw file. This file contains a large LZMA compressed and Base64 encoded file which is a minify-ed version of the original payload created using the pyminifier application.
Figure 2: Stage 2 entrypoint before decoding and decompression
After decompression and decoding are performed, the original code is executed. The main function sets up the persistence and threads responsible for keylogging and data stealing.
Figure 3: Main function
An interesting feature of the W4SP Stealer is that there are two mechanisms for data exfiltration. The first uploads stolen data to the tranfer.sh service and sends the download url to the threat actor controlled C2 server together with the information about the infected machine. You can see this part of code in Figure 4, together with the calls to data stealing functions to get a sense about the type of data that gets stolen.
Figure 4: Data exfiltration using transfer.sh service
The second data exfiltration mechanism is based on Cloudflare’s reverse tunnels. The malware first downloads Cloudflare Tunnel client (formerly Argo Tunnel) from their GitHub repository. Then it creates a reverse tunnel and sends the generated tunnel’s URL to the same threat actor-controlled C2 server mentioned in the first case.
Figure 5: Data exfiltration using reverse cloudflare tunnel
The Flask micro web framework is then used to create a local web server which responds to the requests received through the created tunnel. URL paths are created in a modular way, so each functionality could be executed on its own. This activity can be seen in Figure 6, where the paths are the strings inside the @app.route() annotations. This is another example of malicious actors abusing commercial services to further attacks and data exfiltration with tools like reverse tunnels, an increasingly popular technique among malware authors.
Figure 6: Url paths defined using Flask framework
A new RUST downloader
As part of our research, we also employed the ReversingLabs Titanium Platform’s RetroHunt feature, which can test YARA rules against recent file samples submitted to us. The results of that scan included several Python scripts which were expected since the campaign was targeting PyPI users, but also an unexpected Windows PE (portable executable). That sample executes a Powershell block very similar to the one documented above and seen in the PyPI downloader packages. (Figure 7.)
Figure 7: Powershell code snippet responsible for downloading Stage 2 payload
Strings from the executable lead us to the conclusion that this sample was written in Rust. This, again, is consistent with recent trends among malware authors, who are turning to rust to create ransomware and other malicious applications.
The compilation timestamp on the Rust downloader also helps to confirm the timeline for this PyPi campaign. Specifically: the sample we observed was created on September 25th 2022. That is the same date on which the first PyPI package observed as a part of this research — pygradient — was published.
The malicious packages ReversingLabs discovered predate those discovered by Phylum by weeks. According to that company’s blog, the supply chain attack started “around October 12, 2022” but intensified in the third week in October, before it was detected. Phylum did mention, as well, a small set of malicious PyPI packages with similar IOCs it discovered that date to July, 2022.
That suggests the September packages were part of a larger and longer running campaign. One possible explanation is that the packages we discovered were used to test functionality before the actual launch of the malicious campaign in October. Another possibility is that the packages we discovered are artifacts of a real malicious compromise that has not been disclosed. Unfortunately typical pivoting attempts like searching on file similarity and section hashes didn’t have any results so no additional samples were discovered, so there are many questions that remain unanswered at this time.
Conclusion
Our discovery of an earlier tranche of malicious PyPI packages expands our understanding of this latest software supply chain attack on the PyPI platform. It is also a reminder to organizations that the mere discovery and disclosure of supply chain attacks and compromises is not enough to end such attacks, or prevent malicious actors from continuing their work.
Based on our findings, the W4SP Stealer malware campaign exposed by Phylum and Checkmarx in October started on September 25th and had its climax a month later, in late October. Based on Phylum data, the campaign may stretch back even further, to July, 2022. Still, it is alive and active today, with new PyPI packages published on a daily basis.
This is similar to the behavior we have seen with earlier supply chain attacks, such as IconBurst, where new, malicious packages consistent with the IconBurst campaign have continued to turn up in the months following initial discovery of that campaign on the npm platform.
As this incident and incidents like IconBurst reveal: Disclosure of supply chain attacks might fail to capture the full breadth of malicious campaigns. At the same time, malicious actors may continue to publish new malicious packages during and even after the campaign has been exposed. To counter these threats, development organizations and their customers have to remain vigilant: scanning open source packages for malicious functionality and being on the lookout for other supply chain attacks including dependency confusion, typosquatting and more.
Indicators of Compromise (IOC) list
C2 IP address:
206.189.80.30
185.112.83.115
PyPI packages:
package_name |
version |
SHA1 |
pygradient |
2.3 |
7ba6cbb93ad96f7f4e8a0e04b8fc6a317579e933 |
pygradient |
2.5 |
c71b137da681681507379205aaa91b7b5ff95457 |
clistyling |
2.0 |
723dfe8bdb6ca2ce1d41a3dc36177357300714bb |
styling |
1.9 |
c6a1b89578f75b8a7208f2d65eb8485301e2b74b |
styling |
2.0 |
a829c65a2fbf63fe7e19d42bb8715feaaf6614bf |
styler |
2.0 |
ceb389fe35a02b23bc57417c121f13a18c5bca6c |
paintpy |
2.0 |
745f78c9fa96c4133ac5fe8b580b28c93d6bdd1e |
devicespoofer |
2.0 |
25a4146e81147ba0b3043e3f90c775d15ba134a7 |
devicespoofer |
2.2 |
f3d2639b15bf1ef9233787db092e2868554c64f3 |
devicespoof |
2.0 |
2ea977718fb9d131799a61a1d9ce872076d43628 |
1inch |
8.6 |
94376c20c5e65419dde80e6125fb6e03e9bf4698 |
1inch |
8.7 |
d5056548377c32149ab814d658395842bf64d93d |
1inch |
8.9 |
dcfd849cfe2a14137858db35201427af99170555 |
ethereum2 |
8.4 |
01c7251610510bd0f122e7b11cb05d6a07baa317 |
ethereum2 |
8.6 |
26f0844b44f1ca02d9724da894e7d1ca66111b79 |
ethereum2 |
8.9 |
b57d662cc5814b7e6ab2f7bccd6e0b7e5d778610 |
ethereum0etl |
8.6 |
5ada51df30f972ea27bfa55799e8bf4d2dcdc39c |
Stage 2 zip archives SHA1:
f44cf80ce3a162f2354d5b60f8d48eb09760edb9 |
750b1ba531e59ddc51d75b1fe48025f27f8157dc |
4742cfe83d8e9ebf6590b1ed553d804e343d2b72 |
b6d0a1dce731563abbf1bce0c6c229edcb6da9e0 |
42fff691ca7c67144ef25084ac7c262606b963ff |
8f7b0902135f172a11a869acb02ebed32a3d9459 |
56932c92f9d389252f9a84e128696bdb298263d0 |
acf42993259db2998c4c7960f3529412b740baaf |
0717a5fe9dd8ca6cb20d2bc1e78108e4ae8fd057 |
4461e37aaf15ae350b5b3068babdf758dee6fe87 |
8b83dc33acc228f321e18f7e2b8722123ee19611 |
a2840950dcc021012414c5cadfc07e111ccbf27b |
589dd13059f63c09ff281d9106122f29c59fa4c9 |
aacc3639677d6f7c307de37c57b68ed15187bde4 |
07b64e16e08605da5b16d592ed0a954b75136e48 |
Executable downloader SHA1:
f6849ddf8b5f043b7499e239092ffeee91da2e47
[ Open-source protection: ReversingLabs YARA Rules GitHub page ]