APT37 / ScarCruft — NarwhalRAT (Fake Microsoft Alert → LNK → Batch → Compiled-Python RAT)
Executive Summary
NarwhalRAT (Genians Security Center, June 16 2026) is an APT37 / ScarCruft (a.k.a. Reaper, Group123) espionage campaign that opens with spear-phishing emails impersonating Microsoft account-security and one-time-password (OTP) abuse alerts. The lure is engineered to alarm the recipient — "suspicious sign-in", "your OTP was abused" — into opening the attachment: a ZIP archive disguised as a Microsoft security advisory containing a malicious LNK file. Opening the LNK launches a multi-stage, largely in-memory infection chain: the shortcut spawns cmd.exe / PowerShell, drops and runs env-var-obfuscated batch scripts (e.g. KHjWFcsE.bat) that abuse a copied curl.exe to download the next stage and display a decoy HWP document.
The final payload, NarwhalRAT, is a compiled-Python RAT (Python bytecode disguised as a Windows catalog file — config.cat / AccountConfig.cat) executed via a renamed pythonw.exe (userscreen.exe). It reflectively maps and runs shellcode in RWX memory and supports keylogging (with active-window context), periodic screen capture, microphone/audio recording, USB/removable-media collection, and remote command execution. Persistence is a scheduled task with a legitimate-sounding name (MicrosoftUserInterfacePicturesUpdateTackMachine) firing at one-minute intervals from C:\Users\Public\AccountPictures\. C2 is a dead-drop resolver that fronts on compromised legitimate Korean websites and uses the pCloud API as a secondary channel, with staged collection to a hidden %APPDATA%\naverwhale working directory.
Because the chain is fileless after the LNK and C2 hides behind legitimate infrastructure, detection centers on the LNK-from-archive execution lineage, the batch/env-var-obfuscation + curl-copy staging, the renamed-pythonw / compiled-Python loader behavior, the capture (keylog/screenshot/audio) and naverwhale staging artifacts, the scheduled-task persistence, and egress to pCloud / dead-drop domains.
cmd/powershell) and Q2 (env-var-obfuscated batch + copied curl.exe download) first — those break the chain before NarwhalRAT ever loads. Then chase Q3 (renamed pythonw.exe / compiled-Python loader), Q6 (scheduled-task persistence), and Q7 (pCloud / dead-drop egress). Coach users that a "security alert" telling them to open an attached advisory is itself the attack.Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry |
|---|---|---|---|
| 1 · Vendor research | Genians Security Center — NarwhalRAT | Full chain + IOCs: ZIP→LNK→batch (env-var obfuscation, curl copy)→compiled-Python RAT; renamed pythonw; scheduled-task persistence; pCloud / Korean-site dead-drop C2; keylog/screenshot/audio/USB capture | yes |
| 2 · News / analysis | The Hacker News — fake Microsoft alerts deploy NarwhalRAT | Phishing pretext: fake MS account-security / OTP-abuse alerts; ZIP disguised as MS advisory; APT37 attribution | yes |
| 2 · News / analysis | GBHackers — APT37 use NarwhalRAT malware | Multi-stage in-memory chain; compiled-Python RAT capabilities; DPRK targeting of South Korea | partial |
| 2 · News / analysis | SC World / SC Media — NK hackers use fake MS alerts | Corroborates lure theme, attribution, dead-drop-resolver C2 over legitimate websites + cloud storage | partial |
Decisions: front-door-first (LNK-from-archive lineage and the batch/curl staging are the cheapest, highest-leverage breaks); behavior-anchored across the fileless chain (env-var-obfuscated batch, renamed pythonw / compiled-Python, RWX in-memory exec); named artifacts (userscreen.exe, config.cat, naverwhale, the scheduled-task name, pCloud) give specific hunts; the listed MD5s and domains/IPs are durable-but-rotatable atoms, so chain behavior is the resilient surface. Author handle: HuntPack. No victim names — rephrased around TTPs.
Hunt Brief & Attack Chain
Working hypothesis: A user receives a fake Microsoft security-alert / OTP-abuse email, opens a ZIP disguised as a Microsoft advisory, and runs the LNK inside. The LNK spawns cmd/PowerShell, which writes and runs env-var-obfuscated batch scripts; those copy curl.exe to stage a compiled-Python payload, display a decoy HWP, install a one-minute scheduled task, and launch NarwhalRAT (renamed pythonw.exe) for keylog/screenshot/audio/USB collection and dead-drop / pCloud C2.
| Step | Behavior | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 · Phish | Fake MS account-security / OTP-abuse alert email; ZIP attachment disguised as advisory | Mail gateway; ZIP write | N1; user coaching |
| 2 · LNK exec | LNK in archive spawns cmd/PowerShell from Explorer / archive tool | ProcessRollup2 | Q1; N2 |
| 3 · Batch staging | Env-var-obfuscated .bat; copied curl.exe downloads payload; decoy HWP shown | ProcessRollup2; file write | Q2 |
| 4 · Python loader | Renamed pythonw.exe (userscreen.exe) runs .cat bytecode; RWX in-memory exec | ProcessRollup2 | Q3 |
| 5 · Collection | Keylog (active-window), screenshots, mic/audio, USB; stage to %APPDATA%\naverwhale | file write; ProcessRollup2 | Q4 |
| 6 · Persistence | Scheduled task MicrosoftUserInterfacePicturesUpdateTackMachine, 1-min interval | ScheduledTask; reg | Q6; N3 |
| 7 · C2 / exfil | Dead-drop resolver over Korean sites + pCloud API; staged exfil | NetworkConnectIP4; DNS | Q7, Q8; N4 |
Consolidated IOC Table
| Type | Value | Conf | Action | Context |
|---|---|---|---|---|
| Behavior | Archive/Explorer → LNK → cmd/powershell lineage | high | detect | Front door — flagship |
| Behavior | Env-var-obfuscated .bat (%var:~n,1%) + copied curl.exe download | high | detect | Stage 3 staging |
| Behavior | Renamed pythonw.exe (userscreen.exe) running .cat bytecode | medium | hunt | Compiled-Python loader |
| File | userscreen.exe ; config.cat ; AccountConfig.cat | medium | detect | Renamed pythonw + bytecode |
| File | KHjWFcsE.bat ; GuFLjO7q.bat | medium | hunt | Obfuscated batch droppers |
| Path | %APPDATA%\naverwhale (Hidden+System) ; C:\Users\Public\AccountPictures\UserInerfacePicture\ | medium | hunt | Staging + persistence dir |
| Persistence | Scheduled task: MicrosoftUserInterfacePicturesUpdateTackMachine (1-min) | high | detect | NarwhalRAT relaunch |
| Mutex | i5zJH9FL10cVd3sSW9eyWWErPJ | medium | hunt | RAT single-instance |
| Network | api.pcloud.com (dead-drop secondary C2) | medium | hunt | Cloud-storage C2 |
| Network | daehoat.com ; novel21.co.kr ; webhostingkorea.com ; crwellfood.com ; fe01.co.kr | medium | detect | Dead-drop resolver fronts |
| Network | 121.254.222.10 ; 121.254.222.80 ; 211.239.157.126 ; 218.150.78.198 ; 218.150.78.231 ; 61.100.9.206 | medium | detect | C2 IPs |
| Hash (MD5) | 3715092aa00f380cefe8b4d2eddb7d08 ; 7cef19f9c4480adac0cd4702ff98f46c ; 7eb9cee1f696727752169f25cf79a338 ; b6b0602310bb2d4360c52685119aac1b | medium | detect | Known samples — block |
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Workstations (users) | Process lineage (archive/Explorer → LNK → cmd/ps) | Critical | Low — process telemetry |
| Mail / web gateway | Attachment ZIP + phishing-pretext detection | High | Medium — lure delivery |
| Command-line / scripting | cmd/batch + PowerShell script-block (4104); curl copy | High | Medium — logging gap |
| Python loader | Process metadata (OriginalFileName vs FileName); file writes (.cat) | Medium | Medium — renamed binary |
| Persistence | Scheduled-task creation + registry telemetry | High | Low — task events |
| Egress (C2/exfil) | DNS + netflow to cloud-storage / dead-drop fronts | Medium | Medium — legit infra |
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Query / Control |
|---|---|---|---|
| Initial Access | T1566.001 — Spearphishing Attachment | Fake MS alert email; ZIP disguised as advisory | N1; coaching |
| Execution | T1204.002 / T1059.001 / T1059.003 — User exec / PowerShell / cmd | LNK launches cmd/PowerShell; batch scripts run | Q1, Q2; N2 |
| Defense Evasion | T1027 / T1140 — Obfuscated files / deobfuscate | Env-var %var:~n,1% command reconstruction | Q2 |
| Defense Evasion | T1036.003 / T1036.008 — Masquerading (rename / extension) | pythonw → userscreen.exe; .pyc → .cat; legit task name | Q3, Q5, Q6 |
| Command & Control | T1105 — Ingress tool transfer | Copied curl.exe downloads next stage | Q2 |
| Execution / Defense Evasion | T1620 / T1106 — Reflective load / native API | RWX VirtualAlloc → RtlMoveMemory → CFUNCTYPE exec | Q3 |
| Collection | T1056.001 / T1113 / T1123 / T1025 — Keylog / screen / audio / removable | Capture + stage to naverwhale | Q4 |
| Persistence | T1053.005 — Scheduled Task | 1-min task MicrosoftUserInterface... | Q6; N3 |
| Command & Control | T1102 / T1071.001 — Web service / web C2 | Dead-drop resolver over Korean sites + pCloud API | Q7, Q8; N4 |
| Defense Evasion | T1497.001 — Virtualization / sandbox checks | CPUID anti-VM (VMware/VBox/Parallels) | Q3 (context) |
Native Audit-Log Hunts
| Hunt | Source | Logic | Response |
|---|---|---|---|
| N1 · Phish attachment | Mail gateway / EOP | Inbound mail w/ ZIP attachment + MS-security / OTP-abuse pretext; sender spoof of Microsoft | Quarantine; coach users |
| N2 · LNK / batch exec | Sysmon 1 / Security 4688 | explorer.exe or archive tool → cmd/powershell; child .bat with env-var substring tricks | Isolate; pull tree |
| N3 · Scheduled task | Security 4698 / TaskScheduler Op | Task created with MS-lookalike name running from C:\Users\Public\AccountPictures\ at 1-min interval | Disable task; triage host |
| N4 · Cloud / dead-drop egress | Proxy / DNS / netflow | Connections to api.pcloud.com or the dead-drop Korean fronts from a non-browser binary | Block; identify host |
CrowdStrike LogScale CQL Hunt Queries
SyntheticProcessRollup2. The chain is fileless after the LNK — Q1/Q2 (archive→LNK→batch + curl staging) are the cheapest, highest-yield breaks. Pair the in-memory hunts with PowerShell script-block logging (4104) and scheduled-task telemetry.Looks for: Explorer or an archive tool (WinRAR/7-Zip/Windows Zip handler) spawning cmd/powershell with download / hidden / batch indicators — the LNK-inside-ZIP launch. Accomplishes: breaks the front door before staging. FP: low-med — some legit installers; flag hidden/encoded/curl/.bat tells.
// HUNT: ZIP->LNK launch -> cmd/powershell staging lineage // MITRE: T1204.002, T1059.001, T1059.003 | CONF: high FP: low-med COST: low // REQUIRES: ProcessRollup2 process telemetry // FALSE POSITIVES: legit installers/IT scripts launched from Explorer -- flag hidden/encoded/curl/.bat tells #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^(explorer|7zFM|7zG|winrar|rar|opener|isoburn)\.exe$/i | FileName=/^(cmd|powershell|pwsh|mshta|wscript|cscript)\.exe$/i | CommandLine=/(-w\s?hidden|-enc|FromBase64|IEX|Invoke-WebRequest|curl|\.bat|\.lnk|ExecutionPolicy\s+Bypass)/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
Looks for: the env-var substring-substitution obfuscation (%x:~n,1%) used by the batch droppers, and curl downloading to a user/temp path or running from a non-standard copy. Accomplishes: catches stage-3 staging. FP: low — substring-substitution command building is rare in benign scripts.
// HUNT: env-var substring obfuscation + curl ingress download // MITRE: T1027, T1140, T1105, T1059.003 | CONF: high FP: low COST: low // REQUIRES: ProcessRollup2 with full CommandLine // FALSE POSITIVES: rare; some packagers build strings via %var:~%; confirm parent is cmd/.bat #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | (CommandLine=/%[A-Za-z0-9_]+:~\d+,1%/ or (FileName=/^curl\.exe$/i and CommandLine=/(-o|--output|-O)\b/i and CommandLine=/https?:/i)) | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
Looks for: a process whose on-disk name differs from its PE OriginalFileName of python/pythonw (the userscreen.exe rename), or a Python interpreter loading a .cat/.pyc from a user path. Accomplishes: catches the compiled-Python RAT loader. FP: low-med — baseline sanctioned Python apps.
// HUNT: renamed pythonw (userscreen.exe) / compiled-Python .cat loader // MITRE: T1036.003, T1059.006, T1620 | CONF: med-high FP: low-med COST: low // REQUIRES: ProcessRollup2 with OriginalFileName (PE VersionInfo) // FALSE POSITIVES: sanctioned packaged Python apps -- allow-list signer/path #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ((OriginalFileName=/^pythonw?\.exe$/i and FileName!=/^pythonw?\.exe$/i) or FileName=/^userscreen\.exe$/i or CommandLine=/\\(config|AccountConfig)\.cat\b/i or CommandLine=/\.cat["'\s]/i and ImageFileName=/python/i) | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, OriginalFileName, ImageFileName, CommandLine], limit=200)
Looks for: file writes into the hidden %APPDATA%\naverwhale staging directory or the AccountPictures\UserInerfacePicture persistence path. Accomplishes: surfaces keylog/screenshot/audio staging. FP: medium — narrow on the specific dir names; correlate to Q3 on the same host.
FP note: file-write hunts can be noisy — anchor on the literal naverwhale / UserInerfacePicture directory names (note the misspelling) and require co-occurrence with a Q3 hit on the host.
// HUNT: NarwhalRAT staging/persistence directory writes // MITRE: T1056.001, T1113, T1123, T1025 | CONF: medium FP: medium COST: low // REQUIRES: file-write telemetry (FileCreateInfo / PeFileWritten) // FALSE POSITIVES: anchor on literal naverwhale/UserInerfacePicture dir names; correlate to Q3 #event_simpleName=/PeFileWritten|NewExecutableWritten/ | TargetFileName=/\\(naverwhale|UserInerfacePicture)\\/i | table([@timestamp, ComputerName, UserName, ImageFileName, TargetFileName], limit=200)
Looks for: execution or write of any reported NarwhalRAT MD5. Accomplishes: a high-fidelity known-sample sweep. FP: low — exact-hash match. Hashes rotate, so pair with the behavioral hunts.
// HUNT: known NarwhalRAT sample MD5s // MITRE: T1204.002 | CONF: high FP: low COST: low // REQUIRES: MD5HashData on process/file events // FALSE POSITIVES: none on exact match; hashes rotate -- keep behavioral hunts on #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2|PeFileWritten/ | MD5HashData=/^(3715092aa00f380cefe8b4d2eddb7d08|7cef19f9c4480adac0cd4702ff98f46c|7eb9cee1f696727752169f25cf79a338|b6b0602310bb2d4360c52685119aac1b)$/i | table([@timestamp, ComputerName, UserName, FileName, ImageFileName, MD5HashData], limit=200)
Looks for: a scheduled task created with the MS-lookalike name, or any task whose action runs from C:\Users\Public\AccountPictures\ — the NarwhalRAT relaunch. Accomplishes: a high-fidelity persistence signal. FP: low — no legit task runs from that user path.
// HUNT: NarwhalRAT scheduled-task persistence // MITRE: T1053.005, T1036.005 | CONF: high FP: low COST: low // REQUIRES: ScheduledTask telemetry or schtasks ProcessRollup2 // FALSE POSITIVES: low -- no benign task runs from Users\Public\AccountPictures #event_simpleName=/ScheduledTaskRegistered|ScheduledTaskModified|ProcessRollup2|SyntheticProcessRollup2/ | (TaskName=/MicrosoftUserInterfacePicturesUpdate/i or TaskExecArguments=/Users\\Public\\AccountPictures/i or (FileName=/^schtasks\.exe$/i and CommandLine=/(\/create).*(Users\\Public\\AccountPictures|userscreen\.exe)/i)) | table([@timestamp, ComputerName, UserName, TaskName, TaskExecArguments, CommandLine], limit=200)
Looks for: DNS / connections to pcloud infrastructure made by a non-browser, user/temp-path binary (NarwhalRAT abusing the pCloud API as C2). Accomplishes: surfaces the cloud dead-drop channel. FP: medium — baseline sanctioned pCloud desktop clients; flag python/userscreen parents.
FP note: orgs that legitimately use pCloud will hit this — allow-list the official pCloud client by signer/path and require a non-browser, user-path image (e.g. userscreen.exe / a renamed pythonw).
// HUNT: pCloud API used as dead-drop C2 by non-browser binary // MITRE: T1102, T1567.002 | CONF: medium FP: medium COST: med // REQUIRES: DnsRequest and/or NetworkConnectIP4 telemetry // FALSE POSITIVES: legit pCloud desktop client -- allow-list signer/path; flag user-path images #event_simpleName=/DnsRequest|NetworkConnectIP4/ | (DomainName=/(^|\.)pcloud\.com$/i or DomainName=/api\.pcloud\.com$/i) | ContextImageFileName=/\\(Temp|AppData|ProgramData|Users\\Public)\\/i | ContextImageFileName!=/\\(chrome|msedge|firefox|brave|pcloud)\.exe$/i | table([@timestamp, ComputerName, UserName, ContextImageFileName, DomainName, RemoteAddressIP4], limit=200)
Looks for: DNS / connections to the reported dead-drop front domains or hardcoded C2 IPs. Accomplishes: a targeted IOC sweep for the C2 channel. FP: low-med — these are reported-malicious atoms; some fronts are compromised legit sites, so confirm host context.
// HUNT: NarwhalRAT dead-drop resolver fronts + C2 IPs // MITRE: T1071.001, T1102 | CONF: high FP: low-med COST: low // REQUIRES: DnsRequest and/or NetworkConnectIP4 telemetry // FALSE POSITIVES: fronts are compromised legit sites -- confirm initiating process context #event_simpleName=/DnsRequest|NetworkConnectIP4/ | (DomainName=/(^|\.)(daehoat\.com|novel21\.co\.kr|webhostingkorea\.com|crwellfood\.com|fe01\.co\.kr)$/i or RemoteAddressIP4=/^(121\.254\.222\.(10|80)|211\.239\.157\.126|218\.150\.78\.(198|231)|61\.100\.9\.206)$/) | table([@timestamp, ComputerName, UserName, ContextImageFileName, DomainName, RemoteAddressIP4], limit=200)
CrowdStrike Custom IOA Recommendations
| IOA Name | Field Patterns | Benign Exclusions | Deployment Path |
|---|---|---|---|
| NarwhalRAT — archive→LNK→shell | Parent explorer/7z/winrar.exe; Child cmd/powershell + .bat/curl/hidden cradle | Sanctioned installers / IT scripts (allow-list) | Endpoint Security → Custom IOA → Process Creation, Detect+Block |
| NarwhalRAT — env-var batch obfuscation | CommandLine matches %var:~n,1% substring substitution | Vetted packaging scripts | Custom IOA → Process Creation, Detect+Block |
| NarwhalRAT — scheduled-task persistence | schtasks /create with Users\Public\AccountPictures or MS-lookalike task name | None expected from that path | Custom IOA → Process Creation, Detect+Block |
| NarwhalRAT — renamed pythonw loader | OriginalFileName=pythonw.exe & on-disk name differs; or userscreen.exe | Sanctioned packaged Python apps (signer allow-list) | Custom IOA → Process Creation, Detect |
Machine-Readable IOC Appendix
Grouped IOC Quick-Copy
One-click blocks for detection / blocklisting. The chain is fileless after the LNK and C2 fronts on legitimate infra — behavior is the durable surface, atoms rotate. Add per-incident atoms during triage.
Explorer/archive tool (7z/winrar) spawning cmd/powershell from a ZIP-extracted LNK Env-var substring obfuscation in batch: %var:~n,1% command reconstruction Copied curl.exe downloading payload to a user/temp path (ingress tool transfer) Renamed pythonw.exe (OriginalFileName=pythonw.exe, on-disk userscreen.exe) running .cat bytecode RWX in-memory exec: VirtualAlloc(PAGE_EXECUTE_READWRITE) -> RtlMoveMemory -> ctypes CFUNCTYPE File writes into %APPDATA%\naverwhale (Hidden+System) staging dir Scheduled task w/ MS-lookalike name running from C:\Users\Public\AccountPictures at 1-min interval Non-browser user-path binary resolving/connecting to api.pcloud.com or Korean dead-drop fronts
Cybersecurity Advisory Notice (Regarding One-Time Password Abuse).zip # lure archive Cybersecurity Advisory Notice (Regarding One-Time Password Abuse).lnk # malicious shortcut Cybersecurity Advisory Notice (Regarding One-Time Password Abuse).hwp # decoy doc KHjWFcsE.bat ; GuFLjO7q.bat # env-var-obfuscated batch droppers curl.exe # copied/abused legit downloader config.cat ; AccountConfig.cat # Python bytecode disguised as Windows catalog userscreen.exe # renamed pythonw.exe (NarwhalRAT loader) Mutex: i5zJH9FL10cVd3sSW9eyWWErPJ Scheduled task: MicrosoftUserInterfacePicturesUpdateTackMachine (1-min) Staging dir: %APPDATA%\naverwhale (Hidden+System) Persistence dir: C:\Users\Public\AccountPictures\UserInerfacePicture\ Config: %LOCALAPPDATA%\Microsoft\Internet Explorer\<random>.ent (AES-128)
api.pcloud.com # secondary dead-drop C2 (cloud storage API) daehoat.com novel21.co.kr webhostingkorea.com crwellfood.com fe01.co.kr 121.254.222.10 121.254.222.80 211.239.157.126 218.150.78.198 218.150.78.231 61.100.9.206
3715092aa00f380cefe8b4d2eddb7d08 7cef19f9c4480adac0cd4702ff98f46c 7eb9cee1f696727752169f25cf79a338 b6b0602310bb2d4360c52685119aac1b
Email impersonating a Microsoft "account security" / "OTP abuse" alert urging you to open an attachment A ZIP attachment described as a "security advisory" containing a shortcut (.lnk) file A document that opens but "nothing else happens" (decoy HWP while malware runs in background) # Rule for users: a real security alert never ships its "advisory" as a ZIP you must open. Report, don't open.
User awareness: a "MS security alert" attaching a ZIP advisory = phish -- report, do not open Block/strip .lnk inside inbound archives at the mail gateway; quarantine MS-spoof OTP lures ASR: block executable content from email/webmail; block obfuscated scripts; block Office child procs PowerShell Constrained Language Mode + script-block/module logging (4104) fleet-wide WDAC/app control: deny user-path execution of pythonw/renamed interpreters and unsigned .exe Audit scheduled-task creation (4698); alert on tasks running from Users\Public Egress: baseline + flag non-browser user-path binaries reaching api.pcloud.com / dead-drop fronts
Detection Validation Gates
- Telemetry confirmation: verify process lineage (parent/child +
OriginalFileName), file-write, scheduled-task (4698 / ScheduledTaskRegistered), DNS/netflow, and PowerShell script-block (4104) are all flowing from the user fleet. - Baseline: enumerate any sanctioned packaged-Python apps, the official pCloud desktop client (signer/path), and legit IT scripts launched from Explorer — allow-list before promoting Q1/Q3/Q7.
- Benign lab test: launch a benign
explorer → powershell(Q1), a script using%var:~n,1%(Q2), a renamed copy ofpythonw.exe(Q3), and aschtasks /createfromUsers\Public(Q6) to confirm the queries fire and to measure FP volume. - Promote: enable script-block logging + scheduled-task auditing, then promote Q1/Q2/Q6 to Detect+Block IOAs. Break Q1/Q2 and NarwhalRAT never loads.
- IOC sweep: run Q5 (hashes) and Q8 (domains/IPs) as a one-time retro-hunt over 30-90 days, then feed the atoms to threat-intel blocklists (atoms rotate — keep behavioral hunts live).
Hardening — Tiered & Deployable
Break the phishing front door, neutralize the LNK/batch staging, constrain the Python loader, and surface persistence + egress.
- User awareness: a "Microsoft security/OTP alert" attaching a ZIP advisory is the attack — M1017. Report, don't open — the cheapest control.
- Strip/block
.lnk(and nested.lnkinside archives) at the mail gateway; quarantine MS-spoof OTP-abuse lures — M1031 / M1054. - ASR: block executable content from email/webmail, block obfuscated scripts, block Office/Outlook child processes — M1042. Blunts the LNK→cmd→batch chain.
- Promote Q1/Q2/Q6 to Detect+Block IOAs — M1040.
- PowerShell Constrained Language Mode + script-block/module logging (4104) fleet-wide — M1038 / M1047. Feeds N2 and the in-memory hunts.
- WDAC / app control: deny user-path execution of
pythonw/ renamed interpreters and unsigned EXEs — M1038. Kills theuserscreen.exeloader. - Enable scheduled-task creation auditing (4698); alert on tasks running from
Users\Public— M1047. - Egress control: baseline + flag non-browser user-path binaries reaching
api.pcloud.com/ dead-drop fronts — M1037.
- Application allowlisting; default-deny user-path execution — M1038.
- Disable / restrict removable-media auto-collection; DLP on staging dirs — M1057 / M1034. Blunts USB collection + naverwhale staging.
- Block unsanctioned cloud-storage egress (pCloud) at the proxy; phishing-resistant MFA — M1021 / M1032. Cuts the dead-drop C2 channel and limits credential reuse.
Deployable Playbooks
A · Block .lnk in inbound archives (Exchange Online / EOP transport rule — concept)
Transport rule: if attachment is/contains an archive (.zip/.7z/.rar) AND archive contains *.lnk
-> Quarantine + notify SOC.
Common Attachment Types Filter: add 'lnk' to the blocked list (anti-malware policy).
Pretext rule: subject/body matches ("account security"|"one-time password"|"OTP")
AND sender fails DMARC for microsoft.com -> Quarantine.
B · ASR rules (GPO / Intune — set to Block)
Block executable content from email client and webmail : BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550 = 1 Block all Office applications from creating child procs : D4F940AB-401B-4EFC-AADC-AD5F3C50688A = 1 Block execution of potentially obfuscated scripts : 5BEB7EFE-FD9A-4556-801D-275E5FFC04CC = 1 Block Win32 API calls from Office macros : 92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B = 1 # Deploy: Defender > ASR (Block); pilot in Audit (=2) first, then Block (=1).
C · Scheduled-task persistence sweep (PowerShell — read-only, defensive)
Get-ScheduledTask | Where-Object {
$_.TaskName -match 'MicrosoftUserInterfacePicturesUpdate' -or
($_.Actions.Execute -match 'Users\\Public\\AccountPictures|userscreen\.exe')
} | Select-Object TaskName, State, @{n='Action';e={$_.Actions.Execute}}
# Review hits; disable confirmed-malicious tasks via Disable-ScheduledTask after IR sign-off.
D · PowerShell logging + Constrained Language Mode (GPO)
Computer Config > Admin Templates > Windows Components > Windows PowerShell: Turn on Module Logging = Enabled (Module Names: *) Turn on PowerShell Script Block Logging = Enabled System-wide env (machine-tier policy): __PSLockdownPolicy / WDAC enforces ConstrainedLanguage. # Forward 4103/4104 to SIEM; feeds Native hunt N2.
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain the host; capture memory before reboot (chain is in-memory after the LNK); preserve PowerShell logs, the .bat/.cat artifacts, %APPDATA%\naverwhale, and the scheduled task | SOC L2 | Containment timestamp; Q1/Q3 events; memory image |
| Triage | Map ZIP→LNK→batch→pythonw→NarwhalRAT; confirm scheduled-task persistence; identify C2 (pCloud / dead-drop fronts); assess scope of keylog/screenshot/audio/USB collection | IR | Process tree; task export; C2 list; staged-data inventory |
| Eradicate | Disable + delete the scheduled task; kill userscreen.exe; remove naverwhale + AccountPictures\UserInerfacePicture; block C2 domains/IPs + pCloud egress | IR | Removal log; blocklist entries |
| Recover | Reimage if RAT collection occurred (assume keystroke/credential capture); rotate any credentials typed on the host; restore from clean backup | IR + IT | Rebuild ticket; cred-reset log |
| Harden | Promote Q1/Q2/Q6; strip .lnk in archives at the gateway; ASR; CLM + script-block logging; WDAC for renamed interpreters; user coaching on MS-alert phish | Detection Eng | IOA enabled; transport rule live; logging on |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1566.001 | Fake MS-alert phish + ZIP | — | — | GAP mail gateway (N1) + awareness |
| T1204.002 / T1059 | Archive→LNK→cmd/ps | Q1 | IOA-1 | Good + N2 |
| T1027 / T1140 / T1105 | Env-var batch + curl download | Q2 | IOA-2 | Good |
| T1036.003 / T1620 | Renamed pythonw / compiled-Python | Q3 | IOA-4 | Partial correlate to Q1/Q2 |
| T1056/T1113/T1123/T1025 | Keylog/screenshot/audio/USB staging | Q4 | — | Partial dir-name anchored |
| Known samples | NarwhalRAT MD5s | Q5 | — | Good (rotates) |
| T1053.005 | Scheduled-task persistence | Q6 | IOA-3 | Good + N3 |
| T1102 / T1567.002 | pCloud dead-drop C2 | Q7 | — | Partial + proxy (N4) |
| T1071.001 / T1102 | Korean dead-drop fronts + C2 IPs | Q8 | — | Good (rotates) + N4 |
Hunt Summary Ticket
TITLE: Hunt — APT37/ScarCruft NarwhalRAT (fake MS alert -> ZIP/LNK -> batch -> compiled-Python RAT)
SEVERITY: High (DPRK-nexus espionage; keylog/screenshot/audio/USB collection)
SCOPE: User workstations; mail gateway; PowerShell logging; scheduled tasks; egress
HYPOTHESIS: Fake Microsoft security/OTP-abuse email -> ZIP "advisory" -> LNK -> cmd/powershell ->
env-var-obfuscated .bat + copied curl download -> renamed pythonw (userscreen.exe)
-> NarwhalRAT (compiled-Python). Persistence = 1-min scheduled task from Users\Public;
C2 = dead-drop resolver over Korean sites + pCloud API; staging to %APPDATA%\naverwhale.
QUERIES RUN: Q1 archive->LNK->shell (IOA) | Q2 env-var batch + curl (IOA) | Q3 renamed pythonw |
Q4 naverwhale staging | Q5 known MD5s | Q6 scheduled-task persistence (IOA) |
Q7 pCloud dead-drop | Q8 Korean fronts + C2 IPs + Native N1-N4
DO FIRST: User coaching + Q1/Q2 -- break the LNK/batch staging before NarwhalRAT loads
FINDINGS: <pending analyst execution>
GAPS: Web/mail delivery of the lure (gateway + awareness); fileless after LNK (needs memory/4104)
ACTIONS: Promote Q1/Q2/Q6; strip .lnk in archives at gateway; ASR; CLM + script-block logging;
WDAC for renamed interpreters; audit 4698; egress baselining for pCloud / dead-drop fronts
OWNER: HuntPack
VERSION: v0.1 - 2026-06-17
Changelog
table() calls now carry an explicit row limit: the default is 200 and truncation is silent, so a capped result was indistinguishable from a complete one. Atomic-IOC sweeps (filters over 5+ hashes or C2 IPs) use limit=max so a wide infection is never silently under-scoped; behavioural hunts use limit=200, where exceeding the cap indicates the query needs tuning. Where present, event names that do not exist in the Falcon data model were corrected (e.g. ServiceInstalled is a Sysmon concept, not a Falcon event; ElfFileWritten is ELFFileWritten) — such queries could never return a row. No detection logic, fields, or IOCs changed.References
| Tier | Source | Used For |
|---|---|---|
| 1 | Genians Security Center — NarwhalRAT | Full chain + IOCs: ZIP/LNK/batch, renamed pythonw, compiled-Python RAT, scheduled-task persistence, dead-drop/pCloud C2, capture commands, hashes |
| 2 | The Hacker News — Fake Microsoft alerts deploy NarwhalRAT | Phishing pretext (fake MS / OTP-abuse alerts); ZIP-as-advisory; APT37 attribution |
| 2 | GBHackers — APT37 use NarwhalRAT malware | Multi-stage in-memory chain; RAT capabilities; DPRK targeting of South Korea |
| 2 | SC World — NK hackers use fake Microsoft alerts to deploy NarwhalRAT | Corroborates lure, attribution, dead-drop-resolver C2 over legit sites + cloud storage |
| 2 | MITRE ATT&CK G0067 — APT37 (Reaper) | Actor mapping / technique attribution |
HuntPack v0.1 · APT37 / ScarCruft NarwhalRAT (fake MS alert → ZIP/LNK → batch → compiled-Python RAT) · Generated 2026-06-17 · Defensive use only — no offensive code. The chain rides on a phishing-driven LNK; breaking the LNK/batch staging (Q1/Q2 + mail-gateway .lnk stripping + user coaching) is the highest-leverage control. The remaining stages are fileless — pair the CQL hunts with PowerShell script-block logging and scheduled-task auditing. C2 fronts on legitimate infrastructure and atoms rotate — validate field names and keep the behavioral hunts live before promoting any query.