# OceanLotus (APT32) PyPI Supply Chain Campaign — ZiChatBot Cross-Platform Malware via uuid32-utils, termncolor & colorinal Wheels Using Zulip REST API for C2

> Kaspersky GReAT attributed a PyPI supply chain campaign to OceanLotus (APT32) involving three malicious wheel packages — uuid32-utils, termncolor and colorinal — uploaded between 10-22 July 2025 that drop a previously unknown cross-platform malware family named ZiChatBot. The malware abuses the Zulip team-chat REST API on the helper.zulipchat.com organization (now deactivated) as a covert C2 channel, weaponising a benign vcpktsvr.exe via DLL side-loading of libcef.dll on Windows and a /tmp/obsHub/obs-check-update ELF on Linux. Attribution to OceanLotus is supported by Kaspersky's Threat Attribution Engine which matched dropper decompression and decryption logic with 64% similarity to known APT32 droppers; an earlier Zscaler ThreatLabz disclosure in August 2025 covered the termncolor/colorinal cluster without naming the actor.

- **Published:** 2026-05-06T12:00:00Z
- **Last reviewed:** 2026-05-06T12:00:00Z
- **Canonical:** https://intel.threadlinqs.com/threat/TL-2026-0467
- **ID:** TL-2026-0467
- **Severity:** HIGH
- **Category:** MALWARE
- **Status:** MONITORING
- **Actor:** APT32 (Vietnam)
- **Detections:** 9 · **IOCs:** 42 (full data via the Threadlinqs MCP server — Purple tier)

## Description

Overview
--------
Between 10 July and 22 July 2025, three malicious Python wheel packages were published to the Python Package Index (PyPI) by accounts laz**** (tutamail.com) and sym**** (proton.me): uuid32-utils (multiple 1.x.x versions), colorinal 0.1.7 and termncolor 3.1.0. termncolor depends on colorinal so installing termncolor pulls in the malicious chain; uuid32-utils carries the dropper directly. colorinal had ~529 downloads and termncolor ~355 downloads before removal. All three packages were taken down by PyPI maintainers, and the attacker-controlled Zulip organisation "helper" (helper.zulipchat.com) was officially deactivated by Kandra Labs. Kaspersky's Securelist disclosure on 6 May 2026 attributed the campaign to OceanLotus (APT32) — a Vietnam-aligned espionage group — based on a 64% similarity match between the dropped loader's decryption/decompression logic and a known OceanLotus dropper, surfaced by Kaspersky's Threat Attribution Engine. The malware family was named ZiChatBot for its use of the Zulip Chat platform as command and control.

Windows Infection Chain
------------------------
1. The user runs `pip install termncolor` (or installs uuid32-utils). The wheel registers `colorinal\unicode.py` which is imported by `__init__.py`.
2. `is_color_supported()` in unicode.py loads the bundled native module `terminate.dll` into the running Python process and invokes the exported function `envir` with the parameter `xterminalunicod` (UTF-8 encoded).
3. terminate.dll AES-CBC decrypts an embedded blob and drops two files into `%LOCALAPPDATA%\vcpacket\` — a legitimate signed Microsoft binary `vcpktsvr.exe` (the side-loading host) and the malicious `libcef.dll` (the ZiChatBot payload).
4. vcpktsvr.exe is launched, side-loads libcef.dll, and ZiChatBot begins execution.
5. Persistence is established by writing the registry value `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\pkt-update` pointing to `%LOCALAPPDATA%\vcpacket\vcpktsvr.exe`.
6. The dropper and unicode.py self-delete to reduce forensic surface.

Linux Infection Chain
---------------------
1. The Linux variant of the wheel ships terminate.so (and a variant Backward.so) which performs the equivalent decryption stage.
2. The ELF payload is dropped to `/tmp/obsHub/obs-check-update`.
3. Persistence is established with the user crontab entry: `5 * * * * /tmp/obsHub/obs-check-update`.

ZiChatBot — C2 over Zulip
-------------------------
Unlike traditional malware ZiChatBot does not contact dedicated attacker infrastructure. Instead it authenticates to the Zulip Cloud REST API at `https://helper.zulipchat.com/api/v1/` using a hard-coded base64-embedded credential that decodes to `Morian-bot@helper.zulipchat.com:U8REXlI6Kf8qXB9rQzOPBiIA4brJ58qG`. It uses two fixed channel/topic pairs: one for posting harvested system information from the victim and one for fetching shellcode tasking. Shellcode is XOR-decoded with the three-byte key `3a7` and executed reflectively in the host process. The bot supports a single command — "execute shellcode received from server" — and acknowledges successful execution by sending the heart emoji (`:heart:`) reaction back to the C2 message. Strings, imports and configuration are protected with AES-CBC. Forensic analysis of the Zulip organisation showed three active operator users and 90,692 messages exchanged from 10 July 2025 onward.

