Keypoints
- ReversingLabs found three new malicious PyPI packages (tablediter, request-plus, requestspro) that continue the VMConnect supply-chain campaign.
- tablediter embeds XOR/hex-encoded payloads decrypted at runtime from an add_row function rather than executing on install.
- request-plus and requestspro impersonate the legitimate requests library, modify __init__.py to spawn a thread, and use cookies.py to perform POST/GET C2 communication and retrieve double-encrypted modules.
- Malware uses layered obfuscation (XOR + hex or Base64 + XOR), periodic C2 polling loops, and downloads next-stage payloads for execution.
- Code similarities (payload decoding, subprocess.Popen execution, platform-based branching, and polling loops) match earlier samples (e.g., py_QRcode/QRLog), supporting linkage to Lazarus Group sub-groups.
- IOCs include C2 domains (packages-api.test, tableditermanaging.pro), an IP (45.61.136.133), and multiple PyPI package versions and SHA1 hashes.
MITRE Techniques
- [T1195] Supply Chain Compromise – Malicious packages were published to PyPI to distribute malware as dependencies. (‘malicious supply chain campaign that the research team dubbed “VMConnect”’)
- [T1036] Masquerading – Attackers used typosquatting and copied package descriptions to impersonate popular libraries. (‘typosquatting attacks in which malicious packages are given names and descriptions that closely resemble the names of legitimate open-source packages’)
- [T1059.006] Command and Scripting Interpreter: Python – Decoded payloads are executed as Python code (subprocess/Popen calls spawn execution). (‘When that Base64 string was decoded and executed, it contacted the C2 server’)
- [T1027] Obfuscated Files or Information – Payloads are hidden using XOR, hex encoding, and Base64 to avoid detection. (‘uses a combination of XOR encryption and hex encoding’ )
- [T1071.001] Application Layer Protocol: Web Protocols – C2 communication occurs over HTTP(S) using POST and GET to exchange tokens and payloads. (‘send it to a URL referencing the C2 server in the form of a POST HTTP request’)
- [T1105] Ingress Tool Transfer – Infected hosts download additional encoded/obfuscated modules and next-stage payloads from attacker-controlled URLs. (‘attempted to download another Base64-encoded string with additional commands’)
Indicators of Compromise
- [Domain] C2 infrastructure – packages-api.test, tableditermanaging.pro (domains used by malware for command-and-control and payload delivery)
- [IP address] C2 server – 45.61.136.133 (listed as an associated C2 IP)
- [PyPI package names] Malicious packages – tablediter, requestspro (used to distribute the malicious code; impersonate popular libraries)
- [File hashes] Malicious package releases – 321363f11464208ee24e56a700ad5d26154df4bd (request-plus), 859f5b0af717fca9f890dcba0b87ac63be469033 (tablediter), and 12 more hashes
- [File names] Modified source files – __init__.py, cookies.py, tablediter.py, bounding.py (locations where malicious code, threads, decryption, and C2 logic were implemented)
The malicious PyPI packages follow a common technical pattern: they embed an obfuscated payload in the package code and only decrypt/execute it at runtime when specific functions are invoked. For tablediter the malicious code is placed in the add_row method and calls a decryption routine (bounding.py) that XOR-decrypts a long hex string, inserts the real C2 address into a template, builds a host identifier, and executes the decoded loader; the loader then polls the C2 in a loop to fetch and run further commands. Requests-impersonator packages modify __init__.py to spawn a background thread that calls cookies.py, which collects host information and performs POST/GET exchanges with the C2; responses contain double-encrypted (Base64 + XOR) Python modules that are decoded and executed to download next-stage payloads.
Across samples, attackers used evasion measures to defeat dynamic detectors: delaying execution until import/function invocation (instead of execute-on-install), replicating legitimate package files and descriptions to avoid manual inspection, and layering encodings (XOR/hex or Base64+XOR) to conceal payloads. The downloaded payloads exhibit consistent behaviors—platform-aware path handling (platform.system()), Base64-encoded next-stage blobs written to disk and executed via subprocess.Popen, and an infinite polling loop with sleep intervals to receive Base64-encoded commands—supporting reuse of code and shared C2 infrastructure observed in earlier campaigns.
Detection and response should prioritize static analysis for indicators such as unusual encodings in package files, unexpected network endpoints referenced in modules, modified __init__.py threads or functions, and the specific package names/hashes/domains listed in the IOCs. Blocking known C2 domains/IPs, monitoring for abnormal outbound HTTP POST/GET traffic from developer/build systems, and scanning PyPI dependencies for tampered files (e.g., cookies.py, bounding.py) can interrupt the infection chain before next-stage payloads are retrieved.
Read more: https://www.reversinglabs.com/blog/vmconnect-supply-chain-campaign-continues