A PyPI package called dbgpkg, published under an account impersonating a well-known Python developer, carries a fully functional backdoor that fires the moment the package is imported. Armando found all three published versions -- 1.3.6, 1.3.7, and 1.3.8 -- to be malicious, each embedding a distinct command-and-control mechanism inside the package's __init__.py. With 439 downloads before removal, dbgpkg targeted Python developers and CI/CD systems that pull unreviewed packages from PyPI. The package has since been removed from the registry.
Key Findings
- Malicious versions: v1.3.6, v1.3.7, and v1.3.8, all published 2025-05-13; no clean version exists
- Entry point: import-time execution -- the backdoor runs when
import dbgpkg is called, with no install hook required
- Targets: Linux systems -- the payloads use shell commands and Linux paths
- Evasion: v1.3.8 checks for a sentinel file (
/lib/systemd/system/defunct.dat) before executing, preventing re-infection of the same host
- Detection: Script-Python.Backdoor.SupplyChain, Text.Backdoor.SupplyChain, Archive-ZIP.Backdoor.SupplyChain
Package Background
dbgpkg was published to PyPI on 2025-05-13 under the description "Python Debugging Toolkit," presenting itself as a utility for developers who work with Python debugging workflows. The account publishing it used the handle "Lukasa" with the email me@lukasa.org -- a deliberate impersonation of Cory Benfield, the legitimate open source developer who maintains well-known Python HTTP libraries under that same handle. The attacker published 24 packages under this account, all named after real Python ecosystem projects (certifi, requests, h2, hyper, hpack, and others) that the real Lukasa is associated with, creating a convincing false identity across the entire account. The package accumulated 439 downloads and had no declared source code repository. It was removed from PyPI after discovery.
Attack Analysis
All three versions of dbgpkg share the same attack surface: the __init__.py file, which executes at import time without any user interaction. A developer who adds dbgpkg to their project and runs import dbgpkg triggers the payload immediately, whether on a developer workstation, a CI/CD runner, or a build system.
The attacker published three versions with escalating sophistication, suggesting active development during the campaign window. Version 1.3.6 is the simplest: a polling backdoor loop that connects to a dedicated C2 server. Versions 1.3.7 and 1.3.8 swap the direct C2 for a more evasive architecture -- module-hijacking combined with a third-party reverse shell installer -- and 1.3.8 adds a one-time-execution check to evade repeated sandbox analysis.
The package claims an install lifecycle hook via setup.py, but the actual malicious execution does not depend on it. The hook mechanism is present as a side effect of the standard setuptools structure; the real trigger is the Python import system itself, which runs __init__.py the moment any code imports the package.
Malicious Files
__init__.py (v1.3.6): Armando's analysis shows this file implements a classic polling C2 loop. On import, it enters an infinite loop that sends HTTP GET requests to https://ba-ace-f-bee-dfcfa.anondns.net/ every second, reading the server's response as a command. The backdoor supports three command types: reading arbitrary files from disk, writing arbitrary content to disk, and executing shell commands via subprocess.run(value, shell=True) with full output capture. Results are sent back to the C2 via HTTP POST to the same domain's /output endpoint. The simplicity of the design makes it highly portable and reliable. The same __init__.py hash appeared previously in another malicious PyPI package, requestsdev@1.3.6, which has also been removed.
__init__.py (v1.3.7 and v1.3.8): In these versions the attacker replaced the direct C2 with a multi-stage attack using legitimate infrastructure for cover. The __init__.py defines a Proxy class that intercepts calls to the requests and socket Python modules by replacing them in sys.modules at import time. When any wrapped module function is first called, the payload triggers a four-step shell chain: it fetches an RSA public key stored on Pastebin (api_paste_key=qzTFV1NH), pipes the output of https://gsocket.io/y through bash to install the gs-netcat reverse shell binary, encrypts the binary's path using the fetched RSA key, and posts the encrypted result back to Pastebin as a private paste. This architecture routes the exfiltration through Pastebin rather than a dedicated C2, making network-level detection harder. Version 1.3.8 wraps the entire payload in a check for the file /lib/systemd/system/defunct.dat: if that sentinel exists, the payload is skipped, preventing the routine from running twice on the same host and avoiding behavioral sandboxes that re-execute packages.
Recommendations
Developers and security teams should take the following steps:
- Identify exposure: Check whether
dbgpkg v1.3.6, v1.3.7, or v1.3.8 appears in your dependency tree, including transitive dependencies. Any pipeline that ran pip install while these versions were current should be considered potentially affected.
- Update immediately: Remove
dbgpkg entirely -- there is no clean version to upgrade to. Audit your requirements.txt, pyproject.toml, and any lock files for this package.
- Inspect affected systems: On machines that imported this package, check for outbound connections to
ba-ace-f-bee-dfcfa.anondns.net, for the gs-netcat process, and for the sentinel file /lib/systemd/system/defunct.dat. Rotate any credentials or tokens that may have been accessible to the process at the time of import.
- 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 |
|---|
| dbgpkg-1.3.6-py3-none-any.whl | 4194fadcf369e5e7c6d5fcbad39abf29a4fae4bb1633b015606d7c7349d893c7 | Archive-ZIP.Backdoor.SupplyChain |
| PKG-INFO | 02261269d6674977dd25d41076b20369f319dc90dcc9697b9748af72c58b460b | Text.Backdoor.SupplyChain |
| init.py | 994ca8a062b600ac6c36feab989a412db2c7a2b381f51b171ff8da33d5615ae7 | Script-Python.Backdoor.SupplyChain |
Network IOCs
| URL / IP | Purpose |
|---|
| https://ba-ace-f-bee-dfcfa.anondns.net/ | C2 server polled for commands; results POSTed to /output endpoint (v1.3.6) |
| https://pastebin.com/api/api_post.php | RSA public key retrieval and encrypted binary path exfiltration (v1.3.7, v1.3.8) |
| https://gsocket.io/y | Reverse shell binary (gs-netcat) download and installation (v1.3.7, v1.3.8) |
Archive Hashes
| File | SHA256 |
|---|
| dbgpkg-1.3.6.tar.gz | 96b1662de454d1beccec90952236dc0c68176ea00905814ae31f6630acded5c5 |
| dbgpkg-1.3.6-py3-none-any.whl | 4194fadcf369e5e7c6d5fcbad39abf29a4fae4bb1633b015606d7c7349d893c7 |