Attribution
-----------
Kaspersky Threat Attribution Engine reported a 64% similarity score between ZiChatBot's loader code (decryption and decompression routines, control-flow markers and string obfuscation) and a known OceanLotus dropper. OceanLotus (also tracked as APT32, SeaLotus, APT-C-00, Cobalt Kitty, Ocean Buffalo) is a Vietnam-nexus espionage actor with a long history of supply chain abuse, watering-hole operations and signed-binary side-loading targeting ASEAN governments, dissidents, automotive and pharmaceutical sectors. The choice of Zulip as a C2 channel is novel for the actor and represents the first publicly documented use of Zulip's REST API for C2 by a nation-state cluster.

Defensive Implications
----------------------
Developers and CI/CD pipelines that pull from public PyPI without pinning or hash-checking are the primary attack surface. The Zulip-as-C2 design defeats domain blocklists that allow productivity SaaS and bypasses TLS inspection by riding a legitimate, certificate-pinned vendor. Defenders should hunt for child processes of `python.exe` writing to `%LOCALAPPDATA%\vcpacket\`, registry Run-key writes named `pkt-update`, outbound TLS to `helper.zulipchat.com`, and any anomalous workstation traffic to `*.zulipchat.com` from hosts that do not normally use Zulip.

## MITRE ATT&CK

- T1195 Supply Chain Compromise
- T1195.002 Compromise Software Supply Chain
- T1059.006 Command and Scripting Interpreter: Python
- T1106 Native API
- T1204.002 User Execution: Malicious File
- T1547.001 Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder
- T1053.003 Scheduled Task/Job: Cron
- T1574.001 DLL
- T1027 Obfuscated Files or Information
- T1140 Deobfuscate/Decode Files or Information
- T1070.004 Indicator Removal: File Deletion
- T1620 Reflective Code Loading
- T1036.005 Match Legitimate Resource Name or Location
- T1082 System Information Discovery
- T1071.001 Application Layer Protocol: Web Protocols
- T1102.002 Web Service: Bidirectional Communication
- T1573.001 Encrypted Channel: Symmetric Cryptography
- T1132.001 Data Encoding: Standard Encoding
- T1583.006 Acquire Infrastructure: Web Services
- T1585.002 Establish Accounts: Email Accounts
- T1587.001 Develop Capabilities: Malware

## Sources

- [OceanLotus suspected of using PyPI to deliver ZiChatBot malware](https://securelist.com/oceanlotus-suspected-pypi-zichatbot-campaign/119603/)
- [Supply Chain Risk in Python: Termncolor and Colorinal Explained](https://www.zscaler.com/blogs/security-research/supply-chain-risk-python-termncolor-and-colorinal-explained)
- [Malicious PyPI and npm Packages Discovered Exploiting Dependencies in Supply Chain Attacks](https://thehackernews.com/2025/08/malicious-pypi-and-npm-packages.html)
- [Weaponized Python Package termncolor Uses Windows Run Key for Persistence](https://gbhackers.com/weaponized-python-package-termncolor/)
- [uuid32-utils — Safety DB advisory](https://data.safetycli.com/packages/pypi/uuid32-utils/)
- [MITRE ATT&CK Group G0050 — APT32 (OceanLotus)](https://attack.mitre.org/groups/G0050/)
- [ESET malware-ioc OceanLotus indicators repository](https://github.com/eset/malware-ioc/tree/master/oceanlotus)

## Full data

Detection queries (Splunk SPL / Microsoft KQL / Sigma) and IOC values require the Threadlinqs MCP server (Purple tier): https://intel.threadlinqs.com/mcp

Canonical: https://intel.threadlinqs.com/threat/TL-2026-0467
