TonRAT / "Photo-ZIP" Hospitality Campaign
Executive Summary
Microsoft (2026-06-25) reported a campaign — active since April 2026 and not yet attributed to a known actor — that targets the hospitality sector (front-desk, reception, reservations staff at hotels) in Europe and Asia. The intrusion is themed around guest photos and complaints: victims download a photo-themed ZIP (photo-<random>.zip) that contains a fake image shortcut (IMG-*.png.lnk / PHOTO-*.png.lnk). Opening the LNK launches powershell.exe -ep bypass running an obfuscated BigInt-arithmetic decoder, which pulls a .ps1 to %TEMP%, drops a legitimate Node.js runtime into AppData\Local\Nodejs, and runs node.exe with a JavaScript implant tracked as TonRAT.
TonRAT's distinguishing trait is C2 resolution via the TON blockchain API — it reads attacker-controlled domains from on-chain records, then opens an encrypted WebSocket on non-standard ports (8443, 8445, 8453, 5555, 56001–56003). Persistence is dual: Run (Node.js component) and RunOnce (ProgramData EXE). The actor laundered sender authentication through abused Calendly + a Google share redirect chain so phishing passes SPF/DKIM/DMARC/CompAuth.
The strongest defensive angle is endpoint behavior: a signed-but-living-off-the-land chain (LNK → PowerShell → iwr → node.exe from a non-standard path) plus Run/RunOnce writes and a Defender exclusion add. Falcon EDR covers this well; this pack leans into process-lineage and registry CQL hunts rather than network signatures alone, because the blockchain-resolved C2 rotates.
Defender priority: Hunt for node.exe executing from C:\Users\*\AppData\Local\Nodejs\*.js with a domain argument — this is the implant launch and the single highest-fidelity signal in the chain. Pair it with the LNK→PowerShell→iwr ancestry hunt to catch the delivery stage.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Fwd |
|---|---|---|---|
| 1 · Primary | Microsoft Security Blog (2026-06-25) | Full kill chain, TonRAT implant, TON-blockchain C2, dual persistence, IOCs (hashes, IPs, domains, ports), auth-laundering chain, lure languages/targets. | Yes |
| 2 · Corroborating | The Hacker News | Independent write-up of the Microsoft report; confirms hospitality targeting, Node.js implant, Calendly/Google-share laundering. | Partial |
Hunter notes: Microsoft did not attribute the campaign to a named actor. Two waves are described: Wave 1 (IMG-*.png.lnk, fixed-IP C2, .info domains) and Wave 2 (PHOTO-*.png.lnk, Cloudflare-fronted photo-*.cfd, csc.exe→cvtres.exe compile-after-delivery). All IOCs in this pack trace to the Microsoft primary source. Blockchain-resolved C2 means domain/IP IOCs are perishable — prioritize behavioral hunts.
Hunt Brief & Attack Chain
Hunt hypotheses (high-fidelity first)
- H1 — Implant launch:
node.exeruns fromAppData\Local\Nodejs\*.jswith a domain argument. CONF HIGH Covers T1059.007. RequiresProcessRollup2. - H2 — LNK delivery: A
.lnkspawnspowershell.exe -ep bypasswhose command line containsiwr/Invoke-WebRequest+-OutFile. CONF HIGH T1059.001 / T1204.002. - H3 — PS1 drop to TEMP: PowerShell writes a randomly-named
.ps1into%TEMP%. CONF MED T1059.001 / T1105. - H4 — Run/RunOnce persistence: Registry value write under
...\CurrentVersion\RunorRunOncepointing at a Node.js component or aProgramData\<random>\*.exe. CONF HIGH T1547.001. - H5 — Defender exclusion abuse:
Add-MpPreference -ExclusionProcesstargeting a%TEMP%\<random>.exe. CONF HIGH T1562.001. - H6 — Compile-after-delivery (Wave 2):
csc.exe→cvtres.exeproducing a.dll, parented from the PowerShell/Node chain. CONF MED T1027.004. - H7 — Non-standard-port C2 + geo lookup: Outbound to ports 8443/8445/8453/5555/56001–56003 or to
ip-api.com, fromnode.exe/ dropped EXE. CONF MED T1571 / T1016. - H8 — Known-bad atomic IOCs: Listed SHA256 hashes, Wave-1 C2 IPs, and campaign domains. CONF HIGH (perishable for net IOCs).
Attack chain
| # | Step | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 | Phishing (lure: guest complaint / bedbug / verification call) → browser downloads photo-*.zip | Mail gateway, proxy, FileCreateInfo | ZIP with photo-* name + LNK content; auth-laundering sender |
| 2 | User opens IMG-*.png.lnk / PHOTO-*.png.lnk (1,989–2,079 B) | ProcessRollup2 | LNK parent of powershell.exe — H2 |
| 3 | PowerShell -ep bypass BigInt decoder → iwr -OutFile %TEMP%\<rand>.ps1 | ProcessRollup2, FileCreateInfo | CmdLine iwr+OutFile; ps1 write to TEMP — H2/H3 |
| 4 | Drop legit Node.js runtime to AppData\Local\Nodejs; run node.exe <rand>.js <domain> | ProcessRollup2, PeFileWritten | node.exe from AppData + JS + domain arg — H1 |
| 5 | (Wave 2) csc.exe → cvtres.exe compile .dll | ProcessRollup2 | Compiler chain off the implant — H6 |
| 6 | Persistence: Run (Node) + RunOnce (ProgramData EXE) | RegGenericValueUpdate | ASEP value write — H4 |
| 7 | Evasion: Add-MpPreference -ExclusionProcess; Inno-Setup is-*.tmp /SL5 /VERYSILENT | ProcessRollup2 | Exclusion-add CmdLine — H5 |
| 8 | C2: resolve domains via TON blockchain API → encrypted WebSocket on non-standard ports; geo via ip-api.com | NetworkConnectIP4, DnsRequest | Odd ports / known IPs / ip-api.com — H7/H8 |
Consolidated IOC Table
Network IOCs are perishable — C2 is resolved on-chain and rotates; Wave-2 fronts behind Cloudflare. Treat IPs/domains as hunt/enrich, not permanent detect. All values below trace to the Microsoft primary source.
| Type | Value | Conf | Action | Context |
|---|---|---|---|---|
| SHA256 | 04ec44f2618460f5c77c5e56014a512cc03a123c9c5b6b6b1273e2a1681ac2e1 | high | detect | PE xmnrwv9l.exe |
| SHA256 | 9f10e3b6e5745784f26d18c38ce01fba054b19749c17260978ac11472564aee2 | high | detect | LNK IMG-386443483.png.lnk (Wave 1) |
| SHA256 | 97448688b292bfec6d83b153588076fe59b111c35ac4e42a916238df16a71e2f | high | detect | LNK PHOTO-215746435.png.lnk (Wave 2) |
| SHA256 | c5baa0c16b0074a1e94b48aa0177e9bfc23746aca8a5b42848a6685da85658b5 | high | detect | PS qFWe908J.ps1 |
| SHA256 | b7f46b192cd83a1d2487cb048cca645f6e8855b9673d500d50bbdb04eebc6bea | high | detect | DLL bjygtujc.dll (Wave 2 compile) |
| SHA256 | d14ba95cdce1ef7dc9ad3ac74949ca5db38b27378ee30f30a23cf26f9e875a11 | med | enrich | Legit node-v24.13.0-win-x64 (~89.9 MB) — benign binary, hunt on PATH not hash |
| IP | 178.16.54.27 | high | hunt | Wave 1 C2 |
| IP | 95.217.97.121 | high | hunt | Wave 1 C2 |
| IP | 193.202.84.32 | high | hunt | Wave 1 C2 |
| IP | 178.16.55.179 | high | hunt | Wave 1 C2 |
| IP | 208.95.112.1 | med | enrich | ip-api.com geolocation lookup (shared infra — context only) |
| Domain | safedocphoto[.]info | high | hunt | Wave 1 C2 domain |
| Domain | recallnine[.]info | high | hunt | Wave 1 C2 domain |
| Domain | kentjerk[.]info | high | hunt | Wave 1 C2 domain |
| Domain | photodoc-secure[.]info | high | hunt | Wave 1 C2 domain |
| Domain | sec-safe-dc[.]info | high | hunt | Wave 1 C2 domain |
| Domain | photo-26254[.]cfd | med | hunt | Wave 2 example (photo-<digits>.cfd, Cloudflare-fronted) |
| Domain pattern | photo-<digits>[.]cfd · TLDs .cfd / .click / .bond | med | hunt | Wave 2 C2 / Turnstile gate naming pattern |
| Ports | 8443, 8445, 8453, 5555, 56001, 56002, 56003 | med | hunt | Encrypted WebSocket C2 — non-standard |
| Auth-laundering | em1618.calendly.com · "Booking Manager (via Calendly)" | med | enrich | Abused Calendly sender; Google share redirect chain |
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Windows endpoints (front-desk / reception workstations) | Falcon ProcessRollup2, RegGenericValueUpdate, PeFileWritten | Critical | Low — well covered by Falcon |
| PowerShell / script host | ProcessRollup2 CommandLine; (native) PS ScriptBlock 4104 | High | Med — confirm ScriptBlock logging on |
| Outbound network | NetworkConnectIP4, DnsRequest | Med | Med — C2 rotates / Cloudflare-fronted |
| Email gateway (initial access) | Mail gw logs; SPF/DKIM/DMARC/CompAuth verdicts | Med | High — laundered auth passes; not in Falcon |
| Microsoft Defender AV config | Exclusion change events / Add-MpPreference CmdLine | High | Low |
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Query / Control |
|---|---|---|---|
| Initial Access | T1566.002 Phishing: Spearphishing Link | Browser download of photo-*.zip via Calendly/Google-share chain | Mail gw hunt · S7 |
| Initial Access | T1199 Trusted Relationship | Abused Calendly + Google share redirect to pass SPF/DKIM/DMARC | Mail gw hunt · S7 |
| Execution | T1059.001 PowerShell | powershell.exe -ep bypass BigInt decoder + iwr -OutFile | Q2, Q3 |
| Execution | T1059.007 JavaScript | node.exe runs JS implant (TonRAT) from AppData | Q1 |
| Execution | T1204.002 User Execution: Malicious File | User opens *.png.lnk | Q2 |
| Defense Evasion | T1027 Obfuscated Files / Information | BigInt-arithmetic PowerShell decoder | Q2, Q3 |
| Defense Evasion | T1027.004 Compile After Delivery | Wave 2 csc.exe → cvtres.exe → .dll | Q6 |
| Defense Evasion | T1036 Masquerading | LNK named *.png.lnk; legit Node.js runtime abused | Q1, Q2 |
| Defense Evasion | T1562.001 Impair Defenses: Disable/Modify Tools | Add-MpPreference -ExclusionProcess | Q5 |
| Persistence | T1547.001 Registry Run Keys / Startup Folder | Run (Node) + RunOnce (ProgramData EXE) | Q4 |
| Discovery | T1016 System Network Configuration Discovery | Geolocation via ip-api.com | Q7 |
| Command & Control | T1571 Non-Standard Port | Encrypted WebSocket on 8443/8445/8453/5555/56001-56003 | Q7 |
Native Audit-Log Hunts (non-CQL)
Email gateway / M365
- Hunt inbound mail from
*.calendly.comsenders with display name containingBooking Manager/(via Calendly)to reception/reservations mailboxes — especially where the body links into ashare.google/→google.com/share_googleredirect chain. - Flag links resolving to
photo-*.cfd/*.click/*.bondor a Cloudflare Turnstile gate. Note: these pass SPF/DKIM/DMARC/CompAuth — do not rely on auth verdicts alone. - Target accounts to watch (lure recipients):
reception,frontdesk,recepcja,recepce,accueil. Lure languages: Japanese, Danish, Dutch.
Windows PowerShell logging (Event ID 4104 ScriptBlock)
- Search ScriptBlock text for BigInt arithmetic patterns and
Invoke-WebRequest/iwrwith-OutFilewriting to$env:TEMP. - Confirm ScriptBlock logging (GPO: Turn on PowerShell Script Block Logging) is enabled fleet-wide — front-desk images often miss it.
Microsoft Defender
- Review Defender exclusion change events (Operational log) for newly added
-ExclusionProcessentries pointing at%TEMP%EXEs.
CrowdStrike LogScale CQL Hunt Queries
Looks for: the TonRAT launch — node.exe executing a .js from AppData\Local\Nodejs. Highest-fidelity signal in the chain.
// HUNT: TonRAT node.exe implant launch from AppData\Local\Nodejs // MITRE: T1059.007, T1036 | CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ImageFileName=/\\node\.exe$/i | CommandLine=/AppData\\Local\\Nodejs\\/i | CommandLine=/\.js/i | table([ComputerName, UserName, ImageFileName, CommandLine, ParentBaseFileName, SHA256HashData, ProcessStartTime], limit=200)
Looks for: the delivery stage — PowerShell launched with -ep bypass whose command line downloads via iwr/Invoke-WebRequest. Pair with Q1.
// HUNT: PowerShell -ep bypass running Invoke-WebRequest (Photo-ZIP delivery) // MITRE: T1059.001, T1204.002, T1027 | CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ImageFileName=/\\powershell\.exe$/i | CommandLine=/-ep\s+bypass|-executionpolicy\s+bypass/i | CommandLine=/iwr|invoke-webrequest/i | CommandLine=/-outfile/i | table([ComputerName, UserName, ParentBaseFileName, CommandLine, SHA256HashData, ProcessStartTime], limit=200)
Looks for: the second-stage drop — a .ps1 file written into the user TEMP path by a PowerShell process. FP: admin/automation scripts legitimately stage .ps1 in TEMP; scope to interactive front-desk hosts and correlate with Q2 ancestry.
// HUNT: .ps1 written to %TEMP% (random-named second stage) // MITRE: T1059.001, T1105 | CONF: high FP: med COST: low | REQUIRES: FileCreateInfo // TUNING: exclude known admin tooling paths / signed deployment agents writing to TEMP #event_simpleName=/PeFileWritten|NewExecutableWritten/ | TargetFileName=/\\(Local|Temp)\\Temp\\[A-Za-z0-9]{4,12}\.ps1$/i | ContextBaseFileName=/powershell\.exe/i | table([ComputerName, UserName, ContextBaseFileName, TargetFileName, Timestamp], limit=200)
Looks for: dual ASEP persistence — a Run/RunOnce value whose data points at AppData\Local\Nodejs or a ProgramData\<random>\*.exe. FP: legitimate installers write Run keys; constrain on the Node/ProgramData data pattern below and triage by value data, not just key path.
// HUNT: Run/RunOnce persistence to Nodejs component or ProgramData EXE // MITRE: T1547.001 | CONF: high FP: med COST: low | REQUIRES: RegGenericValueUpdate // TUNING: exclude vendor software Run-key writes; keep only Nodejs / ProgramData payload data #event_simpleName=/RegGenericValueUpdate|AsepValueUpdate/ | RegObjectName=/\\CurrentVersion\\Run(Once)?$/i | RegStringValue=/AppData\\Local\\Nodejs\\|ProgramData\\[^\\]+\\[^\\]+\.exe/i | table([ComputerName, UserName, RegObjectName, RegValueName, RegStringValue], limit=200)
Looks for: defense evasion — Add-MpPreference -ExclusionProcess targeting a randomly-named EXE in %TEMP%.
// HUNT: Add-MpPreference exclusion on a %TEMP% executable // MITRE: T1562.001 | CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | CommandLine=/Add-MpPreference/i | CommandLine=/-ExclusionProcess|-ExclusionPath/i | CommandLine=/\\Temp\\[A-Za-z0-9]{4,16}\.exe/i | table([ComputerName, UserName, ImageFileName, ParentBaseFileName, CommandLine, ProcessStartTime], limit=200)
Looks for: Wave-2 in-place compilation — cvtres.exe spawned by csc.exe (the .NET compiler chain producing a .dll). FP: legit .NET build/dev tooling and some installers invoke csc/cvtres; scope to front-desk hosts and correlate with the PowerShell/Node ancestry from Q1/Q2.
// HUNT: csc.exe -> cvtres.exe compile chain (Wave 2 DLL build) // MITRE: T1027.004 | CONF: med FP: med COST: low | REQUIRES: ProcessRollup2 // TUNING: exclude developer workstations / known build agents; alert on reception/front-desk images #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ImageFileName=/\\cvtres\.exe$/i | ParentBaseFileName=/csc\.exe/i | table([ComputerName, UserName, ImageFileName, ParentBaseFileName, CommandLine, ProcessStartTime], limit=200)
Looks for: the encrypted-WebSocket C2 on the campaign's non-standard ports, plus the ip-api.com geolocation IP. FP: 8443 is a common alt-HTTPS port and 5555 sees ADB/dev traffic; constrain by initiating process (node.exe / dropped EXE) and treat the rarer ports (8445/8453/56001-56003) as higher-signal.
// HUNT: TonRAT non-standard-port C2 and ip-api.com geolocation // MITRE: T1571, T1016 | CONF: med FP: med COST: med | REQUIRES: NetworkConnectIP4 // TUNING: scope RemotePort to node.exe / dropped-EXE initiators; exclude known internal services on 8443 #event_simpleName=NetworkConnectIP4 | (RemotePort=8443 or RemotePort=8445 or RemotePort=8453 or RemotePort=5555 or RemotePort=56001 or RemotePort=56002 or RemotePort=56003 or RemoteAddressIP4=/^208\.95\.112\.1$/) | ContextBaseFileName=/node\.exe|\.exe$/i | table([ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort, Timestamp], limit=200)
Looks for: any execution/connection matching the published SHA256 hashes, Wave-1 C2 IPs, or campaign DNS. Net IOCs are perishable (on-chain rotation) — run as a hunt, not a standing block.
// HUNT: TonRAT atomic IOC sweep (hashes / Wave-1 IPs / domains) // MITRE: multiple | CONF: high FP: low COST: low | REQUIRES: ProcessRollup2, NetworkConnectIP4, DnsRequest #event_simpleName=/ProcessRollup2|NetworkConnectIP4|DnsRequest/ | SHA256HashData=/^(04ec44f2618460f5c77c5e56014a512cc03a123c9c5b6b6b1273e2a1681ac2e1|9f10e3b6e5745784f26d18c38ce01fba054b19749c17260978ac11472564aee2|97448688b292bfec6d83b153588076fe59b111c35ac4e42a916238df16a71e2f|c5baa0c16b0074a1e94b48aa0177e9bfc23746aca8a5b42848a6685da85658b5|b7f46b192cd83a1d2487cb048cca645f6e8855b9673d500d50bbdb04eebc6bea)$/i OR RemoteAddressIP4=/^(178\.16\.54\.27|95\.217\.97\.121|193\.202\.84\.32|178\.16\.55\.179)$/ OR DomainName=/(safedocphoto|recallnine|kentjerk|photodoc-secure|sec-safe-dc)\.info$/i | table([ComputerName, ImageFileName, SHA256HashData, RemoteAddressIP4, DomainName, Timestamp], limit=max)
CrowdStrike Custom IOA Recommendations
| IOA name | Pattern | Exclusions | Action |
|---|---|---|---|
| TonRAT — Node implant from AppData | Process node.exe; CommandLine matches AppData\\Local\\Nodejs\\.*\.js | None expected — legit Node installs to Program Files / nvm, not user AppData\Local\Nodejs | Detect → Block |
| Photo-ZIP — LNK→PS download | Parent *.lnk; child powershell.exe; CmdLine -ep bypass + iwr/-OutFile | Allowlist signed admin LNK launchers if any | Detect → Block |
| Defender exclusion on TEMP EXE | Add-MpPreference + -ExclusionProcess + \Temp\*.exe | Exclude approved security/IT automation | Detect |
| Node→ProgramData persistence | Run/RunOnce value data referencing Nodejs or ProgramData\<rand>\*.exe | Exclude vendor installer value names | Detect |
Promotion path: Q1 and Q2 are the strongest IOA candidates (low FP, attacker-specific path/lineage). Deploy in Detect on a pilot ring of front-desk hosts, confirm zero benign hits over a baseline window, then escalate Q1 to Block.
Machine-Readable IOC Appendix
Grouped quick-copy blocks for direct ingestion / triage. Network IOCs are perishable — set short expirations and prefer detect on hashes, hunt on net infra.
type,value,action,severity,expiration,description,tags sha256,04ec44f2618460f5c77c5e56014a512cc03a123c9c5b6b6b1273e2a1681ac2e1,prevent,critical,2026-12-27,TonRAT PE xmnrwv9l.exe,campaign:TonRAT-PhotoZIP sha256,9f10e3b6e5745784f26d18c38ce01fba054b19749c17260978ac11472564aee2,prevent,critical,2026-12-27,TonRAT LNK Wave1,campaign:TonRAT-PhotoZIP sha256,97448688b292bfec6d83b153588076fe59b111c35ac4e42a916238df16a71e2f,prevent,critical,2026-12-27,TonRAT LNK Wave2,campaign:TonRAT-PhotoZIP sha256,c5baa0c16b0074a1e94b48aa0177e9bfc23746aca8a5b42848a6685da85658b5,prevent,critical,2026-12-27,TonRAT PS qFWe908J.ps1,campaign:TonRAT-PhotoZIP sha256,b7f46b192cd83a1d2487cb048cca645f6e8855b9673d500d50bbdb04eebc6bea,prevent,critical,2026-12-27,TonRAT DLL bjygtujc.dll,campaign:TonRAT-PhotoZIP ipv4,178.16.54.27,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP ipv4,95.217.97.121,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP ipv4,193.202.84.32,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP ipv4,178.16.55.179,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP domain,safedocphoto.info,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP domain,recallnine.info,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP domain,kentjerk.info,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP domain,photodoc-secure.info,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP domain,sec-safe-dc.info,detect,high,2026-09-27,TonRAT Wave1 C2,campaign:TonRAT-PhotoZIP
node.exe executing *.js from C:\Users\*\AppData\Local\Nodejs\ + domain arg *.png.lnk (1989-2079 bytes) -> powershell.exe -ep bypass -> iwr -OutFile %TEMP%\*.ps1 Run/RunOnce value data referencing Nodejs OR C:\ProgramData\<random>\*.exe Add-MpPreference -ExclusionProcess %TEMP%\<random>.exe csc.exe -> cvtres.exe compiling a .dll on a non-developer host (Wave 2) Inno-Setup is-*.tmp installer launched with /SL5 /VERYSILENT Outbound encrypted WebSocket to ports 8443/8445/8453/5555/56001-56003
Implant: TonRAT (Node.js / JavaScript; TON-blockchain C2 resolution) Legit runtime abused: node-v24.13.0-win-x64 (sha256 d14ba95cdce1ef7dc9ad3ac74949ca5db38b27378ee30f30a23cf26f9e875a11) Wave 2 C2 pattern: photo-<digits>.cfd (TLDs .cfd / .click / .bond; Cloudflare-fronted; Turnstile gate) Geo lookup: ip-api.com (208.95.112.1) Auth laundering: em1618.calendly.com / "Booking Manager (via Calendly)" redirect chain: calendly.com/url?q= -> share.google/TOKEN -> www.google.com/share_google -> photo-*.cfd Lure recipients: reception, frontdesk, recepcja, recepce, accueil (JP/DA/NL lures)
[ ] ASR: Block executable content from email client and webmail (BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550) [ ] ASR: Block JavaScript/VBScript from launching downloaded executable content (D3E037E1-3EB8-44C8-A917-57927947596D) [ ] ASR: Block process creations originating from PSExec and WMI commands [ ] Block/quarantine inbound .lnk and double-extension *.png.lnk at mail gateway [ ] Mark-of-the-Web enforced; SmartScreen on for downloaded archives [ ] PowerShell Script Block Logging (4104) enabled on front-desk images [ ] Tamper Protection ON so Add-MpPreference exclusions cannot be silently added [ ] Flag inbound *.calendly.com senders with "Booking Manager (via Calendly)" display names
Detection Validation Gates
| Gate | Check | Pass criteria |
|---|---|---|
| Telemetry ready | Confirm ProcessRollup2, RegGenericValueUpdate, NetworkConnectIP4 flowing from front-desk hosts | Events present in last 24h on target host group |
| Benign baseline | Run Q3, Q4, Q6, Q7 over 7-day window pre-deploy | Catalogue benign hits; build TUNING exclusions before alerting |
| Positive test | In a lab: drop node.exe to AppData\Local\Nodejs and run a benign .js + domain arg | Q1 fires; field projection populated |
| Field validation | Confirm RegStringValue / ContextBaseFileName populate as expected in tenant | Non-empty values in Q4/Q3 results |
| Promotion | Q1/Q2 zero benign hits over baseline window | Promote to Custom IOA (Detect → Block) |
Hardening — Tiered
- Enable ASR "Block executable content from email client and webmail" (GUID
BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550) and "Block JS/VBScript from launching downloaded executable content" (D3E037E1-3EB8-44C8-A917-57927947596D). MITRE M1042. Kills the LNK→PowerShell→download path at the source. - Block
.lnkand double-extension*.png.lnkattachments / downloads at the mail gateway and via web filtering. MITRE M1021 / M1049. - Enable Defender Tamper Protection so
Add-MpPreference -ExclusionProcesscannot silently whitelist the payload. MITRE M1054. - Enforce Mark-of-the-Web + SmartScreen on downloaded archives so the ZIP/LNK trips a warning. MITRE M1017.
- WDAC / AppLocker: deny user-writable execution of
node.exefrom%LOCALAPPDATA%and block unsigned EXEs underC:\ProgramData\. MITRE M1038. Front-desk images rarely need a user-path Node runtime. - PowerShell Script Block Logging (4104) + Constrained Language Mode on reception/reservations images. MITRE M1042 / M1049.
- Mail auth-laundering rule: flag/quarantine inbound
*.calendly.comsenders with "Booking Manager (via Calendly)" display names linking throughshare.googleredirects, since SPF/DKIM/DMARC pass. MITRE M1054.
- Application allowlisting (WDAC enforced mode) on the hospitality fleet — front-desk endpoints run a fixed app set and are an ideal allowlisting candidate. MITRE M1038.
- Egress filtering / proxy enforcement blocking direct outbound on non-standard ports (8443/8445/8453/5555/56001-56003) from workstation segments. MITRE M1037.
- Phishing-resistant MFA + conditional access for reception/reservations identities to limit post-compromise lateral value. MITRE M1032.
Deployable Playbooks
1 · Enable the two key ASR rules (Defender, Block mode)
Add-MpPreference -AttackSurfaceReductionRules_Ids BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550 -AttackSurfaceReductionRules_Actions Enabled Add-MpPreference -AttackSurfaceReductionRules_Ids D3E037E1-3EB8-44C8-A917-57927947596D -AttackSurfaceReductionRules_Actions Enabled # Verify: (Get-MpPreference).AttackSurfaceReductionRules_Ids (Get-MpPreference).AttackSurfaceReductionRules_Actions
2 · Enforce Tamper Protection + Script Block Logging (GPO/registry)
# Tamper Protection (managed via Intune/MDE; registry readout to verify): Get-MpComputerStatus | Select-Object IsTamperProtected # PowerShell Script Block Logging: $rk = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" New-Item -Path $rk -Force | Out-Null Set-ItemProperty -Path $rk -Name EnableScriptBlockLogging -Value 1 -Type DWord # Verify: Get-ItemProperty -Path $rk -Name EnableScriptBlockLogging
3 · WDAC/AppLocker — deny node.exe from user AppData (AppLocker EXE rule, deny)
# Audit first, then enforce. Deny rule path condition (AppLocker): # %LOCALAPPDATA%\Nodejs\* and %OSDRIVE%\ProgramData\*\*.exe # Set the AppLocker EXE collection to Enabled and add a DENY rule for the paths above. # Spot-check current effective mode: Get-AppLockerPolicy -Effective -Xml | Select-String "Exe"
4 · Post-deploy CQL spot-check (behavior gone)
// Confirm no node.exe launching from AppData\Local\Nodejs after enforcement #event_simpleName=ProcessRollup2 | ImageFileName=/\\node\.exe$/i | CommandLine=/AppData\\Local\\Nodejs\\/i | groupBy([ComputerName], function=count())
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Falcon network-contain any host hit by Q1/Q8. Block Wave-1 C2 IPs/domains at egress. Disable involved reception account sessions. | SOC / IR | Q1, Q8 detections; host AID |
| Eradicate | Remove AppData\Local\Nodejs\*.js + dropped Node runtime; delete ProgramData\<rand>\*.exe; remove Run/RunOnce values; remove any Add-MpPreference exclusion; delete staged %TEMP%\*.ps1. | IR / Endpoint | Q3, Q4, Q5 results |
| Recover | Re-enable Defender exclusions baseline + Tamper Protection; rotate credentials for the affected reception identities; restore from known-good if persistence depth unclear. | Endpoint / IAM | Clean re-scan; Q1 returns zero |
| Lessons | Push the mail auth-laundering rule fleet-wide; brief front-desk staff on photo-ZIP / Calendly lure; deploy ASR + WDAC tiers from S12. | SecEng | Coverage map closed |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1059.007 | node.exe implant launch from AppData | Q1 | Yes | Good |
| T1059.001 / T1204.002 | LNK→PowerShell→iwr delivery | Q2 | Yes | Good |
| T1105 / T1059.001 | .ps1 dropped to %TEMP% | Q3 | — | Partial |
| T1547.001 | Run / RunOnce persistence | Q4 | Yes | Good |
| T1562.001 | Defender exclusion on TEMP EXE | Q5 | Yes | Good |
| T1027.004 | csc.exe→cvtres.exe compile (Wave 2) | Q6 | — | Partial |
| T1571 / T1016 | Non-standard-port C2 + ip-api geo | Q7 | — | Partial |
| multiple | Atomic hash / IP / domain IOCs | Q8 | — | Good |
| T1566.002 / T1199 | Phishing via Calendly/Google-share laundering | — | — | Gap |
Known gap: the initial-access / auth-laundering stage (T1566.002, T1199) is not covered by Falcon EDR — it lives in mail-gateway / M365 telemetry. Cover it with the Native Audit-Log Hunts (S7) and the mail rule in S12, not CQL. Validation gates (S11) must pass — confirm telemetry, baseline Q3/Q4/Q6/Q7, and promote Q1/Q2 only after a clean baseline window.
Hunt Summary Ticket
TITLE: TonRAT / Photo-ZIP Hospitality Campaign — Node.js implant hunt
SEVERITY: HIGH (active in-the-wild; vendor-attributed; not yet actor-named)
SCOPE: Windows endpoints — hotel/hospitality front-desk, reception, reservations
HYPOTHESIS: A photo-ZIP/LNK lure ran PowerShell -ep bypass, dropped a Node.js runtime to
AppData\Local\Nodejs, and launched the TonRAT implant with TON-blockchain C2.
QUERIES: Q1 node.exe@AppData (primary) · Q2 LNK->PS->iwr · Q3 ps1@TEMP · Q4 Run/RunOnce ·
Q5 MpPreference exclusion · Q6 csc->cvtres · Q7 non-std-port C2 · Q8 atomic IOCs
DO FIRST: Run Q1 and Q8 across all front-desk host groups (7d). Triage hits with Q2 ancestry.
FINDINGS: <fill: hosts / users / C2 reached / persistence present>
GAPS: Initial-access auth-laundering (T1566.002/T1199) not in EDR — see Native Hunts (S7).
ACTIONS: Isolate hits, eradicate per S14, deploy ASR+Tamper Protection (S12 Immediate).
OWNER: HuntPack
VERSION: v0.1 · 2026-06-27
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 | Access Date |
|---|---|---|---|
| 1 · Primary | Microsoft Security Blog — Photo-ZIP campaign delivers Node.js implant (2026-06-25) | Full kill chain, TonRAT, TON-blockchain C2, all IOCs, persistence, auth-laundering, lures, MITRE | 2026-06-27 |
| 2 · Corroborating | The Hacker News — Microsoft warns of Photo-ZIP phishing | Independent corroboration of targeting, implant, laundering chain | 2026-06-27 |
| Ref | MITRE ATT&CK | Technique IDs & mitigations (M1038/M1042/M1037/M1054) | 2026-06-27 |