Doina Loader via Amazon-Themed ClickFix (finger.exe .plan)
Executive Summary
Attacker objective: run code on a Windows (and, per the report, macOS) machine and leave behind credential theft and remote access. Huntress investigated the campaign across multiple incidents and states it hit at least three of its customers. A sponsored Google result for the search term "amazon business" led to a spoofed Amazon sign-in page. After the victim types an email or phone number, a fake "I'm not a robot" CAPTCHA copies a command to the clipboard and instructs the victim to paste it into the Windows Run dialog.
The distinctive first stage is the built-in finger.exe client. The pasted command is obfuscated with commas, carets, hyphens and underscores, and it makes finger talk to a user account on an attacker domain over 79/tcp. The returned .plan text is a Windows script, hidden below the console height with 68 extra carriage returns. That script uses curl to fetch the real IronPython 3.4.2 release into the user Temp directory under a .pdf name, unpacks it with tar, and runs ipyw32.exe from the net462 folder with a Base64 and zlib-compressed Python blob.
The IronPython stage downloads timer6, a Cyrillic-substituted Base64 Python loader around XOR-protected shellcode. It decodes to a PE loader that finds an .ini file holding a URL, downloads from it and RC4-decrypts the body (v6), which is injected into memory. Huntress reports the vendor consensus is that v6 is Doina, a modular loader and RAT. Doina then pulls a ZIP with a portable Node.js runtime and an obfuscated index.js into C:\ProgramData\NodeView. The script decrypts two high-entropy files with AES-256-GCM: a shellcode reflective DLL injector and a larger payload that Huntress describes as "allegedly" Lumma Stealer, injected by process hollowing. A pirated NetSupport Client is also deployed to C:\ProgramData\Prodclear and talks to 167.148.201[.]106 on 725/tcp.
Attribution and persistence: no threat actor is named by any source, and this pack does not name one. Huntress reports no scheduled task or other persistence mechanism, so none is claimed here. The Lumma Stealer identification is hedged by Huntress and is hedged here.
Highest-value defensive angle: the chain is built from legitimate binaries (finger, curl, tar, IronPython, Node.js, NetSupport), so hash blocking alone fails. Hunt the behavior: finger.exe execution and TCP/79 egress (near-zero false positives on a modern fleet), IronPython running from a user-writable folder, node.exe running index.js from ProgramData, and Client32.exe in ProgramData. The pack ships 14 CQL queries, none of which recommends blocking a stock binary.
Defender priority: any host where finger.exe ran or connected out on port 79 should be treated as a credential-theft incident, not a malware cleanup. The final payload is an infostealer targeting browsers, crypto wallets, password managers and email clients, and a remote-management client was installed alongside it. Isolate the host, then reset credentials and sessions for the user and move any crypto holdings, without waiting for host forensics.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Forward |
|---|---|---|---|
| 1 | Huntress, "Amazon-Themed ClickFix Campaign Serves Up Finger-Licking Malware" (2026-10-21) | Primary report. Full infection chain, all infrastructure, nine hashes, port 79 and 725 behavior, Doina identification, hedged Lumma identification, NetSupport at Prodclear, no persistence reported. | Yes. Source of every campaign indicator. Snapshot 01. |
| 2 | Security Online, "Amazon ClickFix Campaign Abuses Windows Finger Command" | Secondary write-up of the Huntress report. Confirms the Doina, Node.js, suspected-Lumma and NetSupport chain, states no attribution, advises blocking outbound port 79. Adds no indicators. | Partial. Corroborates the chain and the port 79 control only; same lineage as Huntress. Snapshot 02. |
| 2 | SigmaHQ rule "Network Connection Initiated via Finger.EXE" | Independent detection content: any network connection initiated by finger.exe is high severity and maps to T1059.003 and T1071.004. States finger's presence in modern Windows already raises suspicion and cites a ClickFix campaign. | Yes. Independent basis for the finger.exe behavioral detections (Q1, Q3, Q4). Snapshot 03. |
| 3 | BlueTeamCoolTeam, "Seven layers of obfuscation, one 1970s LOLBIN" | A different finger.exe and IronPython campaign (different domains, different UUID and C2). Independently confirms the technique: cmd.exe with caret obfuscation, finger response executed as batch, renamed curl, IronPython 3.4.2 zip saved as .pdf, tar extraction, ipyw32.exe running an inline Base64 and zlib payload, Cyrillic-substituted Base64. | Behavior only. Second independent source for the technique; none of its indicators ship. Snapshot 04. |
Hunter notes & deliberate omissions
- Indicators are single-lineage. Every campaign domain, IP and hash comes from the Huntress report alone; Security Online relays it without adding values. Per the pack rule that blocking needs two independent sources, all 18 imported indicators are detect, none is prevent, and none is critical severity. The finger.exe, IronPython and port 79 behaviors do have two independent sources.
- The sibling finger/IronPython campaign is not this pack's campaign. The BlueTeamCoolTeam post documents separate infrastructure. Its domains, campaign UUID, build marker, XOR key and YARA are deliberately not shipped here, because a pack carries only its own campaign's indicators.
- Doina identification. Huntress says the "security vendor consensus" is that v6 is Doina and cites a 2024 Halcyon report tying Doina to pre-ransomware activity. The Halcyon report was not retrieved, so no Doina detail beyond what Huntress states is claimed.
- Lumma Stealer is hedged. Huntress calls the larger payload "allegedly Lumma Stealer" and says the stealer is "suspected" elsewhere. Query names and IOC descriptions here say attributed or suspected, never confirmed.
- Domain spelling discrepancy. The ad landing host is written orders-amazon[.]com[.]advancedfences[.]com in the article body and orders-amazon-com[.]advancedfences[.]com in the IOC table. Both are in the snapshot; the pack imports the IOC-table form and notes the other.
- The ClickFix command is not reproduced. The article does not print the obfuscated string and this pack does not invent one. Q2 and Q12 hunt its shape (finger-letters with separator padding and a user@host argument).
- Searched but unavailable: Dark Reading, BleepingComputer and Petri coverage returned HTTP 403 to automated fetch, so none is cited.
- macOS variant: the report says macOS users received Terminal instructions and the destination was offline during research. No macOS payload analysis exists, so no macOS detection is claimed.
Hunt Brief & Attack Chain
Hypotheses (ordered by fidelity)
- H1, high: a user pasted a ClickFix command that launched finger.exe, which connected out on 79/tcp (T1204.004, T1218, T1105). Q1, Q2, Q3, Q4, Q12.
- H2, high: an IronPython interpreter ran from a user-writable folder with an inline encoded script and reached the internet (T1059.006, T1105). Q5, Q6, Q7, Q8.
- H3, medium-high: a Node.js runtime executed index.js from ProgramData (T1059.007). Q9.
- H4, medium-high: a pirated NetSupport Client ran from ProgramData and beaconed on 725/tcp (T1219). Q10, Q11.
- H5, high: the host resolved a published campaign domain (T1071.001). Q13.
- H6, high: a single host shows two or more of the chain tools in the window. Q14.
Attack chain
| Step | Behavior | Telemetry (platform in parentheses) | Hunt angle |
|---|---|---|---|
| 0 | Sponsored ad to spoofed Amazon sign-in on Google Sites; fake CAPTCHA copies a command | Proxy or browser URL logs with ad-click markers aclk and gclid; DNS (outside endpoint telemetry) | Native hunt N1. No endpoint signal |
| 1 | Victim pastes the command into Run; obfuscated finger command executes | Process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); registry value writes (RunMRU) | Q1, Q2, Q12 |
| 2 | finger.exe connects to 79/tcp and returns the .plan script, executed by cmd | Network connections (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents); DNS requests | Q3, Q4 |
| 3 | curl downloads IronPython.3.4.2.zip to Temp as .pdf; tar unpacks; ipyw32.exe runs from net462 with an encoded blob | Process creation with command line | Q5, Q6, Q7 |
| 4 | ipyw32.exe fetches timer6; PE loader finds an .ini URL, fetches v6, RC4-decrypts, injects Doina in memory | Network and DNS from the interpreter; memory injection (outside endpoint telemetry for the shellcode itself) | Q8, Q13. In-memory stage is a gap |
| 5 | Doina downloads the Node package to C:\ProgramData\NodeView; node.exe runs index.js | Process creation with command line | Q9 |
| 6 | index.js decrypts two files (AES-256-GCM); sRDI injects the stealer by process hollowing; HTTPS POST then WebSocket beacons | In-memory injection and WebSocket content (outside endpoint telemetry); DNS and network for the C2 hosts | Q13. Stealer behavior is a gap |
| 7 | Pirated NetSupport Client in C:\ProgramData\Prodclear talks to 167.148.201[.]106 on 725/tcp | Process creation; network connections | Q10, Q11 |
Consolidated IOC Table
Read before importing: every campaign indicator comes from one primary report, so all import as detect. Legitimate software (finger.exe, curl.exe, tar.exe, ipyw32.exe, node.exe, Client32.exe, the IronPython archive) is listed for context and is never a block target.
| Type | Value | Confidence | Action | Context |
|---|---|---|---|---|
| domain | grillrecipespro[.]com | HIGH | detect | Host queried by finger.exe for the .plan script. Source: Huntress. |
| domain | grillmasterchef[.]com | HIGH | detect | Serves timer6 and v6; Doina C2 (HTTPS POST). Source: Huntress. |
| domain | thegrilljournal[.]com | HIGH | detect | Reported attacker infrastructure; exfil after check-in. Source: Huntress. |
| domain | homelygrill[.]com | HIGH | detect | Reported attacker infrastructure; exfil after check-in. Source: Huntress. |
| domain | perfectgrillrecipes[.]com | HIGH | detect | Stealer initial check-in. Source: Huntress. |
| domain | EmbersAndBites[.]com | MEDIUM | detect | Listed as attacker infrastructure; role not detailed. |
| domain | kiriosyka[.]com | HIGH | detect | Node package (sqr-viewertr6) delivery. |
| domain | liokida[.]com | HIGH | detect | Node and stealer packages (sqr-viewertr6z); embedded in the stealer. |
| domain | orders-amazon-com[.]advancedfences[.]com | HIGH | detect | Sponsored-ad landing subdomain. The article prose renders it orders-amazon[.]com[.]advancedfences[.]com while the IOC table renders it orders-amazon-com[.]advancedfences[.]com; the pack ships the IOC-table form and hunts both via the ad-click markers. |
| url | hxxps[://]sites[.]google[.]com/view/ch-pc/sign-in | MEDIUM | hunt | Fake Amazon sign-in with the ClickFix CAPTCHA, on a legitimate Google Sites host. Hunt only: the host is Google. |
| url | hxxp[://]ashevilleroofingcontractor[.]com/curl/... | LOW | hunt | Alternate macOS payload URL, offline when researched. Looks like a compromised third-party site, so not imported. |
| url | hxxp://grillmasterchef[.]com/90e4a147-1234-505c-a024-062e543b5df8/timer6 | HIGH | detect | timer6 delivery URL (Cyrillic-substituted Base64 Python). Domain imported; full path hunted in proxy logs. |
| url | hxxps://grillmasterchef[.]com/90e4a147-1234-505c-a024-062e543b5df8/v6 | HIGH | detect | v6 stage URL (RC4-encrypted Doina). |
| url | hxxps://kiriosyka[.]com/KsxmmKiN5N0j/sqr-viewertr6 | HIGH | detect | Node package URL, a 52MB ZIP per Huntress. |
| url | hxxps://liokida[.]com/KsxmmKiN5N0j/sqr-viewertr6z | HIGH | detect | Node/stealer package URL. |
| ipv4 | 167.148.201[.]106 | HIGH | detect | Rogue NetSupport C2 on 725/tcp, URL http://167.148.201[.]106:725/fakeurl.htm. The address only hosts an unrelated restaurant website, so expect no legitimate use but do not block without review. |
| port | 79/tcp | HIGH | detect | Finger protocol egress. Behavioral, not importable. |
| port | 725/tcp | MEDIUM | detect | NetSupport Client traffic on a nonstandard port. Behavioral. |
| sha256 | d864b28c302db9ed10baf8def0fb26bac0b97d27349d22c4e9390d30fce54649 | HIGH | detect | timer6 |
| sha256 | 891cc3ca2d29e9b185a8913e93810f48859d2be1f23ddd977a15cdbeed509e0e | HIGH | detect | v6 (Doina) RC4-decrypted body |
| sha256 | 87a047160eb1a9b92e6ede511252dfc624b0f5a7185ac2cec1c7ebde8cdf4eea | HIGH | detect | index.js |
| sha256 | 035d396b3a329f59d680dc36a0a639eb7fb306bea97e42f5f53293dce817e8b7 | HIGH | detect | Nodeview.zip |
| sha256 | 24a293a662600b2292d9259baebba1c09b41df82a5c9c20f8572625ae759696f | HIGH | detect | 754224f2-3449-4ac7-8b13-1cb8e6af6408.tmp (payload attributed to Lumma Stealer) |
| sha256 | b8419a676a3d4af04ea98287a2b85052fae5c06e6278ca9d8bfa4b5b396b39a6 | HIGH | detect | 1572cef1-df62-4594-a3aa-a9db91b9d7db.tmp |
| sha256 | 42527a4388ef4ade6f4eaf52706f0ab31cb4e9ced3a56a6b15402551f9f01208 | HIGH | detect | 29d37679-4112-4751-8613-15b94b29ad39.tmp |
| sha256 | 408b7fc4f08f2a9044046025f05995e8cbd5c96c39844aaa8a7eb8ee4697ea89 | HIGH | detect | 545b93c2-e4f7-4397-b32c-8a3c77538594.tmp |
| filename | IronPython.3.4.2.zip | HIGH | hunt | Legitimate archive, saved to Temp with a .pdf extension. Do not block the name. |
| filename | ipyw32.exe, node.exe, Client32.exe, finger.exe, curl.exe, tar.exe | HIGH | hunt | Legitimate software abused. Behavioral detections only (Q1, Q5 to Q10); never a prevent target. |
| path | C:\ProgramData\NodeView, C:\ProgramData\Prodclear | HIGH | hunt | Install folders for the Node runtime and the pirated NetSupport Client. |
Affected Surface & Telemetry Matrix
| Surface | Telemetry (platform in parentheses) | Priority | Gap |
|---|---|---|---|
| Windows workstations (Run dialog, finger, cmd, curl, tar) | Process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents) | HIGH | None for the process events themselves |
| Egress on 79/tcp and 725/tcp | Network connections with process (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents) | HIGH | Perimeter and proxy logs are the second source for hosts without an agent |
| DNS for the barbecue-themed domains | DNS requests with process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceNetworkEvents) | HIGH | Process attribution varies by sensor; validate in tenant |
| Run-dialog history | Registry value writes (CrowdStrike: RegGenericValueUpdate. SentinelOne: Registry Value Modified. Defender: DeviceRegistryEvents) | MEDIUM | RunMRU capture depends on sensor policy; outside endpoint telemetry if not collected |
| Ad-click chain to spoofed sign-in | Web proxy or secure web gateway URL logs; browser history | MEDIUM | Outside endpoint telemetry |
| In-memory Doina, shellcode and stealer | Memory scanning, behavioral prevention engine | MEDIUM | No generic hunt telemetry for reflective loading or process hollowing is written in this pack |
| macOS users | Terminal process telemetry | LOW | Payload offline during research; no detection claimed |
ATT&CK Mapping
| Tactic | Technique | Observed behavior | Query / Control |
|---|---|---|---|
| Resource Development | T1583.008 Malvertising | Sponsored Google result for "amazon business" | N1; S2 |
| Execution | T1204.004 Malicious Copy and Paste | Fake CAPTCHA copies a command, victim pastes it into Run | Q2, Q12; I2, S2 |
| Execution | T1059.003 Windows Command Shell | cmd executes the finger-returned .plan script | Q1, Q2; I3 |
| Defense Evasion | T1218 System Binary Proxy Execution | finger.exe and tar.exe used as living-off-the-land binaries | Q1, Q5; I1, N1 |
| Command and Control | T1105 Ingress Tool Transfer | finger over 79/tcp, curl for IronPython, timer6, v6 and Node package downloads | Q3, Q4, Q5, Q8; I1 |
| Defense Evasion | T1036.008 Masquerade File Type | IronPython ZIP saved with a .pdf extension | Q5 |
| Execution | T1059.006 Python | ipyw32.exe runs a Base64 and zlib Python blob, then timer6 | Q6, Q7, Q8; N1 |
| Defense Evasion | T1027 Obfuscated Files or Information | Comma, caret, hyphen and underscore padding; Cyrillic-substituted Base64; XOR shellcode; RC4 and AES-256-GCM payloads | Q2, Q7 |
| Defense Evasion | T1620 Reflective Code Loading / T1055.012 Process Hollowing | Shellcode in memory; sRDI injects the stealer by process hollowing | None (gap); S1 |
| Execution | T1059.007 JavaScript | node.exe runs obfuscated index.js from ProgramData\NodeView | Q9 |
| Credential Access / Collection | T1555.003 Credentials from Web Browsers; T1005 Data from Local System | Suspected Lumma Stealer targets browsers, wallets, password managers, email clients | None (gap); C2 |
| Command and Control | T1071.001 Web Protocols | HTTPS POST and WebSocket C2; DNS to barbecue-themed domains | Q8, Q13 |
| Command and Control | T1219 Remote Access Software | Pirated NetSupport Client at ProgramData\Prodclear, 725/tcp | Q10, Q11; I1 |
Native Audit-Log Hunts
| ID | Source | What to look for |
|---|---|---|
| N1 | Web proxy, secure web gateway or browser history | A request whose path or query carries the ad-click markers aclk or gclid (also gadsource, gadcampaignid, gbraid) followed within minutes by sites.google.com/view/ch-pc/sign-in or a subdomain of advancedfences.com. Huntress recommends these markers to reconstruct the ad-to-malicious-site chain. |
| N2 | Firewall, NetFlow, proxy | Any outbound 79/tcp from a workstation subnet; any 725/tcp flow; flows to 167.148.201.106. |
| N3 | Windows Security 4688 with command-line auditing, or Sysmon 1 / 3 / 11 / 22 | finger.exe process creation; Sysmon 3 connections from finger.exe (the SigmaHQ rule logic); ipyw32.exe or a node.exe from ProgramData; file creation under %TEMP% of an IronPython*.pdf. |
| N4 | DNS server or resolver logs | Queries for the eight published second-level domains and the advancedfences.com subdomain. |
| N5 | File system sweep (read-only) | Existence of C:\ProgramData\NodeView and C:\ProgramData\Prodclear, and GUID-named .tmp files of roughly 17KB and 19MB nearby. See playbook P5 for a read-only check. |
| N6 | Proxy logs, timer6 and v6 paths | URL paths containing 90e4a147-1234-505c-a024-062e543b5df8 or KsxmmKiN5N0j, including a ?exclude_bins=true query on the Node package URL. |
Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL
Use your tenant's own address. SentinelOne does not document a link that pre-fills a query, so each "Copy & open SentinelOne" button copies the query and opens this console: paste it into PowerQuery.
Pick your portal. Microsoft documents a Share link in advanced hunting but not its URL format, so each "Copy & open Defender" button copies the query and opens Advanced hunting: paste it into the editor.
Set the time range in the Falcon time picker (7 to 30 days). No query carries an in-query time filter. finger.exe, ipyw32.exe, node.exe and Client32.exe are legitimate binaries: every detection below is behavioral and none should be turned into a block rule for the binary itself.
Looks for: Any execution of the built-in finger client. Stage 1 of the chain pulls the .plan script with it. Run this first: it is the highest-fidelity single signal.
Known limitations and deployment notes
- PowerQuery: A process the sensor did not observe starting is not captured.
- PowerQuery assumes: On a Process Creation event src.process.user is the user of the creating process.
- PowerQuery assumes: tgt.process.name holds the bare image name of the created process.
- KQL – Defender XDR: A process the sensor did not observe starting is not captured.
- KQL – Defender XDR assumes: FileName holds the bare image name of the created process.
// HUNT: finger.exe process execution // MITRE: T1218, T1059.003 // CONF: high | FP: low | COST: low | REQUIRES: process creation with command line (ProcessRollup2) // FALSE POSITIVES: a legacy admin or lab user running finger against an internal Unix host; very rare on modern Windows // TUNING: exclude a named jump-host asset group if finger is still used there; otherwise treat every hit as suspect #event_simpleName=ProcessRollup2 | FileName=/^finger\.exe$/i | table([@timestamp, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, aid], limit=200)
2 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process name
Recommended fields
tgt.process.cmdline: Created command linesrc.process.name: Creating process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.name = * | limit 5 | columns event.type, tgt.process.name
// HUNT: finger.exe process execution
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1218, T1059.003
// CONF: high | FP: low
// FALSE POSITIVES: a legacy admin or lab user running finger against an internal Unix host; very rare on modern Windows
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: the created process is finger.exe. in:anycase reproduces the CQL case-insensitive match. The user shown is the creating process's user.
// REQUIRES: SentinelOne process-creation telemetry (Process Creation events)
// TUNING: Exclude a named jump-host asset group if finger is still used there; otherwise treat every hit as suspect.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A process the sensor did not observe starting is not captured.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' tgt.process.name in:anycase ('finger.exe')
| columns event.time, endpoint.name, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
2 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process name
Recommended fields
ProcessCommandLine: Created command lineInitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName | take 5
// HUNT: finger.exe process execution // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1218, T1059.003 // CONF: high | FP: low // FALSE POSITIVES: a legacy admin or lab user running finger against an internal Unix host; very rare on modern Windows // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: the created process is finger.exe, matched case-insensitively with =~. The creator is InitiatingProcessFileName. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Exclude a named jump-host asset group if finger is still used there; otherwise treat every hit as suspect. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A process the sensor did not observe starting is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where FileName =~ "finger.exe" | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName | sort by Timestamp desc | take 200
Looks for: The ClickFix command breaks the word finger apart with commas, carets, hyphens and underscores that cmd ignores. This regex tolerates those separators between letters and requires a user@host argument. It is a behavioral shape, not the real command (the article does not reproduce the obfuscated string).
Known limitations and deployment notes
- PowerQuery: A command line passed through an environment variable and expanded after launch is not visible.
- PowerQuery assumes: matches is case-insensitive unless :matchcase is given.
- PowerQuery assumes: On a Process Creation event src.process.user is the user of the creating process.
- KQL – Defender XDR: A command line passed through an environment variable and expanded after launch is not visible.
- KQL – Defender XDR assumes: ProcessCommandLine holds the full command line of the created process.
// HUNT: Obfuscated finger command line inside cmd.exe (comma, caret, hyphen, underscore padding) // MITRE: T1204.004, T1027, T1059.003 // CONF: high | FP: low | COST: low | REQUIRES: process creation with command line (ProcessRollup2) // FALSE POSITIVES: none expected; the pattern needs finger-shaped letters plus a user@host argument // TUNING: none expected; if a legitimate script matches, anchor the regex to the parent process instead #event_simpleName=ProcessRollup2 | FileName=/^cmd\.exe$/i | CommandLine=/f[,^_-]*i[,^_-]*n[,^_-]*g[,^_-]*e[,^_-]*r[,^_-]*\s*\S+@[a-z0-9.-]+\.[a-z]{2,}/i | table([@timestamp, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, aid], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creating process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.type, tgt.process.name, tgt.process.cmdline
// HUNT: Obfuscated finger command line inside cmd.exe (comma, caret, hyphen, underscore padding)
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1204.004, T1027, T1059.003
// CONF: high | FP: low
// FALSE POSITIVES: none expected; the pattern needs finger-shaped letters plus a user@host argument
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: cmd.exe whose command line holds finger-shaped letters padded with comma, caret, hyphen or underscore, followed by a user@host argument. matches is case-insensitive by default, which reproduces the CQL i flag.
// REQUIRES: SentinelOne process-creation telemetry with command lines
// TUNING: None expected; if a legitimate script matches, anchor the regex to the creating process instead.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A command line passed through an environment variable and expanded after launch is not visible.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' tgt.process.name in:anycase ('cmd.exe') tgt.process.cmdline matches 'f[,^_-]*i[,^_-]*n[,^_-]*g[,^_-]*e[,^_-]*r[,^_-]*\\s*\\S+@[a-z0-9.-]+\\.[a-z]{2,}'
| columns event.time, endpoint.name, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: Obfuscated finger command line inside cmd.exe (comma, caret, hyphen, underscore padding)
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1204.004, T1027, T1059.003
// CONF: high | FP: low
// FALSE POSITIVES: none expected; the pattern needs finger-shaped letters plus a user@host argument
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: cmd.exe whose command line holds finger-shaped letters padded with comma, caret, hyphen or underscore, followed by a user@host argument. The (?i) prefix reproduces the CQL i flag.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: None expected; if a legitimate script matches, anchor the regex to the initiating process instead.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A command line passed through an environment variable and expanded after launch is not visible.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where FileName =~ "cmd.exe"
| where ProcessCommandLine matches regex @"(?i)f[,^_-]*i[,^_-]*n[,^_-]*g[,^_-]*e[,^_-]*r[,^_-]*\s*\S+@[a-z0-9.-]+\.[a-z]{2,}"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| sort by Timestamp desc
| take 200Looks for: Finger travels in plaintext on 79/tcp and almost no network blocks it. Any workstation connecting to port 79 is anomalous. Also catches a renamed finger client, because it keys on the port.
Known limitations and deployment notes
- PowerQuery: A connection the sensor did not observe is not captured.
- PowerQuery assumes: IP Connect events carry the destination port in dst.port.number as a number.
- PowerQuery assumes: IP Connect covers outbound connections; inbound listeners on port 79 would need the direction field, which is not used here.
- KQL – Defender XDR: A connection the sensor did not observe is not captured.
- KQL – Defender XDR assumes: DeviceNetworkEvents rows with ActionType ConnectionFailed are also returned, which the CQL connect event would not include.
// HUNT: Outbound TCP/79 (finger protocol) from any process // MITRE: T1105, T1071 // CONF: high | FP: low | COST: low | REQUIRES: network connections with remote port (NetworkConnectIP4) // FALSE POSITIVES: legacy finger usage against internal Unix hosts; vulnerability scanners on a scanner host // TUNING: exclude internal RFC1918 destinations and your scanner asset group #event_simpleName=NetworkConnectIP4 | RemotePort=79 | table([@timestamp, ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort, aid], limit=200)
2 required fields · View requirements
Required telemetry
- IP Connect
Required fields
event.type: Event typedst.port.number: Destination port
Recommended fields
dst.ip.address: Destination addresssrc.process.name: Acting process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'IP Connect' dst.port.number = * | limit 5 | columns event.type, dst.port.number, dst.ip.address
// HUNT: Outbound TCP/79 (finger protocol) from any process // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1105, T1071 // CONF: high | FP: low // FALSE POSITIVES: legacy finger usage against internal Unix hosts; vulnerability scanners on a scanner host // EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate: a connection event whose destination port is 79. On a non-process-creation event the acting process is src.process.*. // REQUIRES: SentinelOne network telemetry (IP Connect events) // TUNING: Exclude internal RFC1918 destinations and your scanner asset group. // LOOKBACK: 7d; set the console time picker or LRQ window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A connection the sensor did not observe is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'IP Connect' dst.port.number = 79 | columns event.time, endpoint.name, src.process.user, src.process.name, dst.ip.address, dst.port.number | limit 200
Automated validation: static-passed
2 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeRemotePort: Remote port
Recommended fields
RemoteIP: Remote addressInitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, RemotePort, RemoteIP | take 5
// HUNT: Outbound TCP/79 (finger protocol) from any process // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1105, T1071 // CONF: high | FP: low // FALSE POSITIVES: legacy finger usage against internal Unix hosts; vulnerability scanners on a scanner host // EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate: a connection event whose remote port is 79. The acting process is InitiatingProcessFileName. // REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents) // TUNING: Exclude internal RFC1918 destinations and your scanner asset group. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A connection the sensor did not observe is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceNetworkEvents | where Timestamp >= ago(7d) | where RemotePort == 79 | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, RemoteIP, RemotePort, ActionType | sort by Timestamp desc | take 200
Looks for: Resolves the attacker .plan host. The campaign finger target was grillrecipespro[.]com; this query is domain-agnostic so it also catches rotation to new domains.
Known limitations and deployment notes
- PowerQuery: A lookup answered from the host cache without a DNS event is not captured.
- PowerQuery assumes: The queried name is in event.dns.request; some schema versions use dns.request instead, so confirm which is populated.
- KQL – Defender XDR: A lookup answered from the host cache without a DNS event is not captured.
- KQL – Defender XDR assumes: ActionType DnsQueryResponse is populated on this tenant.
- KQL – Defender XDR assumes: The name is under DnsQueryString or QueryName in AdditionalFields; the readiness test prints AdditionalFields so you can see which key your tenant uses.
// HUNT: DNS lookup initiated by finger.exe // MITRE: T1071, T1105 // CONF: high | FP: low | COST: low | REQUIRES: DNS requests with process context (DnsRequest) // FALSE POSITIVES: none expected on modern endpoints // TUNING: none expected; treat every hit as an incident until proven otherwise #event_simpleName=DnsRequest | ContextBaseFileName=/^finger\.exe$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, DomainName, aid], limit=200)
2 required fields · View requirements
Required telemetry
- DNS Resolved
Required fields
event.type: Event typesrc.process.name: Acting process name
Recommended fields
event.dns.request: Queried name
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'DNS Resolved' src.process.name = * | limit 5 | columns event.type, src.process.name, event.dns.request
// HUNT: DNS lookup initiated by finger.exe
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1071, T1105
// CONF: high | FP: low
// FALSE POSITIVES: none expected on modern endpoints
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a DNS resolution event whose acting process is finger.exe. On a non-process-creation event the acting process is src.process.*.
// REQUIRES: SentinelOne DNS telemetry with process context (DNS Resolved events)
// TUNING: None expected; treat every hit as an incident until proven otherwise.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A lookup answered from the host cache without a DNS event is not captured.
// VALIDATION LEVEL: static-reviewed
event.type = 'DNS Resolved' src.process.name in:anycase ('finger.exe')
| columns event.time, endpoint.name, src.process.user, src.process.name, event.dns.request
| limit 200Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeInitiatingProcessFileName: Initiating process nameAdditionalFields: Event details
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "DnsQueryResponse" | project Timestamp, ActionType, InitiatingProcessFileName, AdditionalFields | take 5
// HUNT: DNS lookup initiated by finger.exe // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1071, T1105 // CONF: high | FP: low // FALSE POSITIVES: none expected on modern endpoints // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a DNS response event whose initiating process is finger.exe. Defender has no endpoint DNS table, so the DnsQueryResponse action on DeviceEvents is used and the name is read from AdditionalFields. // REQUIRES: Defender for Endpoint DNS events (DeviceEvents, ActionType DnsQueryResponse) // TUNING: None expected; treat every hit as an incident until proven otherwise. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A lookup answered from the host cache without a DNS event is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceEvents | where Timestamp >= ago(7d) | where ActionType == "DnsQueryResponse" | where InitiatingProcessFileName =~ "finger.exe" | extend DnsName = tostring(coalesce(parse_json(AdditionalFields)["DnsQueryString"], parse_json(AdditionalFields)["QueryName"])) | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, DnsName | sort by Timestamp desc | take 200
Looks for: Stage 2: the .plan script uses curl to fetch IronPython.3.4.2.zip into the user Temp directory saved with a .pdf extension, then unpacks it with the built-in tar. The two branches below cover both halves. FP: developers fetching IronPython releases; tar extracting legitimately named .pdf archives is rare. Tuning: exclude developer asset groups for the IronPython branch; the tar-on-.pdf branch needs no exclusion.
Known limitations and deployment notes
- PowerQuery: A download performed by a different tool than curl.exe is not captured.
- PowerQuery assumes: contains is a plain substring test and is case-insensitive by default.
- KQL – Defender XDR: A download performed by a different tool than curl.exe is not captured.
- KQL – Defender XDR assumes: ProcessCommandLine holds the full command line of the created process.
// HUNT: curl downloading IronPython and tar unpacking a .pdf archive // MITRE: T1105, T1036.008, T1218 // CONF: medium | FP: medium | COST: low | REQUIRES: process creation with command line (ProcessRollup2) // FALSE POSITIVES: developers fetching IronPython releases; tar extracting legitimately named .pdf archives is rare // TUNING: exclude developer asset groups for the IronPython branch; the tar-on-.pdf branch needs no exclusion #event_simpleName=ProcessRollup2 | (FileName=/^curl\.exe$/i CommandLine=/IronPython/i) or (FileName=/^tar\.exe$/i CommandLine=/\.pdf/i) | table([@timestamp, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, aid], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creating process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.type, tgt.process.name, tgt.process.cmdline
// HUNT: curl downloading IronPython and tar unpacking a .pdf archive
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1105, T1036.008, T1218
// CONF: medium | FP: medium
// FALSE POSITIVES: developers fetching IronPython releases; tar extracting legitimately named .pdf archives is rare
// EQUIVALENCE: equivalent-with-schema-assumptions - Same two-branch predicate: curl.exe with IronPython in the command line, or tar.exe with .pdf in the command line. contains is case-insensitive, which reproduces the CQL i flag.
// REQUIRES: SentinelOne process-creation telemetry with command lines
// TUNING: Exclude developer asset groups for the IronPython branch; the tar-on-.pdf branch needs no exclusion.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A download performed by a different tool than curl.exe is not captured.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' ((tgt.process.name in:anycase ('curl.exe') and tgt.process.cmdline contains 'IronPython') or (tgt.process.name in:anycase ('tar.exe') and tgt.process.cmdline contains '.pdf'))
| columns event.time, endpoint.name, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: curl downloading IronPython and tar unpacking a .pdf archive // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1105, T1036.008, T1218 // CONF: medium | FP: medium // FALSE POSITIVES: developers fetching IronPython releases; tar extracting legitimately named .pdf archives is rare // EQUIVALENCE: equivalent-with-schema-assumptions - Same two-branch predicate: curl.exe with IronPython in the command line, or tar.exe with .pdf in the command line. contains is case-insensitive, which reproduces the CQL i flag. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Exclude developer asset groups for the IronPython branch; the tar-on-.pdf branch needs no exclusion. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A download performed by a different tool than curl.exe is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where (FileName =~ "curl.exe" and ProcessCommandLine contains "IronPython") or (FileName =~ "tar.exe" and ProcessCommandLine contains ".pdf") | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName | sort by Timestamp desc | take 200
Looks for: Stage 3: the stock IronPython windowless interpreter runs from a net462 folder. This is a legitimate binary, so it is a behavioral detection only and never a prevent target. FP: developers and build agents that use IronPython; the chain runs it from a net462 folder under a user profile, which is unusual. Tuning: exclude developer and build asset groups; keep alerting on ipyw32.exe launched from a user-writable path with a cmd.exe parent.
Known limitations and deployment notes
- PowerQuery: A renamed interpreter outside an IronPython folder is not captured by the path branch.
- PowerQuery assumes: tgt.process.image.path holds the full Windows image path with backslash separators.
- PowerQuery assumes: matches is case-insensitive unless :matchcase is given.
- KQL – Defender XDR: A renamed interpreter outside an IronPython folder is not captured by the path branch.
- KQL – Defender XDR assumes: FolderPath holds the full image path of the created process, folder plus file name.
// HUNT: IronPython ipyw32.exe execution (stock interpreter, behavioral detect) // MITRE: T1059.006, T1204.004 // CONF: high | FP: medium | COST: low | REQUIRES: process creation with command line (ProcessRollup2) // FALSE POSITIVES: developers and build agents that use IronPython; the chain runs it from a net462 folder under a user profile, which is unusual // TUNING: exclude developer and build asset groups; keep alerting on ipyw32.exe launched from a user-writable path with a cmd.exe parent #event_simpleName=ProcessRollup2 | FileName=/^ipyw(32)?\.exe$/i or ImageFileName=/\\IronPython[^\\]*\\.*net462\\/i | table([@timestamp, ComputerName, UserName, FileName, ImageFileName, CommandLine, ParentBaseFileName, aid], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.image.path: Created image path
Recommended fields
tgt.process.cmdline: Created command linesrc.process.name: Creating process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.image.path = * | limit 5 | columns event.type, tgt.process.name, tgt.process.image.path
// HUNT: IronPython ipyw32.exe execution (stock interpreter, behavioral detect)
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1059.006, T1204.004
// CONF: high | FP: medium
// FALSE POSITIVES: developers and build agents that use IronPython; the chain runs it from a net462 folder under a user profile, which is unusual
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is ipyw.exe or ipyw32.exe, or its image path has an IronPython folder followed later by a net462 folder. The path regex is case-insensitive by default, which reproduces the CQL i flag.
// REQUIRES: SentinelOne process-creation telemetry with image paths and command lines
// TUNING: Exclude developer and build asset groups; keep alerting on ipyw32.exe launched from a user-writable path with a cmd.exe creator.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A renamed interpreter outside an IronPython folder is not captured by the path branch.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' (tgt.process.name in:anycase ('ipyw.exe', 'ipyw32.exe') or tgt.process.image.path matches '\\\\IronPython[^\\\\]*\\\\.*net462\\\\')
| columns event.time, endpoint.name, src.process.user, tgt.process.name, tgt.process.image.path, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameFolderPath: Created process path
Recommended fields
ProcessCommandLine: Created command lineInitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, FolderPath | take 5
// HUNT: IronPython ipyw32.exe execution (stock interpreter, behavioral detect)
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1059.006, T1204.004
// CONF: high | FP: medium
// FALSE POSITIVES: developers and build agents that use IronPython; the chain runs it from a net462 folder under a user profile, which is unusual
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is ipyw.exe or ipyw32.exe, or its folder path has an IronPython folder followed later by a net462 folder. The (?i) prefix reproduces the CQL i flag.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: Exclude developer and build asset groups; keep alerting on ipyw32.exe launched from a user-writable path with a cmd.exe parent.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A renamed interpreter outside an IronPython folder is not captured by the path branch.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where FileName in~ ("ipyw.exe", "ipyw32.exe") or FolderPath matches regex @"(?i)\\\\IronPython[^\\\\]*\\\\.*net462\\\\"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName
| sort by Timestamp desc
| take 200Looks for: The .plan script passes a Base64-encoded, zlib-compressed Python blob to ipyw32.exe on the command line. Narrower than Q6, so far fewer benign hits.
Known limitations and deployment notes
- PowerQuery: A script read from a file instead of the command line is not visible here.
- PowerQuery assumes: matches is case-insensitive unless :matchcase is given.
- KQL – Defender XDR: A script read from a file instead of the command line is not visible here.
- KQL – Defender XDR assumes: ProcessCommandLine holds the full command line of the created process.
// HUNT: IronPython launched with an inline encoded script // MITRE: T1059.006, T1027 // CONF: high | FP: low | COST: low | REQUIRES: process creation with command line (ProcessRollup2) // FALSE POSITIVES: a developer passing an inline base64 or zlib snippet to IronPython; uncommon // TUNING: none expected; review any hit manually #event_simpleName=ProcessRollup2 | FileName=/^ipyw(32)?\.exe$/i | CommandLine=/(base64|b64decode|zlib|decompress)/i | table([@timestamp, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, aid], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creating process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.type, tgt.process.name, tgt.process.cmdline
// HUNT: IronPython launched with an inline encoded script
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1059.006, T1027
// CONF: high | FP: low
// FALSE POSITIVES: a developer passing an inline base64 or zlib snippet to IronPython; uncommon
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is ipyw.exe or ipyw32.exe and its command line carries base64, b64decode, zlib or decompress. matches is case-insensitive by default, which reproduces the CQL i flag.
// REQUIRES: SentinelOne process-creation telemetry with command lines
// TUNING: None expected; review any hit manually.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A script read from a file instead of the command line is not visible here.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' tgt.process.name in:anycase ('ipyw.exe', 'ipyw32.exe') tgt.process.cmdline matches '(base64|b64decode|zlib|decompress)'
| columns event.time, endpoint.name, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: IronPython launched with an inline encoded script
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1059.006, T1027
// CONF: high | FP: low
// FALSE POSITIVES: a developer passing an inline base64 or zlib snippet to IronPython; uncommon
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is ipyw.exe or ipyw32.exe and its command line carries base64, b64decode, zlib or decompress. contains is case-insensitive, which reproduces the CQL i flag.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: None expected; review any hit manually.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A script read from a file instead of the command line is not visible here.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where FileName in~ ("ipyw.exe", "ipyw32.exe")
| where ProcessCommandLine contains "base64" or ProcessCommandLine contains "b64decode" or ProcessCommandLine contains "zlib" or ProcessCommandLine contains "decompress"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| sort by Timestamp desc
| take 200Looks for: ipyw32.exe fetches the timer6 loader over HTTP from the staging domain, so an IronPython process talking to the internet is the network half of Q6/Q7.
Known limitations and deployment notes
- PowerQuery: A connection the sensor did not observe is not captured.
- PowerQuery assumes: The queried name is in event.dns.request; some schema versions use dns.request instead, so confirm which is populated.
- PowerQuery assumes: Event types are matched case-sensitively as IP Connect and DNS Resolved.
- KQL – Defender XDR: The connection half of the CQL (outbound connections by the interpreter) is not covered; run a DeviceNetworkEvents query on InitiatingProcessFileName for ipyw.exe and ipyw32.exe separately.
- KQL – Defender XDR assumes: ActionType DnsQueryResponse is populated on this tenant.
- KQL – Defender XDR assumes: The name is under DnsQueryString or QueryName in AdditionalFields; the readiness test prints AdditionalFields so you can see which key your tenant uses.
// HUNT: IronPython interpreter making network connections or DNS lookups // MITRE: T1105, T1071.001 // CONF: high | FP: low | COST: low | REQUIRES: network connections and DNS with process context (NetworkConnectIP4, DnsRequest) // FALSE POSITIVES: IronPython scripts that call web services; rare outside developer hosts // TUNING: exclude developer asset groups #event_simpleName=/^(NetworkConnectIP4|DnsRequest)$/ | ContextBaseFileName=/^ipyw(32)?\.exe$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, DomainName, RemoteAddressIP4, RemotePort, aid], limit=200)
2 required fields · View requirements
Required telemetry
- IP Connect
- DNS Resolved
Required fields
event.type: Event typesrc.process.name: Acting process name
Recommended fields
event.dns.request: Queried namedst.ip.address: Destination addressdst.port.number: Destination port
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type in ('IP Connect', 'DNS Resolved') src.process.name = *
| limit 5
| columns event.type, src.process.name// HUNT: IronPython interpreter making network connections or DNS lookups
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1105, T1071.001
// CONF: high | FP: low
// FALSE POSITIVES: IronPython scripts that call web services; rare outside developer hosts
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a connection or DNS event whose acting process is ipyw.exe or ipyw32.exe. On a non-process-creation event the acting process is src.process.*.
// REQUIRES: SentinelOne network and DNS telemetry with process context (IP Connect and DNS Resolved events)
// TUNING: Exclude developer asset groups.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A connection the sensor did not observe is not captured.
// VALIDATION LEVEL: static-reviewed
event.type in ('IP Connect', 'DNS Resolved') src.process.name in:anycase ('ipyw.exe', 'ipyw32.exe')
| columns event.time, endpoint.name, src.process.user, src.process.name, event.type, event.dns.request, dst.ip.address, dst.port.number
| limit 200Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeInitiatingProcessFileName: Initiating process nameAdditionalFields: Event details
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "DnsQueryResponse" | project Timestamp, ActionType, InitiatingProcessFileName, AdditionalFields | take 5
// HUNT: IronPython interpreter making network connections or DNS lookups
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1105, T1071.001
// CONF: high | FP: low
// FALSE POSITIVES: IronPython scripts that call web services; rare outside developer hosts
// EQUIVALENCE: telemetry-gap - The CQL spans two event populations, connections and DNS. Defender keeps them in different tables and a query reads one table, so this partial pivot covers the DNS half through DeviceEvents DnsQueryResponse.
// REQUIRES: Defender for Endpoint DNS events (DeviceEvents, ActionType DnsQueryResponse)
// TUNING: Exclude developer asset groups. Pair with the PowerQuery or CQL tab for the connection half.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: The connection half of the CQL (outbound connections by the interpreter) is not covered; run a DeviceNetworkEvents query on InitiatingProcessFileName for ipyw.exe and ipyw32.exe separately.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceEvents
| where Timestamp >= ago(7d)
| where ActionType == "DnsQueryResponse"
| where InitiatingProcessFileName in~ ("ipyw.exe", "ipyw32.exe")
| extend DnsName = tostring(coalesce(parse_json(AdditionalFields)["DnsQueryString"], parse_json(AdditionalFields)["QueryName"]))
| project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, DnsName
| sort by Timestamp desc
| take 200Looks for: Stage 4: Doina unpacks a portable Node.js runtime plus index.js into C:\ProgramData\NodeView and runs node.exe against it. node.exe is legitimate, so this is behavioral detection only. FP: software that bundles its own Node.js runtime under ProgramData; check the folder name before dismissing. Tuning: exclude known software that ships node.exe under ProgramData in your baseline; the NodeView folder name is the discriminator.
Known limitations and deployment notes
- PowerQuery: A node.exe renamed to another file name is not captured.
- PowerQuery assumes: tgt.process.image.path holds the full Windows image path with backslash separators.
- KQL – Defender XDR: A node.exe renamed to another file name is not captured.
- KQL – Defender XDR assumes: FolderPath holds the full image path of the created process.
// HUNT: node.exe running index.js from ProgramData (NodeView package) // MITRE: T1059.007, T1105 // CONF: high | FP: medium | COST: low | REQUIRES: process creation with command line (ProcessRollup2) // FALSE POSITIVES: software that bundles its own Node.js runtime under ProgramData; check the folder name before dismissing // TUNING: exclude known software that ships node.exe under ProgramData in your baseline; the NodeView folder name is the discriminator #event_simpleName=ProcessRollup2 | FileName=/^node\.exe$/i | ImageFileName=/\\ProgramData\\/i | CommandLine=/index\.js/i | table([@timestamp, ComputerName, UserName, ImageFileName, CommandLine, ParentBaseFileName, aid], limit=200)
4 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.image.path: Created image pathtgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creating process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.image.path = * | limit 5 | columns event.type, tgt.process.name, tgt.process.image.path, tgt.process.cmdline
// HUNT: node.exe running index.js from ProgramData (NodeView package)
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1059.007, T1105
// CONF: high | FP: medium
// FALSE POSITIVES: software that bundles its own Node.js runtime under ProgramData; check the folder name before dismissing
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is node.exe, its image path sits under a ProgramData folder, and its command line names index.js. contains is case-insensitive, which reproduces the CQL i flag.
// REQUIRES: SentinelOne process-creation telemetry with image paths and command lines
// TUNING: Exclude known software that ships node.exe under ProgramData in your baseline; the NodeView folder name is the discriminator.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A node.exe renamed to another file name is not captured.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' tgt.process.name in:anycase ('node.exe') tgt.process.image.path contains '\\ProgramData\\' tgt.process.cmdline contains 'index.js'
| columns event.time, endpoint.name, src.process.user, tgt.process.image.path, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameFolderPath: Created process pathProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, FolderPath, ProcessCommandLine | take 5
// HUNT: node.exe running index.js from ProgramData (NodeView package) // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1059.007, T1105 // CONF: high | FP: medium // FALSE POSITIVES: software that bundles its own Node.js runtime under ProgramData; check the folder name before dismissing // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is node.exe, its path sits under a ProgramData folder, and its command line names index.js. contains is case-insensitive, which reproduces the CQL i flag. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Exclude known software that ships node.exe under ProgramData in your baseline; the NodeView folder name is the discriminator. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A node.exe renamed to another file name is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where FileName =~ "node.exe" | where FolderPath contains "\\ProgramData\\" | where ProcessCommandLine contains "index.js" | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FolderPath, ProcessCommandLine, InitiatingProcessFileName | sort by Timestamp desc | take 200
Looks for: Stage 5: a pirated NetSupport Client is deployed to C:\ProgramData\Prodclear. Client32.exe is a legitimate signed RMM binary, so detect on the location and context only. FP: a sanctioned NetSupport Manager deployment that installs the client under ProgramData. Tuning: exclude hosts and paths of your approved NetSupport deployment; the Prodclear folder name was the observed rogue location.
Known limitations and deployment notes
- PowerQuery: A client renamed to another file name is not captured.
- PowerQuery assumes: tgt.process.image.path holds the full Windows image path with backslash separators.
- KQL – Defender XDR: A client renamed to another file name is not captured.
- KQL – Defender XDR assumes: FolderPath holds the full image path of the created process.
// HUNT: Client32.exe (NetSupport Client) running from ProgramData // MITRE: T1219 // CONF: high | FP: medium | COST: low | REQUIRES: process creation with command line (ProcessRollup2) // FALSE POSITIVES: a sanctioned NetSupport Manager deployment that installs the client under ProgramData // TUNING: exclude hosts and paths of your approved NetSupport deployment; the Prodclear folder name was the observed rogue location #event_simpleName=ProcessRollup2 | FileName=/^client32\.exe$/i | ImageFileName=/\\ProgramData\\/i | table([@timestamp, ComputerName, UserName, ImageFileName, CommandLine, ParentBaseFileName, aid], limit=200)
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.image.path: Created image path
Recommended fields
tgt.process.cmdline: Created command linesrc.process.name: Creating process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.image.path = * | limit 5 | columns event.type, tgt.process.name, tgt.process.image.path
// HUNT: Client32.exe (NetSupport Client) running from ProgramData
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1219
// CONF: high | FP: medium
// FALSE POSITIVES: a sanctioned NetSupport Manager deployment that installs the client under ProgramData
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is client32.exe and its image path sits under a ProgramData folder. contains is case-insensitive, which reproduces the CQL i flag.
// REQUIRES: SentinelOne process-creation telemetry with image paths
// TUNING: Exclude hosts and paths of your approved NetSupport deployment; the Prodclear folder name was the observed rogue location.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A client renamed to another file name is not captured.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' tgt.process.name in:anycase ('client32.exe') tgt.process.image.path contains '\\ProgramData\\'
| columns event.time, endpoint.name, src.process.user, tgt.process.image.path, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameFolderPath: Created process path
Recommended fields
ProcessCommandLine: Created command lineInitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, FolderPath | take 5
// HUNT: Client32.exe (NetSupport Client) running from ProgramData // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1219 // CONF: high | FP: medium // FALSE POSITIVES: a sanctioned NetSupport Manager deployment that installs the client under ProgramData // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: the created process is client32.exe and its path sits under a ProgramData folder. contains is case-insensitive, which reproduces the CQL i flag. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Exclude hosts and paths of your approved NetSupport deployment; the Prodclear folder name was the observed rogue location. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A client renamed to another file name is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(7d) | where FileName =~ "client32.exe" | where FolderPath contains "\\ProgramData\\" | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FolderPath, ProcessCommandLine, InitiatingProcessFileName | sort by Timestamp desc | take 200
Looks for: The Huntress-observed NetSupport C2 is 167.148.201.106 on 725/tcp. Second branch catches a rotated address when Client32.exe uses the same port.
Known limitations and deployment notes
- PowerQuery: A connection the sensor did not observe is not captured.
- PowerQuery assumes: IP Connect events carry the destination port in dst.port.number as a number.
- KQL – Defender XDR: A connection the sensor did not observe is not captured.
- KQL – Defender XDR assumes: DeviceNetworkEvents rows with ActionType ConnectionFailed are also returned, which the CQL connect event would not include.
// HUNT: NetSupport C2: connection to 167.148.201.106 or Client32.exe on port 725 // MITRE: T1219, T1071.001 // CONF: high | FP: low | COST: low | REQUIRES: network connections with process context (NetworkConnectIP4) // FALSE POSITIVES: none expected for the IP; port 725 alone is not used here without the process filter // TUNING: none expected; the IP is single-source so keep it detect and re-verify before any block #event_simpleName=NetworkConnectIP4 | (RemoteAddressIP4="167.148.201.106") or (RemotePort=725 ContextBaseFileName=/^client32\.exe$/i) | table([@timestamp, ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort, aid], limit=200)
4 required fields · View requirements
Required telemetry
- IP Connect
Required fields
event.type: Event typedst.ip.address: Destination addressdst.port.number: Destination portsrc.process.name: Acting process name
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'IP Connect' dst.ip.address = * | limit 5 | columns event.type, dst.ip.address, dst.port.number, src.process.name
// HUNT: NetSupport C2: connection to 167.148.201.106 or Client32.exe on port 725
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1219, T1071.001
// CONF: high | FP: low
// FALSE POSITIVES: none expected for the IP; port 725 alone is not used here without the process filter
// EQUIVALENCE: equivalent-with-schema-assumptions - Same two-branch predicate: a connection to 167.148.201.106, or a connection to port 725 by client32.exe. On a non-process-creation event the acting process is src.process.*.
// REQUIRES: SentinelOne network telemetry with process context (IP Connect events)
// TUNING: None expected; the IP is single-source so keep it detect and re-verify before any block.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A connection the sensor did not observe is not captured.
// VALIDATION LEVEL: static-reviewed
event.type = 'IP Connect' (dst.ip.address = '167.148.201.106' or (dst.port.number = 725 and src.process.name in:anycase ('client32.exe')))
| columns event.time, endpoint.name, src.process.user, src.process.name, dst.ip.address, dst.port.number
| limit 200Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeRemoteIP: Remote addressRemotePort: Remote portInitiatingProcessFileName: Initiating process name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, RemoteIP, RemotePort, InitiatingProcessFileName | take 5
// HUNT: NetSupport C2: connection to 167.148.201.106 or Client32.exe on port 725 // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1219, T1071.001 // CONF: high | FP: low // FALSE POSITIVES: none expected for the IP; port 725 alone is not used here without the process filter // EQUIVALENCE: equivalent-with-schema-assumptions - Same two-branch predicate: a connection to 167.148.201.106, or a connection to port 725 by client32.exe. The acting process is InitiatingProcessFileName. // REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents) // TUNING: None expected; the IP is single-source so keep it detect and re-verify before any block. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A connection the sensor did not observe is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceNetworkEvents | where Timestamp >= ago(7d) | where RemoteIP == "167.148.201.106" or (RemotePort == 725 and InitiatingProcessFileName =~ "client32.exe") | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, RemoteIP, RemotePort, ActionType | sort by Timestamp desc | take 200
Looks for: Sweeps the eight published second-level domains from the Huntress report (finger host, delivery, Doina C2 and Node package hosts). Ships detect only: single reporting lineage.
Known limitations and deployment notes
- PowerQuery: Rotated domains outside the eight listed labels are not covered.
- PowerQuery assumes: The queried name is in event.dns.request; some schema versions use dns.request instead, so confirm which is populated.
- PowerQuery assumes: matches is case-insensitive unless :matchcase is given.
- KQL – Defender XDR: Rotated domains outside the eight listed labels are not covered.
- KQL – Defender XDR assumes: ActionType DnsQueryResponse is populated on this tenant.
- KQL – Defender XDR assumes: The name is under DnsQueryString or QueryName in AdditionalFields; the readiness test prints AdditionalFields so you can see which key your tenant uses.
// HUNT: DNS resolution of the campaign domains (atomic IOC sweep) // MITRE: T1071.001, T1105 // CONF: high | FP: low | COST: medium | REQUIRES: DNS requests (DnsRequest) // FALSE POSITIVES: none expected; any hit warrants host triage // TUNING: none expected; absence of hits is not evidence of absence because the operators rotate barbecue-themed names #event_simpleName=DnsRequest | DomainName=/(^|\.)(grillrecipespro|grillmasterchef|thegrilljournal|homelygrill|perfectgrillrecipes|embersandbites|kiriosyka|liokida)\.com$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, DomainName, aid], limit=500)
2 required fields · View requirements
Required telemetry
- DNS Resolved
Required fields
event.type: Event typeevent.dns.request: Queried name
Recommended fields
src.process.name: Acting process
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'DNS Resolved' event.dns.request = * | limit 5 | columns event.type, event.dns.request
// HUNT: DNS resolution of the campaign domains (atomic IOC sweep) // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1071.001, T1105 // CONF: high | FP: low // FALSE POSITIVES: none expected; any hit warrants host triage // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a DNS resolution whose name is one of the eight campaign labels under .com, with an optional leading subdomain. matches is case-insensitive by default, which reproduces the CQL i flag. // REQUIRES: SentinelOne DNS telemetry (DNS Resolved events) // TUNING: None expected; absence of hits is not evidence of absence because the operators rotate barbecue-themed names. // LOOKBACK: 7d; set the console time picker or LRQ window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Rotated domains outside the eight listed labels are not covered. // VALIDATION LEVEL: static-reviewed event.type = 'DNS Resolved' event.dns.request matches '(^|\\.)(grillrecipespro|grillmasterchef|thegrilljournal|homelygrill|perfectgrillrecipes|embersandbites|kiriosyka|liokida)\\.com$' | columns event.time, endpoint.name, src.process.user, src.process.name, event.dns.request | limit 500
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeAdditionalFields: Event details
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "DnsQueryResponse" | project Timestamp, ActionType, AdditionalFields | take 5
// HUNT: DNS resolution of the campaign domains (atomic IOC sweep) // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1071.001, T1105 // CONF: high | FP: low // FALSE POSITIVES: none expected; any hit warrants host triage // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a DNS response whose name is one of the eight campaign labels under .com, with an optional leading subdomain. Defender has no endpoint DNS table, so DeviceEvents DnsQueryResponse is used and the name is read from AdditionalFields. The (?i) prefix reproduces the CQL i flag. // REQUIRES: Defender for Endpoint DNS events (DeviceEvents, ActionType DnsQueryResponse) // TUNING: None expected; absence of hits is not evidence of absence because the operators rotate barbecue-themed names. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Rotated domains outside the eight listed labels are not covered. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceEvents | where Timestamp >= ago(7d) | where ActionType == "DnsQueryResponse" | extend DnsName = tostring(coalesce(parse_json(AdditionalFields)["DnsQueryString"], parse_json(AdditionalFields)["QueryName"])) | where DnsName matches regex @"(?i)(^|\.)(grillrecipespro|grillmasterchef|thegrilljournal|homelygrill|perfectgrillrecipes|embersandbites|kiriosyka|liokida)\.com$" | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, DnsName | sort by Timestamp desc | take 500
Looks for: The command the victim pastes into Run is recorded in the RunMRU key. Same finger-shaped regex as Q2, applied to the registry value.
Known limitations and deployment notes
- PowerQuery: If the agent policy does not capture RunMRU writes the query returns nothing, which is a telemetry gap and not a clean result.
- PowerQuery assumes: RunMRU value writes are captured as Registry Value Modified events on this tenant.
- PowerQuery assumes: registry.value holds the written string data.
- KQL – Defender XDR: If the sensor does not record RunMRU writes the query returns nothing, which is a telemetry gap and not a clean result.
- KQL – Defender XDR assumes: RunMRU value writes are recorded as RegistryValueSet on this tenant.
- KQL – Defender XDR assumes: RegistryValueData holds the written string data.
// HUNT: Run-dialog (RunMRU) entry shaped like the obfuscated finger command // MITRE: T1204.004 // CONF: medium | FP: low | COST: low | REQUIRES: registry value writes (RegGenericValueUpdate); RunMRU capture depends on sensor policy, validate in tenant // FALSE POSITIVES: none expected // TUNING: none expected; if RunMRU writes are not captured in your tenant this returns nothing, which is a telemetry gap and not a clean result #event_simpleName=/^(RegGenericValueUpdate|AsepValueUpdate)$/ | RegObjectName=/RunMRU/i | RegStringValue=/f[,^_-]*i[,^_-]*n[,^_-]*g[,^_-]*e[,^_-]*r[,^_-]*\s*\S+@[a-z0-9.-]+\.[a-z]{2,}/i | table([@timestamp, ComputerName, UserName, RegObjectName, RegValueName, RegStringValue, aid], limit=200)
3 required fields · View requirements
Required telemetry
- Registry Value Modified
Required fields
event.type: Event typeregistry.keyPath: Registry key pathregistry.value: Registry value data
Recommended fields
registry.valueName: Registry value name
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Registry Value Modified' registry.keyPath contains 'RunMRU' | limit 5 | columns event.type, registry.keyPath, registry.value
// HUNT: Run-dialog (RunMRU) entry shaped like the obfuscated finger command
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1204.004
// CONF: medium | FP: low
// FALSE POSITIVES: none expected
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a registry value write under a RunMRU key whose data holds finger-shaped letters padded with comma, caret, hyphen or underscore, followed by a user@host argument. The CQL also reads a second registry event name; Registry Value Modified is the single SentinelOne event for both.
// REQUIRES: SentinelOne registry telemetry (Registry Value Modified events); RunMRU capture depends on agent policy, so validate in tenant
// TUNING: None expected; if RunMRU writes are not captured in your tenant this returns nothing, which is a telemetry gap and not a clean result.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: If the agent policy does not capture RunMRU writes the query returns nothing, which is a telemetry gap and not a clean result.
// VALIDATION LEVEL: static-reviewed
event.type = 'Registry Value Modified' registry.keyPath contains 'RunMRU' registry.value matches 'f[,^_-]*i[,^_-]*n[,^_-]*g[,^_-]*e[,^_-]*r[,^_-]*\\s*\\S+@[a-z0-9.-]+\\.[a-z]{2,}'
| columns event.time, endpoint.name, src.process.user, registry.keyPath, registry.valueName, registry.value
| limit 200Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceRegistryEvents
Required fields
Timestamp: Event timeActionType: Action typeRegistryKey: Registry keyRegistryValueData: Registry value data
Recommended fields
RegistryValueName: Registry value name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceRegistryEvents | where Timestamp > ago(1d) | where RegistryKey contains "RunMRU" | project Timestamp, ActionType, RegistryKey, RegistryValueData | take 5
// HUNT: Run-dialog (RunMRU) entry shaped like the obfuscated finger command
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1204.004
// CONF: medium | FP: low
// FALSE POSITIVES: none expected
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a registry value write under a RunMRU key whose data holds finger-shaped letters padded with comma, caret, hyphen or underscore, followed by a user@host argument. The (?i) prefix reproduces the CQL i flag.
// REQUIRES: Defender for Endpoint registry telemetry (DeviceRegistryEvents); RunMRU capture depends on sensor policy, so validate in tenant
// TUNING: None expected; if RunMRU writes are not captured in your tenant this returns nothing, which is a telemetry gap and not a clean result.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: If the sensor does not record RunMRU writes the query returns nothing, which is a telemetry gap and not a clean result.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceRegistryEvents
| where Timestamp >= ago(7d)
| where ActionType == "RegistryValueSet"
| where RegistryKey contains "RunMRU"
| where RegistryValueData matches regex @"(?i)f[,^_-]*i[,^_-]*n[,^_-]*g[,^_-]*e[,^_-]*r[,^_-]*\s*\S+@[a-z0-9.-]+\.[a-z]{2,}"
| project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, RegistryKey, RegistryValueName, RegistryValueData
| sort by Timestamp desc
| take 200Looks for: Hosts that run two or more of finger.exe, ipyw32.exe and Client32.exe in the window. Escalate regardless of the individual query ratings.
Known limitations and deployment notes
- PowerQuery: The CQL also lists which tools were seen; this tab returns the counts and first and last seen times only. Run the process-creation card for the tool names.
- PowerQuery assumes: Hosts are grouped by endpoint.name; two hosts that share a name would be merged.
- PowerQuery assumes: estimate_distinct is approximate but exact at this cardinality.
- KQL – Defender XDR: The CQL counts file names without lower-casing; the case-folding here can only merge, never split, hits.
- KQL – Defender XDR assumes: FileName holds the bare image name of the created process.
- KQL – Defender XDR assumes: File names are lower-cased before counting, so Client32.exe and client32.exe count once.
// HUNT: Multi-stage correlation: two or more chain tools on one host // MITRE: T1204.004, T1059.006, T1219 // CONF: high | FP: low | COST: medium | REQUIRES: process creation (ProcessRollup2) // FALSE POSITIVES: a developer host that legitimately runs IronPython and a sanctioned NetSupport client // TUNING: exclude developer and approved remote-support asset groups #event_simpleName=ProcessRollup2 | FileName=/^(finger|ipyw32|ipyw|client32)\.exe$/i | groupBy([aid, ComputerName], function=[count(FileName, distinct=true, as=distinct_tools), collect([FileName])]) | distinct_tools >= 2
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.type: Event typetgt.process.name: Created process nameendpoint.name: Host
Recommended fields
event.time: Event time
Fallbacks
endpoint.name→agent.uuid: Changes the grouping key; agent.uuid is stable across renames.
Readiness test
event.type = 'Process Creation' tgt.process.name = * | limit 5 | columns event.type, tgt.process.name, endpoint.name
// HUNT: Multi-stage correlation: two or more chain tools on one host
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1204.004, T1059.006, T1219
// CONF: high | FP: low
// FALSE POSITIVES: a developer host that legitimately runs IronPython and a sanctioned NetSupport client
// EQUIVALENCE: equivalent-with-schema-assumptions - Same correlation: process creations of finger.exe, ipyw32.exe, ipyw.exe or client32.exe grouped per host, kept when two or more distinct tool names appear. estimate_distinct is an estimate, which is exact at this tiny cardinality.
// REQUIRES: SentinelOne process-creation telemetry
// TUNING: Exclude developer and approved remote-support asset groups.
// LOOKBACK: 7d; set the console time picker or LRQ window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: The CQL also lists which tools were seen; this tab returns the counts and first and last seen times only. Run the process-creation card for the tool names.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' tgt.process.name in:anycase ('finger.exe', 'ipyw32.exe', 'ipyw.exe', 'client32.exe')
| group distinct_tools = estimate_distinct(tgt.process.name), process_events = count(), first_seen = min(event.time), last_seen = max(event.time) by endpoint.name
| filter distinct_tools >= 2
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameDeviceId: Host identifier
Recommended fields
DeviceName: Host name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, DeviceId | take 5
// HUNT: Multi-stage correlation: two or more chain tools on one host
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1204.004, T1059.006, T1219
// CONF: high | FP: low
// FALSE POSITIVES: a developer host that legitimately runs IronPython and a sanctioned NetSupport client
// EQUIVALENCE: equivalent-with-schema-assumptions - Same correlation: process creations of finger.exe, ipyw32.exe, ipyw.exe or client32.exe grouped per host, kept when two or more distinct tool names appear. dcount is an estimate, which is exact at this tiny cardinality.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: Exclude developer and approved remote-support asset groups.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: The CQL counts file names without lower-casing; the case-folding here can only merge, never split, hits.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed-with-warnings; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where FileName in~ ("finger.exe", "ipyw32.exe", "ipyw.exe", "client32.exe")
| summarize DistinctTools = dcount(tolower(FileName)), Tools = make_set(tolower(FileName)), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceId, DeviceName
| where DistinctTools >= 2
| sort by LastSeen desc
| take 200Detection Rules by Platform
Five queries are clean enough to promote to Custom IOA. All are detect-only (Monitor). No rule blocks a legitimate binary and the pack recommends no prevent action.
CrowdStrike: Custom IOA
| IOA name | From | Rule type | Pattern | Exclusions | Action |
|---|---|---|---|---|---|
| DoinaClickFix, finger.exe Execution | Promote Q1 | Process Creation (Windows) | Image finger.exe | Named legacy jump hosts | Detect: High (never Block: stock binary) |
| DoinaClickFix, Obfuscated finger via cmd | Promote Q2 | Process Creation (Windows) | Image cmd.exe; command line matches finger letters with separator padding and a user@host argument | None known | Detect: High |
| DoinaClickFix, IronPython Inline Script | Promote Q7 | Process Creation (Windows) | Image ipyw32.exe or ipyw.exe; command line contains base64 or zlib decode calls | Developer asset group | Detect: High (never Block: stock interpreter) |
| DoinaClickFix, NodeView node.exe | Promote Q9 | Process Creation (Windows) | Image node.exe under \ProgramData\; command line contains index.js | Known ProgramData Node apps | Detect: Medium (never Block: stock runtime) |
| DoinaClickFix, Client32 in ProgramData | Promote Q10 | Process Creation (Windows) | Image client32.exe under \ProgramData\ | Approved NetSupport deployment | Detect: Medium (never Block: legitimate RMM) |
Deployment path
- Endpoint security → Configuration → Custom IOA rule groups. Create a group for this campaign on a pilot prevention policy.
- Create each rule in Monitor severity and leave it one business week.
- Do not raise any rule to Block. All five patterns match legitimate binaries.
- Import the section 10 CSV into IOC Management as detect entries.
Sample alert package (Q1)
- Subject:
[HIGH] Detection: finger.exe executed (ClickFix chain) - Body: finger.exe ran on HOST-01 as user jdoe, parent process shown in the alert. In the Amazon-themed ClickFix chain this retrieves the .plan script that starts the IronPython and Doina stages. Isolate the host, run Q3, Q6 and Q14 on the same aid, and begin the section 14 runbook.
- Triage: (1) confirm the parent was explorer.exe or cmd.exe, (2) read the command line for a user@host argument, (3) run Q3 and Q4 for the port 79 connection and resolved host, (4) run Q5 to Q7 for curl, tar and IronPython within 5 minutes, (5) run Q9 and Q10 for NodeView and Prodclear, (6) check the user's browser history for the ad-click markers, (7) isolate and open credential resets for the user.
Investigate-only queries
- Q3, Q4, Q5, Q6, Q8, Q11, Q12, Q13, Q14: schedule to a hunt queue. Q14 is the correlation query: any host it returns escalates regardless of individual ratings.
SentinelOne: custom detection rules
| Rule | Source query | Run as | Notes |
|---|---|---|---|
| DoinaClickFix, finger.exe Execution | Q1, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, Obfuscated finger via cmd | Q2, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, IronPython Inline Script | Q7, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, NodeView node.exe | Q9, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, Client32 in ProgramData | Q10, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
Create the rule with POST /web/api/v2.1/cloud-detection/rules, queryType: "scheduled" and queryLang: "2.0", with the query in data.scheduledParams.query. Use 2.0 operator spellings: a rule written with the 1.0 token ContainsCIS was accepted and never fired on a tenant. The API does not warn, so lint the body before deploying.
Microsoft Defender: custom detection rules
| Rule | Source query | Run as | Notes |
|---|---|---|---|
| DoinaClickFix, finger.exe Execution | Q1, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, Obfuscated finger via cmd | Q2, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, IronPython Inline Script | Q7, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, NodeView node.exe | Q9, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
| DoinaClickFix, Client32 in ProgramData | Q10, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
Q3, Q4, Q5, Q6, Q8, Q11, Q12, Q13 and Q14 stay a hunt in this pack, not a rule. A rule can generate only 150 alerts per run. Continuous (NRT) frequency needs a single table and no comment lines, so delete the generated comment header from the query first.
Machine-Readable IOC Appendix
Read before importing: all 18 rows import as detect. They rest on one primary report, so none is prevent. The legitimate binaries (finger, curl, tar, ipyw32, node, Client32) are deliberately absent. Expiries are 6 months for infrastructure and hashes, 3 months for the ad-landing subdomain.
type,value,action,severity,expiration,description,tags domain,grillrecipespro.com,detect,high,2027-04-29,Attacker finger/.plan host (finger user@domain),campaign:DoinaAmazonClickFix domain,grillmasterchef.com,detect,high,2027-04-29,Doina stage host (timer6 and v6 URLs),campaign:DoinaAmazonClickFix domain,thegrilljournal.com,detect,high,2027-04-29,Reported attacker infrastructure and exfil host,campaign:DoinaAmazonClickFix domain,homelygrill.com,detect,high,2027-04-29,Reported attacker infrastructure and exfil host,campaign:DoinaAmazonClickFix domain,perfectgrillrecipes.com,detect,high,2027-04-29,Stealer initial check-in host,campaign:DoinaAmazonClickFix domain,embersandbites.com,detect,medium,2027-04-29,Reported attacker infrastructure (role not detailed),campaign:DoinaAmazonClickFix domain,kiriosyka.com,detect,high,2027-04-29,Node package delivery host (sqr-viewertr6),campaign:DoinaAmazonClickFix domain,liokida.com,detect,high,2027-04-29,Node and stealer package host embedded in stealer,campaign:DoinaAmazonClickFix domain,orders-amazon-com.advancedfences.com,detect,high,2027-01-29,Sponsored-ad landing subdomain (spoofed Amazon lure),campaign:DoinaAmazonClickFix ipv4,167.148.201.106,detect,high,2027-04-29,Rogue NetSupport C2 (725/tcp; also hosts an unrelated restaurant site),campaign:DoinaAmazonClickFix sha256,d864b28c302db9ed10baf8def0fb26bac0b97d27349d22c4e9390d30fce54649,detect,high,2027-04-29,timer6 loader script,campaign:DoinaAmazonClickFix sha256,891cc3ca2d29e9b185a8913e93810f48859d2be1f23ddd977a15cdbeed509e0e,detect,high,2027-04-29,v6 (Doina) RC4-decrypted body,campaign:DoinaAmazonClickFix sha256,87a047160eb1a9b92e6ede511252dfc624b0f5a7185ac2cec1c7ebde8cdf4eea,detect,high,2027-04-29,index.js obfuscated Node loader,campaign:DoinaAmazonClickFix sha256,035d396b3a329f59d680dc36a0a639eb7fb306bea97e42f5f53293dce817e8b7,detect,high,2027-04-29,Nodeview.zip package,campaign:DoinaAmazonClickFix sha256,24a293a662600b2292d9259baebba1c09b41df82a5c9c20f8572625ae759696f,detect,high,2027-04-29,Decrypted payload attributed to Lumma Stealer (Huntress hedges),campaign:DoinaAmazonClickFix sha256,b8419a676a3d4af04ea98287a2b85052fae5c06e6278ca9d8bfa4b5b396b39a6,detect,high,2027-04-29,Decrypted payload attributed to Lumma Stealer (Huntress hedges),campaign:DoinaAmazonClickFix sha256,42527a4388ef4ade6f4eaf52706f0ab31cb4e9ced3a56a6b15402551f9f01208,detect,high,2027-04-29,Decrypted payload attributed to Lumma Stealer (Huntress hedges),campaign:DoinaAmazonClickFix sha256,408b7fc4f08f2a9044046025f05995e8cbd5c96c39844aaa8a7eb8ee4697ea89,detect,high,2027-04-29,Decrypted payload attributed to Lumma Stealer (Huntress hedges),campaign:DoinaAmazonClickFix
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert DomainName,grillrecipespro.com,Audit,Attacker finger/.plan host,Attacker finger/.plan host (finger user@domain),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,grillmasterchef.com,Audit,Doina stage host,Doina stage host (timer6 and v6 URLs),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,thegrilljournal.com,Audit,Reported attacker infrastructure and exfil host,Reported attacker infrastructure and exfil host,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,homelygrill.com,Audit,Reported attacker infrastructure and exfil host,Reported attacker infrastructure and exfil host,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,perfectgrillrecipes.com,Audit,Stealer initial check-in host,Stealer initial check-in host,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,embersandbites.com,Audit,Reported attacker infrastructure,Reported attacker infrastructure (role not detailed),2027-04-29T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True DomainName,kiriosyka.com,Audit,Node package delivery host,Node package delivery host (sqr-viewertr6),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,liokida.com,Audit,Node and stealer package host embedded in stealer,Node and stealer package host embedded in stealer,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,orders-amazon-com.advancedfences.com,Audit,Sponsored-ad landing subdomain,Sponsored-ad landing subdomain (spoofed Amazon lure),2027-01-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True IpAddress,167.148.201.106,Audit,Rogue NetSupport C2,Rogue NetSupport C2 (725/tcp; also hosts an unrelated restaurant site),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,d864b28c302db9ed10baf8def0fb26bac0b97d27349d22c4e9390d30fce54649,Audit,timer6 loader script,timer6 loader script,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,891cc3ca2d29e9b185a8913e93810f48859d2be1f23ddd977a15cdbeed509e0e,Audit,v6,v6 (Doina) RC4-decrypted body,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,87a047160eb1a9b92e6ede511252dfc624b0f5a7185ac2cec1c7ebde8cdf4eea,Audit,index.js obfuscated Node loader,index.js obfuscated Node loader,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,035d396b3a329f59d680dc36a0a639eb7fb306bea97e42f5f53293dce817e8b7,Audit,Nodeview.zip package,Nodeview.zip package,2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,24a293a662600b2292d9259baebba1c09b41df82a5c9c20f8572625ae759696f,Audit,Decrypted payload attributed to Lumma Stealer,Decrypted payload attributed to Lumma Stealer (Huntress hedges),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,b8419a676a3d4af04ea98287a2b85052fae5c06e6278ca9d8bfa4b5b396b39a6,Audit,Decrypted payload attributed to Lumma Stealer,Decrypted payload attributed to Lumma Stealer (Huntress hedges),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,42527a4388ef4ade6f4eaf52706f0ab31cb4e9ced3a56a6b15402551f9f01208,Audit,Decrypted payload attributed to Lumma Stealer,Decrypted payload attributed to Lumma Stealer (Huntress hedges),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,408b7fc4f08f2a9044046025f05995e8cbd5c96c39844aaa8a7eb8ee4697ea89,Audit,Decrypted payload attributed to Lumma Stealer,Decrypted payload attributed to Lumma Stealer (Huntress hedges),2027-04-29T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
Column names and allowed values are from Microsoft's documented parameter table. Check each expirationTime before importing: an indicator whose date has passed is already expired. Download the sample CSV from the Import page to confirm the column order before a bulk import. Each file takes at most 500 indicators. Learn describes the import per entity type, so if a mixed file is rejected, split it by indicatorType. An imported indicator does nothing unless the matching Advanced features are on (for example Custom network indicators and Allow or block file).
This pack does not ship a SentinelOne import block: the import format could not be verified against SentinelOne's own documentation. Export a sample from your console and the block can be added with confidence.
Doina via Amazon-themed ClickFix - behavioral signatures B1 finger.exe executed, or any process connecting to 79/tcp from a workstation B2 cmd.exe command line with finger letters padded by , ^ - _ and a user@host argument B3 curl.exe fetching IronPython.3.4.2.zip; tar.exe extracting a .pdf archive B4 ipyw32.exe running from a net462 folder under a user profile with an inline base64/zlib script B5 ipyw32.exe making network connections to the staging domain B6 node.exe running index.js from C:\ProgramData\NodeView B7 Client32.exe running from C:\ProgramData\Prodclear (or any ProgramData folder) B8 Client32.exe or any process connecting out on 725/tcp B9 Referrer chain with aclk / gclid ad-click markers ending at a Google Sites sign-in page KEY INSIGHT: every executable in the chain is legitimate. Detect the behavior and location, never the binary name alone. NO PERSISTENCE reported by Huntress: absence of a scheduled task or Run key does not clear a host.
Finger target : user account on grillrecipespro[.]com, TCP 79 (plaintext)
.plan script : hidden below 68 extra carriage returns
Archive : IronPython.3.4.2.zip saved in the user Temp directory with a .pdf extension
Interpreter : ipyw32.exe from the \net462 folder of the unpacked IronPython
Stage URLs : /90e4a147-1234-505c-a024-062e543b5df8/timer6 and /v6 on grillmasterchef[.]com
Node package path : /KsxmmKiN5N0j/sqr-viewertr6 (kiriosyka[.]com), /sqr-viewertr6z (liokida[.]com)
Node install folder : C:\ProgramData\NodeView (node.exe + index.js)
NetSupport folder : C:\ProgramData\Prodclear (Client32.exe), 725/tcp, /fakeurl.htm
Loader behaviors : timer6 = Cyrillic-substituted Base64 Python, 19-byte XOR shellcode key;
v6 = RC4, key in the first 64 bytes; payload files AES-256-GCM
Attribution : none. Doina (vendor consensus); Lumma Stealer is hedged ("allegedly")
# Run dialog removed for the user? Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer' -Name NoRun -ErrorAction SilentlyContinue # Outbound finger / NetSupport port rules present? Get-NetFirewallRule -DisplayName 'HuntPack Block Outbound TCP 79','HuntPack Block Outbound TCP 725' -ErrorAction SilentlyContinue | Select-Object DisplayName, Enabled, Action # Command-line process auditing on (needed for native 4688 hunts)? Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit' -Name ProcessCreationIncludeCmdLine_Enabled -ErrorAction SilentlyContinue # Any chain folders present? (read-only) Test-Path 'C:\ProgramData\NodeView'; Test-Path 'C:\ProgramData\Prodclear'
Detection Validation Gates
Work these in order. A query that has not cleared gate 2 has no business being an alert.
| Gate | What to prove | How | Pass criteria |
|---|---|---|---|
| G1 · Telemetry ready | The events each query depends on arrive in your tenant | Run each filter alone over 24h with count(). Confirm process creation with command line, network connections with a populated remote port, DNS requests with process name (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceNetworkEvents), and RunMRU registry writes (Q12). | Non-zero counts and populated fields. A zero RunMRU count means the telemetry is not collected. |
| G2 · Benign baseline | You know what normal looks like | Run Q1 to Q14 over 30 days and classify every hit. Expect developer hosts on Q5, Q6, Q9 and a sanctioned NetSupport estate on Q10. | Every recurring hit is explained and excluded, or escalated. |
| G3 · Positive test | The query fires on the behavior it claims to detect | On an isolated test host in a change window: run finger.exe against a local lab host (Q1, Q3); copy a harmless executable renamed to client32.exe into a throwaway ProgramData folder and start it (Q10). Do not use real malware or the clipboard command. | The matching query fires. |
| G4 · Negative test | Your exclusions did not blind the query | Re-run G3 with the tuning exclusions applied. | Test events still fire. |
| G5 · Promotion | Safe to alert | Custom IOA in Monitor mode for one business week on a pilot policy. | Zero unexplained detections. |
| G6 · Response readiness | The team can act on a hit | Tabletop the section 14 runbook. | Analyst states the credential, session and wallet reset step without prompting. |
Hardening: Tiered
Controls are ordered by leverage against this chain. It needs a Run dialog, an open path to 79/tcp, a user-writable place to run an interpreter, and no one watching ProgramData. Authority is MITRE mitigations plus the platform source named in each control. These are organization-level hardening controls and are separate from any Falcon prevent policy.
I1 · Block outbound 79/tcp (and 725/tcp) at the host and perimeter firewall
Finger has no business use on modern endpoints and the stage-1 fetch needs it. The NetSupport C2 used 725/tcp.
Authority: MITRE M1037 (Filter Network Traffic); advice to block outbound finger on port 79 appears in the Security Online and BlueTeamCoolTeam write-ups. Windows Defender Firewall outbound rule. Verify: Get-NetFirewallRule (P1). Risk: low; inventory any legacy finger use first.
I2 · Remove the Run dialog for users who do not need it
ClickFix needs the victim to paste into Run. Huntress notes it does not stop variants that use PowerShell, a command prompt or the Explorer address bar, so treat this as partial.
Authority: MITRE M1042 (Disable or Remove Feature or Program). GPO Remove Run menu from Start Menu (NoRun). Verify: P2. Risk: medium; exempt IT and power users.
I3 · Turn on command-line process auditing
Without it, native 4688 hunts and many SIEM detections see no command line.
Authority: MS Security Baseline (Audit Process Creation, include command line). Verify: P3. Risk: low; raises log volume and may log secrets passed on command lines.
N1 · Application control in audit mode for finger.exe and ProgramData executables
Audit, then enforce, execution of finger.exe, executables under ProgramData and IronPython folders under user profiles.
Authority: MITRE M1038 (Execution Prevention); ASD Essential Eight application control; AppLocker or WDAC. Verify: P4 plus AppLocker audit events. Risk: medium; software installed under ProgramData needs allow rules.
N2 · Egress filtering: default-deny on nonstandard outbound ports for user workstations
Catches finger (79), NetSupport (725) and similar one-offs together. Authority: MITRE M1037. Risk: medium; needs an application port inventory.
N3 · Web filtering for IronPython release downloads from non-developer groups
The chain fetches IronPython.3.4.2.zip from the public GitHub release URL. Alert or block that path for users who do not develop on .NET. Authority: MITRE M1021 (Restrict Web-Based Content). Risk: low for non-developers.
S1 · Allow-list based application control (WDAC) for standard users
Removes the entire class of unsigned and user-writable interpreters and unexpected node.exe or Client32.exe copies. Authority: MITRE M1038; Microsoft WDAC guidance; CIS Controls v8 2.5 and 2.7. Risk: high effort.
S2 · Paid-search and malvertising controls plus ClickFix user awareness
No legitimate CAPTCHA asks anyone to paste text into an operating system command prompt. Teach that rule, and where a managed browser policy exists, funnel brand logins through bookmarks or a password manager rather than search ads. Report ad-click identifiers (aclk, gclid) to the ad platform. Authority: MITRE M1017 (User Training). ⚠ best-practice, no formal benchmark for the browser-policy half.
S3 · Credential hygiene for stealer exposure
Phishing-resistant MFA, no browser-stored passwords or wallets on managed endpoints, and short session lifetimes limit what an infostealer takes. Authority: MITRE M1032 (Multi-factor Authentication), M1027 (Password Policies).
Deployable Playbooks
Test every playbook on a pilot ring before fleet deployment. Each lists its prerequisites, reboot requirement and rollback.
P1 · Block outbound 79/tcp and 725/tcp (Windows Defender Firewall)
# Prerequisites: local administrator or GPO; confirm no legacy finger use and no sanctioned NetSupport estate on 725/tcp # Reboot required: no # Rollback: Remove-NetFirewallRule -DisplayName 'HuntPack Block Outbound TCP 79','HuntPack Block Outbound TCP 725' New-NetFirewallRule -DisplayName 'HuntPack Block Outbound TCP 79' -Direction Outbound -Protocol TCP -RemotePort 79 -Action Block -Profile Any New-NetFirewallRule -DisplayName 'HuntPack Block Outbound TCP 725' -Direction Outbound -Protocol TCP -RemotePort 725 -Action Block -Profile Any # Verify Get-NetFirewallRule -DisplayName 'HuntPack Block Outbound TCP 79','HuntPack Block Outbound TCP 725' | Select-Object DisplayName, Enabled, Action
P2 · Remove the Run dialog (per-user policy)
# Prerequisites: run as the target user, or push through GPO / logon script. Pilot ring first; exempt IT staff.
# Reboot required: no (sign out and back in, or restart Explorer)
# Rollback: Remove-ItemProperty -Path $k -Name NoRun then sign out and back in
$k = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer'
if (-not (Test-Path $k)) { New-Item -Path $k -Force | Out-Null }
New-ItemProperty -Path $k -Name NoRun -PropertyType DWord -Value 1 -Force | Out-Null
# Verify
Get-ItemProperty -Path $k -Name NoRun
P3 · Include the command line in process-creation events
# Prerequisites: local administrator; Audit Process Creation (Success) must also be enabled by audit policy
# Reboot required: no
# Rollback: Remove-ItemProperty -Path $k -Name ProcessCreationIncludeCmdLine_Enabled
$k = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit'
if (-not (Test-Path $k)) { New-Item -Path $k -Force | Out-Null }
New-ItemProperty -Path $k -Name ProcessCreationIncludeCmdLine_Enabled -PropertyType DWord -Value 1 -Force | Out-Null
auditpol /set /subcategory:"Process Creation" /success:enable
# Verify
Get-ItemProperty -Path $k -Name ProcessCreationIncludeCmdLine_Enabled
P4 · AppLocker audit-only rule for finger.exe
# Prerequisites: local administrator; Application Identity service running. Audit mode logs to
# Applications and Services Logs > Microsoft > Windows > AppLocker > EXE and DLL. Nothing is blocked.
# Reboot required: no
# Rollback: Set-AppLockerPolicy -XmlPolicy "$env:TEMP\applocker-backup.xml"
Get-AppLockerPolicy -Local -Xml | Out-File "$env:TEMP\applocker-backup.xml" -Encoding utf8
$x = @'
<AppLockerPolicy Version="1">
<RuleCollection Type="Exe" EnforcementMode="AuditOnly">
<FilePathRule Id="7b2c1d10-4a1e-4d6b-9a52-0d3f6a1c9e01" Name="Allow all (audit baseline)" Description="" UserOrGroupSid="S-1-1-0" Action="Allow">
<Conditions><FilePathCondition Path="*" /></Conditions>
</FilePathRule>
<FilePathRule Id="7b2c1d10-4a1e-4d6b-9a52-0d3f6a1c9e02" Name="Audit finger.exe" Description="Doina ClickFix hunt" UserOrGroupSid="S-1-1-0" Action="Deny">
<Conditions><FilePathCondition Path="%SYSTEM32%\finger.exe" /></Conditions>
</FilePathRule>
</RuleCollection>
</AppLockerPolicy>
'@
$p = "$env:TEMP\huntpack-finger-audit.xml"
$x | Out-File $p -Encoding utf8
Set-AppLockerPolicy -XmlPolicy $p -Merge
# Verify
Get-AppLockerPolicy -Effective -Xml | Select-String 'finger.exe'
P5 · Read-only host check for chain folders
# Prerequisites: run as administrator on the suspect host; read-only, changes nothing
# Reboot required: no
# Rollback: none required, no state is changed
$paths = 'C:\ProgramData\NodeView','C:\ProgramData\Prodclear'
foreach ($p in $paths) { if (Test-Path $p) { Get-ChildItem $p -Recurse -Force -ErrorAction SilentlyContinue | Select-Object FullName, Length, LastWriteTime } }
Get-ChildItem "$env:TEMP" -Filter 'IronPython*' -Force -ErrorAction SilentlyContinue | Select-Object FullName, Length, LastWriteTime
Get-ChildItem "$env:LOCALAPPDATA" -Recurse -Filter 'ipyw32.exe' -ErrorAction SilentlyContinue | Select-Object FullName
Containment Runbook
Sequencing rule: the stealer takes credentials, browser sessions and wallet data in the first minutes. Isolate the host, but do not let endpoint work delay credential, session and wallet resets for the user.
| Phase | Actions | Owner | Evidence to capture |
|---|---|---|---|
| C0 · Triage 0 to 15 min | Confirm which query fired. Identify the user, host and time. Check whether Q1, Q3, Q6, Q9 and Q10 also fired on the same aid; run Q14. | SOC L1 | Process tree; triggering query output; aid and ComputerName |
| Isolate · C1 15 to 30 min | Network-contain the host in Falcon. Do not power off: Doina, the shellcode and the stealer run in memory. Run P5 and capture a memory image if policy allows. | SOC L2 | Containment timestamp; memory image; P5 output |
| Isolate: by platform | CrowdStrike: network-contain the host. SentinelOne: network quarantine (Disconnect from Network; confirm the label in your console version). Microsoft Defender: Isolate device (full network isolation). Keep the host powered on for memory capture on all of them. | SOC L2 | Containment timestamp; memory image; P5 output |
| C2 · Credentials in parallel with C1 | Reset the user's passwords and revoke browser and SaaS sessions and tokens; rotate saved password-manager master secrets if the manager ran on the host; move crypto-wallet funds and rotate seeds for wallets (Electrum, Ledger, Atomic Wallet) that were installed; reset email-client credentials. These are the application classes named in the report. | IAM / Identity | Reset and revocation log |
| C3 · Network block | Add the section 10 domains and the IP to DNS and proxy blocklists, and block 79/tcp and 725/tcp egress (P1). Search N2, N4 and N6 for other hosts. | Network team | Hunt results across the estate |
| C4 · Eradicate | Preferred: reimage. The chain places two memory-resident loaders, a Node.js stage and a remote-management client, so cleanup of one host cannot prove a clean state. If reimage is not possible, stop node.exe and Client32.exe running from the chain folders, preserve then remove C:\ProgramData\NodeView and C:\ProgramData\Prodclear, and remove the IronPython folder and .pdf archive from the user profile. Huntress reports no persistence mechanism; still check run keys, scheduled tasks and services. | SOC L2 / IT | File hashes; removal log |
| C5 · Recover | Rejoin only after reimage or verified clean scan, with credentials already reset. Watch the host 14 days with Q14 and Q13. Apply I1 to I3. | IT | 14-day clean report |
| C6 · Report | Report the sponsored ad and the Google Sites page to the providers; share new domains with your intel provider. Update the ticket and this pack. | SOC lead | Ticket, report receipts |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Hardening | CrowdStrike coverage | SentinelOne | Defender XDR |
|---|---|---|---|---|---|---|---|
| T1204.004 | Run-dialog paste of an obfuscated finger command | Q2, Q12 | Yes (Q2) | I2, S2 | GOOD (Q12 depends on RunMRU capture) | Good, with assumptions | Good, with assumptions |
| T1218 / T1105 | finger.exe fetch of the .plan script over 79/tcp | Q1, Q3, Q4 | Yes (Q1) | I1, N1, N2 | GOOD | Good, with assumptions | Good, with assumptions |
| T1105 / T1036.008 | curl IronPython download, tar unpack of a .pdf | Q5 | No | N3 | PARTIAL (developer FP) | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| T1059.006 | ipyw32.exe with an inline encoded script | Q6, Q7, Q8 | Yes (Q7) | N1, S1 | GOOD | Good, with assumptions | Partial: Q8 telemetry gap |
| T1059.007 | node.exe running index.js from ProgramData\NodeView | Q9 | Yes (Q9) | N1, S1 | PARTIAL (folder-name dependent) | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| T1219 | Pirated NetSupport Client at ProgramData\Prodclear, 725/tcp | Q10, Q11 | Yes (Q10) | I1, N2 | GOOD | Good, with assumptions | Good, with assumptions |
| T1071.001 | C2 and delivery domains | Q13 | IOC Mgmt | N2 | GOOD (perishable; operators rotate names) | Good, with assumptions | Good, with assumptions |
| Multi-stage | Chain tools co-occurring on one host | Q14 | No | S1 | GOOD | Good, with assumptions | Good, with assumptions |
| T1583.008 | Sponsored ad to spoofed Amazon sign-in | None | No | S2 | GAP (outside endpoint telemetry; native hunt N1) | GAP | GAP |
| T1620 / T1055.012 | Reflective loading of Doina; sRDI and process hollowing of the stealer | None | No | S1 | GAP (in-memory; rely on the sensor behavioral engine) | GAP | GAP |
| T1555.003 / T1005 | Credential and wallet theft, HTTPS and WebSocket exfil | None | No | S3 | GAP (handled by response; WebSocket content is outside endpoint telemetry) | GAP | GAP |
Gap summary
- The ad and lure page have no endpoint signal. Use proxy and browser history (N1).
- The in-memory stages are not hunted. Doina and the stealer are injected; this pack detects the loaders and consequences, not the injection.
- Indicators are perishable and single-lineage. The operators keep adding barbecue-themed domains.
- No persistence is claimed because Huntress reports none.
Validation: gates G1 to G6 in section 11 govern promotion. No query proceeds to alert before clearing G2, and none recommends blocking a legitimate binary.
Hunt Summary Ticket
TITLE: Amazon-themed ClickFix to Doina via finger.exe .plan, IronPython, Node.js stage, suspected Lumma, NetSupport
SEVERITY: High - Huntress report 2026-10-21; at least three Huntress customers affected; no named actor
SCOPE: All Windows endpoints with interactive users. Priority: users who search for Amazon business
sign-in pages and users who store browser credentials or crypto wallets.
HYPOTHESIS: A user followed a sponsored result to a spoofed Amazon sign-in, passed a fake CAPTCHA and pasted an
obfuscated command into Run. finger.exe fetched a .plan script from an attacker domain over 79/tcp;
curl and tar unpacked IronPython; ipyw32.exe loaded timer6 and then Doina in memory; a Node.js stage
injected a suspected Lumma Stealer; a pirated NetSupport Client was installed.
QUERIES: Q1 finger.exe execution CONF high | FP low | COST low
Q2 obfuscated finger in cmd.exe CONF high | FP low | COST low
Q3 outbound TCP/79 CONF high | FP low | COST low
Q4 finger.exe DNS lookup CONF high | FP low | COST low
Q5 curl IronPython / tar on .pdf CONF med | FP med | COST low
Q6 ipyw32.exe execution CONF high | FP med | COST low
Q7 IronPython inline encoded script CONF high | FP low | COST low
Q8 IronPython network / DNS CONF high | FP low | COST low
Q9 node.exe index.js in ProgramData CONF high | FP med | COST low
Q10 Client32.exe in ProgramData CONF high | FP med | COST low
Q11 NetSupport C2 IP / port 725 CONF high | FP low | COST low
Q12 RunMRU finger-shaped entry CONF med | FP low | COST low
Q13 campaign domain DNS sweep CONF high | FP low | COST med
Q14 multi-tool correlation on one host CONF high | FP low | COST med
DO FIRST: 1. Q1, Q3 and Q14 across 30 days (finger is near-zero FP on a modern fleet)
2. Q13 retrospective sweep (perishable)
3. Q6 to Q10 on any host with a Q1 or Q3 hit
4. Native hunt N1 (proxy ad-click markers) and N2 (firewall 79/725)
FINDINGS: [ ] Q1 hits: ______ [ ] Q3 hits: ______ [ ] Q13 hits: ______
[ ] Q9 hits: ______ [ ] Q10 hits: ______
[ ] Hosts with 2+ chain tools (Q14): ______
[ ] Users requiring credential, session and wallet reset: ______
GAPS: - Sponsored ad and lure page: outside endpoint telemetry (native hunt N1)
- In-memory Doina, shellcode and stealer injection not hunted
- Credential theft covered by response, not detection
- Indicators single-lineage (Huntress), so all ship detect; Lumma identification is hedged
- No persistence reported by Huntress; none claimed
ACTIONS: [ ] Promote Q1, Q2, Q7, Q9, Q10 to Custom IOA (Monitor mode, pilot policy, 1 week)
[ ] Import the section 10 CSV into Falcon IOC Management (detect)
[ ] Deploy Immediate hardening: I1 egress 79/725, I2 Run dialog, I3 command-line auditing
[ ] Confirm DnsRequest process attribution and RunMRU capture (gate G1)
OWNER: ______________________
VERSION: v0.1 - 2026-10-29
Changelog
References
| Tier | Source | Used for | Accessed |
|---|---|---|---|
| 1 | Huntress: Amazon-Themed ClickFix Campaign Serves Up Finger-Licking Malware | Primary report: chain, infrastructure, hashes | 2026-10-29 |
| 2 | Security Online: Amazon ClickFix Campaign Abuses Windows Finger Command | Corroboration of chain, no-attribution statement, port 79 control | 2026-10-29 |
| 2 | SigmaHQ: Network Connection Initiated via Finger.EXE | Independent detection logic for finger.exe network activity | 2026-10-29 |
| 3 | BlueTeamCoolTeam: Seven layers of obfuscation, one 1970s LOLBIN | Independent confirmation of the technique (different campaign; no indicators used) | 2026-10-29 |
| ref | MITRE ATT&CK | Technique IDs in section 6 and mitigation M-numbers in section 12 | 2026-10-29 |
Provenance statement. Every campaign domain, address and hash is reproduced from the Huntress report snapshot saved with this pack. The BlueTeamCoolTeam post describes a different finger and IronPython campaign and contributes no indicator. Search summaries were not used as a value source. The obfuscated ClickFix command is not published by the source and is not reproduced here.