At Loginsoft, we acknowledge the emerging threats related to supply-chain security and their impact on businesses/organizations and the whole ecosystem. Since open-source package repositories like NPM, PyPi, Nuget, etc., are one of the most targeted ones in such attacks, we actively monitor them for malicious activities and take necessary actions to eradicate such threats.
This blog illustrates one such instance where, in collaboration with the Checkmarx’s supply chain research team, we recently discovered an interesting malicious package in the PyPi public repository at pypi.org. Upon closer inspection of the package by our team, a lot of insightful information was gained, and which confirmed the suspicion – it was classified as malicious. The complete analysis process of the package and our interpretation for the same is explained in detail below.
Here is a quick introduction of our subject i.e., the “pypiele” package. It was first published on the PyPi repository on 30th July 2023 with the motive to blend in like any other ordinary package and compromise as many users/developers. The threat actor behind it released another iteration – 1.0.2 after the initial release of version 1.0.1 on the same day and it had over 404 downloads in total before it was removed. For any ordinary package this count may be very small but with respect to a malicious package, even if we consider only 10% as legitimate downloads from real users (neglecting downloads from automatic scanners), it has a huge impact.
Technical analysis and comprehensive breakdown
We’ve selected the second iteration of the malicious package, which is also the latest one, as the focal point of this analysis.
Exhibit 1.1 provides a visual representation of the package structure for version 1.0.2, displaying all the included files.
Exhibit – 1.1
In the majority of instances, the infection originates from the setup.py file of a malicious Python package, as this script defines the whole build process and gives the threat actor an opportunity to add custom build steps or to override predefined commands like “install” to execute their own malicious code during the package installation process.
“pypiele” also implements the exact same technique; all the other Python files are empty in the package except for the setup.py script where it overrides the predefined “install” command to execute the malicious code defined under a custom class named – “InstallCommand”.
Exhibit – 1.2
The class definition labeled as “InstallCommand” in exhibit 1.3 contains code segment that has been deliberately obfuscated. However, it is trivial to de-obfuscate it and get the plain text code by simply printing the value of the variable “oIoeaTEAcvpae” just after the for-loop statement finishes. This can be easily inferred from the fact that at the end of the try statement, the Python code object generated using the value stored in the “oIoeaTEAcvpae” variable with compile() is subsequently passed to the eval function – a well-known method for executing arbitrary code.
Exhibit – 1.3
After the de-obfuscation process is completed, the resulting plaintext code can be observed in exhibit 1.4. A quick glance through it shows that it checks for the existence of “System64” directory at the following location “%APPDATA%\Microsoft\Windows\Start Menu\Programs\” (By default, there exists no such directory on any Windows system). If it already exists, this means that the system is already infected and it will do nothing or else, it will create this directory at the same location inside which a VB script – “WIN32.vbs” is created. Not going into many details, this visual basic script simply executes a windows batch script – “WIN32.bat” in a hidden window. Coming on to what this “WIN32.bat” (which is created in the next step) does is that it, makes use of the built-in bitsadmin CLI tool (which uses the BITS – Background Intelligent Transfer Service ) to create a new download job and fetch a malicious executable from the following URL – “hxxp[://]51[.]178[.]25[.]148:8081/dl/runtime” and save it inside the same “System64” directory as runtime.exe. Finally, the batch scripts end up executing runtime.exe, hence initiating the next stage of the infection.
If we observe the nomenclature for the directory, files and the executable that the threat actor has chosen i.e., System64, WIN32.vbs, WIN32.bat and runtime.exe respectively – it is an attempt to masquerade as ordinary Windows system files and blend in without raising any suspicions.
Exhibit – 1.4
Exhibit – 1.5 – WIN32.vbs
Exhibit – 1.6 – WIN32.bat
Analysis of the second stage of infection i.e., the “runtime.exe” executable
- Size – 19.2 MB
- Executable type – 64-bit PE – x86-64
- SHA-256 hash – fc75c821a507e560d7d91e16dcb8fffd84a4bea86f58a99de1528a3b24442c23
At the time of analysis there was no information available about this executable as we searched its cryptographic hash on various threat intelligence platforms, which means it’s totally new in the wild.
That’s where we started with static analysis; checking the ASCII strings present in the executable using the “strings” utility showed many instances which resembled with a Python package’s structure along with library names and DLL files belonging to the Python programming language. This confirmed one thing for sure, that it is basically a Python program which was bundled along with the required dependencies into a standalone Windows executable. This is good news because it is possible to extract the actual python application content from executables like this, which can help in further analysis. To do this, it is first required to know which exact tool was used to generate the executable. “pyinstaller” is one such popular tool which is generally used to perform this task.
We confirmed it in this case by simply piping the output of the “strings” utility to the “grep” command to search for the presence of “pyinstaller” in the strings and a few instances were found.
Exhibit – 1.7 – Existence of “pyinstaller” in strings
Now it’s confirmed that “pyinstaller” was indeed used in this case, the executable’s content can be extracted using an open–source Python script called – “PyInstaller Extractor” available at https://github.com/extremecoders-re/pyinstxtractor.
Running – “python pyinstxtractor.py runtime.exe” extracts all the Python compiled bytecode files from the executable including the dependencies and DLL files into a directory named “runtime.exe_extracted”.
Now, inside this directory with all the extracted content, there are a large number of files related dependencies and DLLs which are of no use for our objective. We specifically need to find “.pyc” – Python bytecode file(s) which originated from the Python program code created by the threat actor. The exact way to find these file(s) is very simple; we need to look for file names which do not resemble library names and they seem custom. In this case there is a file named “s.pyc” whose name seemed unique from others and could possibly be the one we are trying to find (spoiler – this is the exact and only file which consists of compiled bytecode for the malicious Python program).
Exhibit – 1.8
Since, this “s.pyc” file is Python compiled bytecode and is not in plain text format, it is first required to be decompiled. “Uncompyle6” is a popular tool which can be used for this task but unfortunately it only works for compiled bytecode created using Python version 3.8 or below and in this case Python 3.9 was used and so it will not work. In this situation there is an alternate universal tool – “pycdc”(https://github.com/zrax/pycdc) which works for every version of Python. Despite having good compatibility support, it also failed to do the job as it could only decompile the bytecode to some extent which didn’t help much. So, here comes “pycdas” a Python bytecode disassembler which is part of the same repository as “pycdc” – https://github.com/zrax/pycdc.
Using “pycdas s.pyc”, we were able to successfully disassemble the compiled bytecode in the “s.pyc” file and perform further analysis.
After carefully analyzing the disassembly, it was concluded that it is a stealer which collects various information related to the compromised host and sensitive information/saved credentials from a list of different software applications that could be installed on the compromised host. Apart from this, it specially targets web browsers and crypto wallet applications to steal cookies and financial information. These are some of the targeted applications – ‘Discord’, ‘Discord Canary’, ‘Lightcord‘, ‘Discord PTB’, ‘Opera’, ‘Amigo’, ‘Torch’, ‘Kometa’, ‘Orbitum‘, ‘CentBrowser‘, ‘7Star’, ‘Sputnik’, ‘Vivaldi’, ‘Chrome SxS‘, ‘Chrome’, ‘Epic Privacy Browser’, ‘Microsoft Edge’, ‘Uran’, ‘Yandex’, ‘Brave’, ‘Iridium’.
All this stolen information is stored into different files – “alls.armageddon, ckk.armageddon, imp.armageddon, pskk.armageddon, toks.armageddon“ which are then exfiltrated by uploading them to the following URL endpoints – “hxxp[://]51[.]178[.]25[.]148:8081/uploader” and “ hxxp[://]51[.]178[.]25[.]148:8081/upload“.
Once this is done, all these files are deleted from the host to clear tracks.
Exhibit – 1.9 – Exfiltration
Exhibit – 1.9.1 – File deletion
Apart from “pypiele”, there are several other packages that we discovered and associated with the same threat actor. Although these packages had seemingly different IOC’s – “hxxps[://]api-hw[.]com/dl/runtime”, “hxxps[://]api-hw[.]com/dl/w”, “hxxps[://]api-hw[.]com/dl/ww” but the suspicion due to the existence of similar URI paths as found in “pypiele” led to an investigation. Upon checking the DNS record history of the domain – “api-hw[.]com” we found that it previously pointed to the same IP address – “51[.]178[.]25[.]148” that is present in the IOCs of our subject package. The use of the same infrastructure along with similar URI paths confirmed this suspicion.
There is a huge spike in the number of malicious packages getting uploaded on open-source package repositories nowadays, as we have seen recently and in the past when PyPi had to stop new uploads due to high volumes of malicious packages being uploaded – https://status.python.org/incidents/qy2t9mjjcc7g. These malicious packages are evolving and getting much more complex day by day. Therefore, it is highly recommended that you should not install any random package as it may end up compromising you – stay vigilant.
Note – As of 26th August 2023, all the mentioned URLs are still live. So, be cautious if you choose to interact with them.
- Package name – pypiele
- PyPi repository link – https://pypi.org/project/pypiele
- Releases – 1.0.1, 1.0.2
- Date of first release – 30th July 2023
- Total no. of downloads (before it was removed) – 404
MITRE ATT&CK framework tactics and techniques mapping
|Initial access||T1195.001||Compromise Software Dependencies and Development Tools|
|Execution||T1059.006||Command and Scripting Interpreter: Python|
Deobfuscate/Decode Files or Information
|Credential Access||T1555.003||Credentials from Web Browsers|
|Collection||T1409||Stored Application Data|
Exfiltration Over Unencrypted Non-C2 Protocol
Exfiltration Over Web Service
Executable SHA-256 hash – fc75c821a507e560d7d91e16dcb8fffd84a4bea86f58a99de1528a3b24442c23
Kartik Singh – Security Researcher, Loginsoft
For over 16 years, leading companies in Telecom, Cybersecurity, Healthcare, Banking, New Media and more have come to rely on Loginsoft as a trusted resource for technology talent. Whether Onsite, Offsite, or Offshore, we deliver.
Loginsoft is a leading Cybersecurity services company providing Security Advisory Research to generate metadata for vulnerabilities in Open source components, Discovering ZeroDay Vulnerabilities, Developing Vulnerability Detection signatures using MITRE OVAL Language.
Expertise in Integrations with Threat Intelligence and Security Products, integrated more than 200+ integrations with leading TIP, SIEM, SOAR and Ticketing Platforms such as Cortex XSOAR, Anomali, ThreatQ, Splunk, IBM QRadar, IBM Resilient, Microsoft Azure Sentinel, ServiceNow, Swimlane, Siemplify, MISP, Maltego, Cryptocurrency APIs with Digital Exchange Platforms, CISCO, Datadog, Symantec, Carbonblack, F5, Fortinet and so on.
Interested to learn more? Let’s start a conversation.