# Elastic catches DPRK passing out KANDYKORN

DPRK actors lured blockchain engineers with a fake arbitrage bot on Discord, delivering a 5-stage macOS chain ending in KANDYKORN RAT with full data exfiltration.

- Payout: Real-world hack
- Severity: high
- Program: Apple
- Analysis by: Colson Wilhoit, Ricardo Ungureanu, Seth Goodwin, Andrew Pease
- Disclosed: 2023-11-01
- Write-up quality: 8/10
- Original: https://www.elastic.co/security-labs/threat-command/elastic-catches-dprk-passing-out-kandykorn
- Library page: https://bugbountylibrary.com/r/blog%3Aelastic-co-security-labs-threat-command-elastic-catches-dprk-passing-out-kandykorn
- Tags: area:os domain:desktop impact:data-leak impact:rce kind:incident platform:threat-intel sev:high tech:email tech:python technique:chained vuln:info-disclosure vuln:rce

**Target:** macOS endpoint (Discord app, PyCharm Python interpreter)
**Root cause:** Victim was socially engineered into running a malicious Python 'arbitrage bot' (Main.py importing Watcher.py) that downloaded and executed staged droppers, ultimately reflectively loading the KANDYKORN RAT.
**Steps:**
1. Attacker DMs victim on public Discord, sends `Cross-Platform Bridges.zip` posing as arbitrage bot
2. Victim runs `Main.py` in PyCharm; it imports `Watcher.py` from `order_book_recorder/`
3. `Watcher.py` creates `_log/`, fetches `testSpeed.py` from a Google Drive URL via urllib, writes and imports it, then deletes it
4. `testSpeed.py` downloads `FinderTools` to `/Users/Shared/` and executes it with URL `tp-globa[.]xyz//OdhLca1mLUp/lZ5rZPxWsh/7yZKYQI43S/fP7savDX6c/bfC`
5. `FinderTools` downloads `.sld` (SUGARLOADER) to `/Users/Shared/` and executes it
6. SUGARLOADER reads/creates RC4-encrypted config `/Library/Caches/com.apple.safari.ck`, connects to C2 `23.254.226[.]90:443`, reflectively loads KANDYKORN via `NSCreateObjectFileImageFromMemory`/`NSLinkModule`
7. SUGARLOADER writes HLOADER as `appname`; HLOADER replaces `/Applications/Discord.app/Contents/MacOS/Discord` (renamed `.lock`) for persistence, spawning Discord and `.log` (SUGARLOADER) via `NSTask.launchAndReturnError`
8. KANDYKORN forks as daemon, decrypts config, awaits integer commands (0xD2–0xE0) for recon, file upload/download/zip exfil, process kill, and interactive shell via `/dev/pts`
**How found:** Elastic detected the intrusion while analyzing attempts to reflectively load a binary into memory on a macOS endpoint — atypical for macOS — and traced back through the Python lure chain.
**Exploitation:** Social engineering via Discord DM to a blockchain engineer; victim manually executes the Python lure in PyCharm. Chain of Python droppers fetches staged payloads from Google Drive and typosquatted C2, then SUGARLOADER reflectively loads KANDYKORN in memory. HLOADER hijacks Discord's execution flow for persistence. KANDYKORN provides encrypted C2 with handshake (random nonce challenge), then full RAT: system enumeration, file exfil (zip to /tmp/tempXXXXXXX), arbitrary command execution via pseudoterminal, and config updates.
**Escalation:** Multi-stage droppers escalate from user-executed Python to in-memory Mach-O loading, then persistence via Discord execution-flow hijack, ending in a memory-resident RAT with interactive shell and exfiltration.
**Insight:** On macOS, watch for `NSCreateObjectFileImageFromMemory-*` temp files and hidden executables in `/Users/Shared`; also monitor renames of `/Applications/*/Contents/MacOS/*` to dotfiles as a persistence hijack signal.

## Proof of concept (copied exactly from the original report)

**Malicious downloader function in Watcher.py**

```
The function import_networklib is then defined. Within it, a Google Drive URL is initialized.

Utilizing the Python urllib library, the script fetches content from this URL and stashes it in the s_args variable. In case of retrieval errors, it defaults to returning the operating system's name. Subsequently, the content from Google Drive (now in s_args) is written into the testSpeed.py file.
```

**FinderTools C2 URL argument**

```
After download, testSpeed.py launches FinderTools, providing a URL (tp-globa[.]xyz//OdhLca1mLUp/lZ5rZPxWsh/7yZKYQI43S/fP7savDX6c/bfC) as an argument which initiates an outbound network connection.
```

**HLOADER self-signed code signature**

```
Executable=Applications/Discord.app/Contents/MacOS/Discord
Identifier=HLOADER-5555494485b460f1e2343dffaef9b94d01136320
Format=bundle with Mach-O universal (x86_64 arm64)
CodeDirectory flags=0x2(adhoc) hashes=12+7 location=embedded
```

