APT32's Multi-Stage macOS Trojan Innovates on Crimeware Scripting Techniques

APT32's macOS Backdoor Borrows From Crimeware Playbook in Multi-Stage Trojan Campaign

Researchers at SentinelOne have detailed a sophisticated multi-stage macOS backdoor deployed by APT32 — the Vietnamese-linked threat group also known as OceanLotus, Bismuth, and SeaLotus — revealing how the nation-state actor is actively borrowing and refining techniques from commodity malware authors to sharpen its intrusion capabilities on Apple platforms.

A Disguised Delivery: The Bait-and-Switch Doc

The initial infection vector is a malicious application bundle disguised as a Microsoft Word document. APT32 embeds a hidden Unicode character in the file name, tricking macOS Launch Services into invoking open on the file rather than handing it off to Microsoft Office. Once executed, the malware swaps out the malicious bundle for a legitimate Office document — using the same visible filename minus the hidden character — and presents it to the victim. The entire operation is invisible to the user, who sees only a normal-looking document open.

While the document distracts the target, a second-stage payload is silently deposited in the /tmp folder and begins establishing persistence.

Shell Script as Dropper: A Crimeware Crossover

The app bundle's main executable is a shell script — a technique increasingly common among commodity adware and malware distributors. Embedded within it is approximately 850KB of base64-encoded data that decodes to the second-stage Mach-O binary. Before executing the payload, the script aggressively targets two macOS security mechanisms:

Persistence: Hidden LaunchAgents and Root Escalation

The second-stage payload — a universal FAT binary with Mach-O slices for both i386 and x86_64, written in C++ — checks the current UID to determine privilege level. If running as root, the persistence mechanism is written to /Library/LaunchDaemons; otherwise it targets the user's LaunchAgents folder. In both cases, the agent carries the label com.apple.marcoagent.voiceinstallerd and points to an executable named mount_devfs stored under a custom directory called Library/User Photos. The Launch Agent or Daemon is written with the Hidden flag set, keeping it invisible in Finder by default.

Third Stage: Backdoor With a Calling Card

The final payload, also a universal FAT binary written in C++, provides full backdoor capabilities: data exfiltration and remote file download. For download operations, APT32 uses libcurl.4.dylib — the same built-in dylib observed in Lazarus APT tooling. The C2 infrastructure in the analyzed sample resolved to the domain mihannevis[.]com.

Two hardcoded strings embedded in the third-stage binary stand out as potential developer calling cards or internal identifiers:

Detection Opportunities

Despite being unknown to static signature engines prior to publication, the malware exposes multiple behavioral detection hooks. The first stage's indiscriminate quarantine bit removal — sweeping from both ~/ and / — is highly anomalous and unlikely to be replicated by any legitimate process. The second stage triggers MITRE ATT&CK techniques T1150 and T1160 through its persistence installation attempts. Additional threat hunting indicators include the Team Identifier UD9UN593Z4, bundle identifier com.apple.files, and the persistence path [~]/Library/User Photos/mount_devfs. Apple has since revoked the samples' code signatures, though the malware remains executable if re-signed.

Nation-State Sophistication Meets Crimeware Ingenuity

The campaign illustrates a growing trend: state-sponsored actors are no longer operating in a vacuum isolated from the crimeware ecosystem. APT32's adoption of shell-script-as-dropper techniques — a hallmark of commodity adware families like Shlayer and Bundlore — signals that the group is actively monitoring and integrating lower-level threat actor innovations into its toolchain, combining them with the resources and discipline of an intelligence-backed operation.

Detection coverage

Read the full analysis on IntelFusions