A PyPI package named fabrice -- one character off the popular SSH-automation library fabric -- bundles a near-complete copy of the real library so it looks and works exactly like the package developers meant to install, then runs a hidden payload the instant it is imported. Armando confirmed that fabrice v1.1.3 fingerprints the operating system and forks its behavior: on macOS it harvests AWS access keys and posts them to a hardcoded server, while on Linux and Windows it downloads or unpacks and executes a multi-file payload for persistence. The package lived on PyPI across 14 versions and 37,804 downloads before removal; every version is malicious.
Key Findings
- Malicious version: v1.1.3, published 2021-09-08; all 14 published versions of fabrice are malicious -- there is no clean baseline
- Entry point: the trojan code lives in
fabrice/group.py, whose top-level test() call executes on import
- Targets: developers and CI systems that typosquat-installed fabrice instead of fabric -- AWS credentials on macOS, persistence/dropper on Linux and Windows
- AWS theft: uses
boto3 to read the host's AWS access key and secret key and POST them to http://89.44.9.227/akkfuifkeifsa
- Cross-platform dropper: Linux fetches a 4-file stager from
http://89.44.9.227/likjfieksce; Windows writes a disguised chrome.exe, p.vbs, and d.py and runs them
- Detection: Script-Python.Trojan.SupplyChain, Archive-ZIP.Trojan.SupplyChain, Archive-GZIP.Trojan.SupplyChain, Archive-TAR.Trojan.SupplyChain, Text.Trojan.SupplyChain
Package Background
fabric is a widely-used Python library for executing shell commands remotely over SSH -- a staple of deployment and automation toolchains. fabrice is a deliberate typosquat of that name, published under the PyPI account "faby" (faby@gmail.com) with a decoy homepage at faby.org and a description that simply reads "SSH Automation." To survive scrutiny, the package ships an almost complete copy of the legitimate fabric source tree, so a developer who installs it gets a working fab command and real documentation -- the malware is hidden inside one of the otherwise-genuine modules.
The package was unusually persistent: 14 versions spanning March to September 2021, accumulating 37,804 downloads before PyPI removed it. The combination of a near-miss name, a functioning copy of the real library, and a long publication window is what made it effective.
Attack Analysis
The malicious logic is appended to fabrice/group.py, and it executes immediately: the module ends with a bare test() call at top level, so simply importing fabrice (which any use of the package does) runs the payload. test() checks platform.system() and branches three ways.
On Windows, it calls winThread(), which reconstructs a payload from a long array of base64-encoded byte chunks, triple-base64-decodes them, and writes the results to C:\Users\Public\Downloads\ as p.vbs, d.py, and a file named chrome.exe -- deliberately disguised as the browser. It then launches the .vbs via os.system, which kicks off the dropped payload.
On Linux, it calls linuxThread(), which issues an HTTP GET to http://89.44.9.227/likjfieksce, splits the response on a SPLITT delimiter into four files, and writes them to ~/.local/bin/vscode/ as service.sh, app.py, info.py, and per.sh -- masquerading as a VS Code directory. It marks the shell scripts executable and runs per.sh via subprocess.check_call, establishing persistence and a foothold.
On macOS (and any other platform), it goes straight for cloud credentials: it creates a boto3.Session(), calls get_credentials() to read the host's AWS access key and secret key, and POSTs them as JSON ({"k": access_key, "s": secret_key}) to http://89.44.9.227/akkfuifkeifsa with a short 4-second timeout. The same IP, 89.44.9.227, serves both the Linux stager and the AWS-key exfiltration endpoint, confirming a single operator.
Every network address in the payload is assembled by concatenating string fragments ("89."+"44."+"9."+"227", "ht"+"tp"+":"+"//") so the C2 never appears as a literal URL in the source -- a simple but effective evasion against grep-style scanning. All three branches are wrapped in bare except: pass, so the host application never sees an error.
Malicious Files
group.py is the trojan. Its top-level test() runs on import and branches by OS: on Linux it downloads a four-file stager from http://89.44.9.227/likjfieksce into ~/.local/bin/vscode and executes per.sh for persistence; on Windows it triple-base64-decodes embedded byte arrays into C:\Users\Public\Downloads\p.vbs, d.py, and a fake chrome.exe and runs them; on macOS it harvests the AWS access key and secret key via boto3 and POSTs them to http://89.44.9.227/akkfuifkeifsa. It is detected as Script-Python.Trojan.SupplyChain.
setup.py, METADATA, and PKG-INFO are the package manifests carrying the "SSH Automation" cover story and the "faby" author identity; they are detected as Trojan.SupplyChain as the manifests of the malicious distribution. The wheel (fabrice-1.1.3-py2.py3-none-any.whl), source tarball (fabrice-1.1.3.tar.gz), and inner .tar are flagged as Trojan containers because they carry group.py.
Recommendations
Developers and security teams should take the following steps:
- Identify exposure: Check whether fabrice (any version) appears anywhere in your dependency tree -- it is easily mistyped for fabric. Any pipeline or developer machine that imported it ran the payload.
- Rotate AWS credentials immediately: On any macOS host (or non-Windows/Linux host) that imported fabrice, assume the AWS access key and secret key were exfiltrated. Rotate them now and review CloudTrail for unauthorized use.
- Hunt the dropped files: On Linux, check for
~/.local/bin/vscode/ containing per.sh, service.sh, app.py, info.py. On Windows, check C:\Users\Public\Downloads\ for p.vbs, d.py, and a chrome.exe in that location. Remove them and investigate any persistence they established.
- Block the C2: Block outbound traffic to
89.44.9.227.
- Update immediately: Remove fabrice entirely and install the legitimate
fabric from PyPI.
- Scan your software supply chain: Use Spectra Assure Community to check whether packages in your environment have been flagged.
Indicators of Compromise
Malicious Files
| Filename | SHA256 | Detection |
|---|
| group.py | 2fc0db63eaa14c1cafb6a63fe1057c01a728597c6c76bda66eba6d1f1065f735 | Script-Python.Trojan.SupplyChain |
| setup.py | 1807ab329044b4849883ed7dcd7fdd64bd6e91d34e90de1a58ec927b47527997 | Script-Python.Trojan.SupplyChain |
| METADATA | e370d2a8050b8be8003a5218487f1d4b3f26d1e16d1ec5beee5fcd2325b89327 | Text.Trojan.SupplyChain |
| PKG-INFO | 3e9548f89d312d0e5ba36edf1a5ffa0c5d1c0b16847f585f9c529aca40cf3506 | Text.Trojan.SupplyChain |
Network IOCs
| URL / IP | Purpose |
|---|
| faby.org | Attacker-registered decoy homepage declared in the package metadata |
| http://89.44.9.227/akkfuifkeifsa | AWS access-key/secret-key exfiltration endpoint (macOS branch); C2 address assembled from string fragments to evade scanning |
| http://89.44.9.227/likjfieksce | Linux stager download (four-file persistence payload split on a SPLITT delimiter); same C2 host |
Archive Hashes
| File | SHA256 |
|---|
| fabrice-1.1.3-py2.py3-none-any.whl | 58d9321bbe399a8eeb0937b7cbcdbf6745532939a53d21afaa79d8db0b21deac |
| fabrice-1.1.3.tar.gz | cbf3bb922b66511eef2f695f2ca8bc152315144185d24cfd10bffd7f99fe00b3 |
| fabrice-1.1.3.tar | 7ec7bd9fcf09ef65925ea4023e9794c889dd853ab78e4d935bccb82730963e17 |