AOMEI Backupper Supply-Chain Tampering (UNC7017): CROSSROAD and SHINYROAD
Executive Summary
Between April and July 2026 an actor tracked by Google Threat Intelligence Group and Mandiant as UNC7017 tampered with the update and notification configuration files that AOMEI Backupper and AnyViewer fetch from AOMEI infrastructure. Each tampering window lasted about 5 to 10 minutes and then reverted to authentic content, so a later check of the vendor URL shows only clean files. Any machine whose AOMEI background services polled during a window was handed a CROSSROAD downloader, which stages the SHINYROAD backdoor (a signed Radmin launcher sideloading a malicious DLL that registers a COM server hosting an embedded PocketPy interpreter). In mid-July the actor also pushed ADAPTAGENT, an Adaptix C2 agent disguised as Nitro PDF software, and added a 1x1 invisible popup that coerces WebDAV/NTLM authentication to an actor server.
No software vulnerability was exploited: the client followed its own update logic against unauthenticated remote configuration. The highest-value defensive angle is therefore endpoint lineage, not the vendor URL: an AOMEI updater process starting an executable from its own html or tmphtml directory (Q1), the two scheduled task names (Q3), the Radmin launcher with --runtimechk (Q4), and user-profile sideload drops (Q5).
Source Review & Web Hunter Notes
| Tier | Source | Key finding | Carry forward | Snapshot |
|---|---|---|---|---|
| 1 | Google Threat Intelligence Group / Mandiant: Investigating Ephemeral Supply Chain Tampering (2026-10-15) | Authoritative campaign report: tampering windows, config diffs, CROSSROAD and SHINYROAD internals, ADAPTAGENT, IOCs, YARA, ATT&CK | Yes | 01-mandiant-gtig.txt |
| 1 | ManageEngine Threat Analysis Lab: AOMEI Backupper supply-chain compromise (2026-10-16) | Discovery telemetry from 28 May and 16 July, process lineage, payload analysis, Sigma rules, its own hash table | Yes (partial overlap with source 1) | 02-manageengine.txt |
Decisions and discrepancies
- Independence. The Mandiant report credits ManageEngine with the initial discovery and the two vendors coordinated, so their prose overlaps. ManageEngine does carry its own endpoint telemetry and its own hash table. Only indicators that appear in both snapshots (the BlkScrDl.dll hash, the component.zip hash, and 8.218.65.184) were allowed to ship as prevent or critical; everything else is detect.
- Domain spelling. One ManageEngine table writes the package host without the hyphen (storagedata). The Mandiant report and ManageEngine's own body text use
storage-data.com. The hyphenated form is shipped; the unhyphenated variant is not. - Radmin location. Mandiant places the components in
%LOCALAPPDATA%\Radmin\; ManageEngine records the genuine Radmin Server installer writing to a Program Files (x86) rserver30 folder. Queries match the\Radmin\folder name and the user-profile path separately rather than assuming one. - Guardrails. Mandiant states CROSSROAD aborts when the country code is CN. ManageEngine adds that it also terminates when geolocation cannot be resolved and when specific endpoint security agents are present (agents not named). Only the CN rule is in the Mandiant text.
- Technique ID. Mandiant maps DLL hijacking to T1574.001; ManageEngine uses T1574.002. This pack follows the Mandiant IDs.
- Not shipped as indicators: the three legitimate file hashes (WinLpcDl.dll, WinLpcDl2.dll, Nitro_Scan2PDFApp.exe) are listed as context only; the benign decoy file hash is omitted; 96.126.191.98 is the IP found in the actor's allowlist file (a target filter, not shown to be actor infrastructure) and is a hunt pivot, not a block; the two declared MD5 values for the malicious archives are shipped as detect.
- Radmin never armed. ManageEngine found Radmin Server installed without a configured address or credential and no active listener; remote access in its sample came from the implant's outbound connection. Treat Radmin presence as staging, not proof of an active Radmin session.
- Attribution confidence. UNC7017 is an uncategorized cluster. Neither source names a nation or sponsor and this pack does not.
Hunt Brief & Attack Chain
Hunt hypotheses (highest fidelity first)
| # | Hypothesis | MITRE | Telemetry | Confidence | Query |
|---|---|---|---|---|---|
| H1 | An AOMEI updater child process runs from html or tmphtml. | T1195.002, T1036.005 | Process creation | High | Q1 |
| H2 | CROSSROAD deletes itself via a --nitrod child and the :wtfbbq stream rename. | T1070.004, T1564.004 | Process creation; file stream telemetry (native) | High (flag); gap (stream) | Q2 |
| H3 | The loader registers RadminComponentRuntime or NitroRuntime. | T1053.005 | Scheduled task registration | High | Q3 |
| H4 | A signed Radmin launcher runs with --runtimechk from a user profile and sideloads BlkScrDl.dll. | T1574.001 | Process creation; executable writes | High | Q4, Q5 |
| H5 | SHINYROAD registers a COM server and spawns a shell from the launcher or dllhost. | T1559.001, T1059.006 | Registry writes; process creation | Medium | Q6, Q7 |
| H6 | Campaign binaries look up their external IP, then reach campaign infrastructure. | T1016, T1614, T1571 | DNS lookup; outbound connection | High | Q8, Q9, Q10, Q14 |
| H7 | ADAPTAGENT is staged under a Nitro PDF masquerade with hidden PowerShell. | T1059.001, T1036.005 | Process creation | High | Q11 |
| H8 | A hijacked popup coerces outbound SMB or WebDAV authentication. | T1187 | Outbound connection; native logs | Medium | Q12 |
Attack chain
| Step | Behavior | Telemetry (plain terms, then platforms) | Hunt angle |
|---|---|---|---|
| 1 | Actor edits down_html.ini (version, MD5, URL) and Notifycfg.ini for 5 to 10 minutes | Server side; at the endpoint only a cached config copy and proxy logs (outside endpoint telemetry) | Native hunt N1, N2: cached ini in Tmphtml, proxy record at alert second |
| 2 | Backupper.exe downloads upgrade-now.zip and stages it to tmphtml, then html | Executable or file written with path (CrowdStrike: PeFileWritten, NewExecutableWritten. SentinelOne: File Creation. Defender: DeviceFileEvents) | Executable written under AOMEI tmphtml (Q1 covers the launch) |
| 3 | ABNotify.exe executes upgrade-now.exe; 35,000-line Notifycfg.ini forces the update path | Process creation with command line, path, parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents) | Q1; native N2 for the dummy-section bloat |
| 4 | CROSSROAD guardrails: AOMEI path check, external IP lookup, abort on CN, named mutex | DNS lookup with requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, confirm on tenant) | Q8; mutex has no endpoint telemetry (gap) |
| 5 | Config reversion: thrid reset to 5 and URL pointed back to AOMEI | File modification of the cached ini (outside default endpoint telemetry) | Native N1 |
| 6 | Download component.zip or en.zip to %TEMP%\svc*.tmp, unpack to local app data | Executable or file written; outbound connection (CrowdStrike: NetworkConnectIP4, DnsRequest) | Q5, Q9, Q10 |
| 7 | Task RadminComponentRuntime runs FamItrfc.exe --runtimechk every 3 hours | Scheduled task or service created (CrowdStrike: ScheduledTaskRegistered. SentinelOne: Task Register. Defender: DeviceEvents ScheduledTaskCreated) | Q3, Q4 |
| 8 | Self-deletion: copy to temp, --nitrod, rename :$DATA to :wtfbbq, delete on close | Process creation; NTFS stream telemetry (not in default endpoint telemetry) | Q2; native N3 (Sysmon stream hash event) |
| 9 | FamItrfc.exe sideloads BlkScrDl.dll; COM server registered; shared memory section mapped | Executable written; registry value set (CrowdStrike: RegGenericValueUpdate, AsepValueUpdate) | Q5, Q7; shared memory has no telemetry (gap) |
| 10 | RuntimeComponent.dll decrypts metadata.dat (ChaCha20) and runs PocketPy bytecode in memory; C2 on 8.218.65.184 | Outbound connection with process | Q9, Q14; in-memory stage is a gap |
| 11 | Interactive shell, chaffed C2 traffic, runtime extension | Process creation with parent; network (chaffing is a network-layer signal) | Q6; chaffing not detectable at the endpoint |
| 12 | Mid-July: ADAPTAGENT via NitroPromotion.exe, task NitroRuntime, Eztwain4.dll sideload, 2-minute beacons to 8.210.84.56:6800 | Process creation; scheduled task; outbound connection | Q3, Q5, Q11, Q14 |
| 13 | 20 July: 1x1 popup to a file:// UNC path forces WebDAV/NTLM to 8.210.84.56 | Outbound connection (SMB 445, WebDAV over HTTP); NTLM audit log (native) | Q12, N4, N5 |
Campaign timeline from the Mandiant report: tampering windows on 28 May 2026 (05:40 to 05:50 UTC), 16 July 2026 (08:33 to 08:39 UTC) and 20 July 2026 (10:35 to 10:40 UTC), plus related samples from late April. The vendor was notified on 17 July and responded on 29 July. Additional windows outside the vendors' visibility are probable.
Consolidated IOC Table
| Type | Value | Confidence | Action | Context | Snapshot |
|---|---|---|---|---|---|
| sha256 | 6670ab3f0434a42f33416f8354150b1701e7615f3cfa2f1635e54f8df0f6eec7 | medium | detect | upgrade-now.zip (May 20 CROSSROAD archive). CROSSROAD archive | 01 |
| sha256 | 11e576df44e9c9eef72b9f5b699291bcdb200a19130778ec231e5b0987b913ad | medium | detect | upgrade-now.exe (May 20 CROSSROAD loader). CROSSROAD loader | 01 |
| sha256 | 130c3970f1c900191615b99d8be522d6a488a3d3b73938896ed937092007d96a | medium | detect | upgrade-now.exe (28 May endpoint sample). CROSSROAD loader seen on endpoint | 02 |
| sha256 | 9a58e8f49edf5644e5728f74cf36f560d3d486fa51db4db69d927963c7aca450 | medium | detect | upgrade-now.exe (July 16 CROSSROAD loader). CROSSROAD loader | 01 |
| sha256 | b360b6db0c2b668317edc1504a7c39ef047f0d925c8f113b521f6724c157ae21 | medium | detect | upgrade-now.exe (July 20 CROSSROAD loader). CROSSROAD loader | 01 |
| sha256 | 9ed503c6a9a3a493fb859401e9ed469eed77ab9927341964d6dbfbfa6255aeba | medium | detect | upgrade-now.zip (July 20 CROSSROAD archive). CROSSROAD archive | 01 |
| sha256 | d167bc898e856da8a4003cb4dfc7de55fea109eee3cd64fc8999710314109464 | high | prevent | component.zip / en.zip (SHINYROAD package). SHINYROAD package; two sources | 01 + 02 |
| sha256 | 5837bc26a73cfb46ffd466cf889f03ce2f6a48f1f3fd42ec8be22189d51cab1e | high | prevent | BlkScrDl.dll (malicious sideloaded DLL). SHINYROAD loader DLL; two sources | 01 + 02 |
| sha256 | 8fbaa74b27dc1e89abfc694e834378746f0ad37a688098c5f8f511410157c8b8 | medium | detect | RuntimeComponent.dll (PocketPy engine). SHINYROAD implant DLL | 01 |
| sha256 | 4c0a75e0f62ddc0bb350c224fe996f3b2ab08dcb0d288231c61ffe9406c7045b | medium | detect | config.zip (Python bytecode archive). SHINYROAD payload archive | 01 |
| sha256 | 4b4d25b3501519476f9a62a88a781e4f446f669280eb5e98c6b96eb6d3743431 | medium | detect | metadata.dat (ChaCha20 payload). SHINYROAD encrypted payload | 01 |
| sha256 | 100657d3e205bfd0e96f1c088ae1739b50657592a696201467b4c65b7d36289a | medium | detect | WNTestdll.dll. Related DLL named in ManageEngine hash table | 02 |
| sha256 | 53b7d01163ccb679001f20e4a8a5e1f33d5d2837c375315f6514cc820f81bd12 | medium | detect | Sample listed as hunted VirusTotal sample. ManageEngine hunted sample; function not stated | 02 |
| sha256 | 14b31918075e6130b9123158743db0a81e2d7bfcf6d39731ee7d9200a98cd591 | medium | detect | NitroPromotion.exe (ADAPTAGENT launcher). ADAPTAGENT launcher | 01 |
| sha256 | 8c8090aca8ddb1cf968d2b810457e0cfce97c03cd721f70d2eca7f37f2681fc4 | medium | detect | Eztwain4.dll (ADAPTAGENT helper DLL). ADAPTAGENT sideloaded DLL | 01 |
| sha256 | 7b252be444dee2fb2a57043a823dc5b5e1fae970aea91ee2c8339aa70329969d | medium | detect | abe.exe (secondary payload). Secondary payload; role not described | 01 |
| sha256 | 924ad92d89dde577d5858b2ebc5703a485467842d78e98b75019b2e2e0ac3fe6 | medium | detect | down_html.ini (tampered July 16). Tampered AOMEI update config | 01 |
| sha256 | f2a7d0a4c8613a9cdd31ede6131b27040c0571ca97970b245a710191806d7ac4 | medium | detect | down_html.ini (tampered July 20). Tampered AOMEI update config | 01 |
| sha256 | a984fdb0830760446d2b599b263506e01fcf1e560a31a057c3bbe7b3f8ac155d | medium | detect | down_html.ini (ManageEngine volatile hash). Tampered AOMEI update config | 02 |
| sha256 | d2f9ef2429312667f0512504b6a19ba4a9cc5514f1b2933923dd42f75a1c5934 | medium | detect | Notifycfg.ini (tampered July 16). Tampered notification config with dummy sections | 01 |
| sha256 | 286e0d8c7bdfaa7c251a3e8f0f2fefbe25c67586c0149d39b1095c89dc6023c8 | medium | detect | Notifycfg.ini (tampered July 20 WebDAV). Tampered notification config with WebDAV popup | 01 |
| sha256 | 2e3b4bfb86224f079b8762ce78bb7ff10afdbf8421b08b23e96429f2e1ee7b50 | medium | detect | Notifycfg.ini (ManageEngine volatile hash). Tampered notification config | 02 |
| md5 | 31323e7e738230fa6a8deef2b7894666 | medium | detect | July 16 declared MD5 for the malicious upgrade-now.zip | 01 |
| md5 | bf73c4391b58ca95220ce3ae03f66dcf | medium | detect | July 20 declared MD5 for the malicious upgrade-now.zip | 01 |
| ipv4 | 8.210.84.56 | medium | detect | WebDAV NTLM target and Adaptix C2 | 01 |
| ipv4 | 8.218.65.184 | high | detect | SHINYROAD C2, ports 41397 and 443 | 01 + 02 |
| ipv4 | 47.239.60.117 | medium | detect | Secondary infrastructure | 01 |
| domain | storage-data.com | medium | detect | Threat actor domain | 01 |
| domain | promotion.storage-data.com | medium | detect | Stage-2 dropper and component staging | 01 |
| domain | aomei.storage-data.com | medium | detect | Stage-3 payload staging | 01 |
| domain | serv.oss-cn-hongkong.aliyuncs.com | medium | detect | Alibaba Cloud OSS bucket used for April and May staging | 01 |
| url | https://promotion.storage-data.com/download/abpopup/upgrade-now.zip | medium | detect | Staging or delivery URL | 01 |
| url | https://promotion.storage-data.com/config/component.zip | medium | detect | Staging or delivery URL | 01 |
| url | https://promotion.storage-data.com/config/en.zip | medium | detect | Staging or delivery URL | 01 |
| url | https://promotion.storage-data.com/config/whitelist.ini | medium | detect | Staging or delivery URL | 01 |
| url | https://storage.googleapis.com/aomeiupdate/upgrade-now.zip | medium | detect | Staging or delivery URL | 01 |
| url | https://storage.googleapis.com/aomeiupdate/component.zip | medium | detect | Staging or delivery URL | 01 |
| unc | \\8.210.84.56\DavWWWRoot\campaign\24hour-flash-sale.html | medium | detect | WebDAV NTLM coercion resource | 01 |
| ipv4 | 96.126.191.98 | low | hunt | IP in the actor's whitelist.ini allowlist (observed June 22). A target filter, not shown to be actor infrastructure. Pivot only. | 01 |
| sha256 | 2d99e9d732ff9304d97c7856b052ca520ae689fec1ce86df724de741913afa4b | high | enrich | WinLpcDl.dll (legitimate Radmin component). Legitimate. Do not block; listed so analysts do not alert on it alone. | 01 |
| sha256 | dad559ae9bc977783c7251591eeb7aa432b860ce821ccf10b1260fd0e650c951 | high | enrich | WinLpcDl2.dll (legitimate Radmin component). Legitimate. Do not block; listed so analysts do not alert on it alone. | 01 |
| sha256 | d71a119b4950ee88dd36205176d989a64df5248ac268591d4bbb78621a73acbd | high | enrich | Nitro_Scan2PDFApp.exe (legitimate signed binary abused for sideloading). Legitimate. Do not block; listed so analysts do not alert on it alone. | 01 |
Prevent is set only for the two hashes that appear in both snapshots (the BlkScrDl.dll loader and the component.zip package). The signed Famatech binaries FamItrfc.exe and FamItrf2.exe, the Radmin support DLLs and the Nitro Pro file names are legitimate product artifacts and ship detect-or-enrich only. Hashes are perishable for this campaign: the dropper is rebuilt per window and self-deletes.
Affected Surface & Telemetry Matrix
| Surface | Telemetry (plain terms, then platforms) | Priority | Gap |
|---|---|---|---|
| Endpoints running AOMEI Backupper or AnyViewer (Windows) | Process creation with parent and command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents) | Critical | None for lineage; hosts without the sensor during April to July are unobservable |
| User-profile Radmin and Nitro Pro folders | Executable or file written with path (CrowdStrike: PeFileWritten, NewExecutableWritten. SentinelOne: File Creation. Defender: DeviceFileEvents) | High | Non-PE files (config.zip, metadata.dat, ini) are outside endpoint telemetry |
| Scheduled tasks | Scheduled task or service created (CrowdStrike: ScheduledTaskRegistered. SentinelOne: Task Register. Defender: DeviceEvents ScheduledTaskCreated) | High | Confirm field names for the task action on your tenant |
| COM registration | Registry value set (CrowdStrike: RegGenericValueUpdate, AsepValueUpdate. SentinelOne: Registry Value Modified. Defender: DeviceRegistryEvents) | Medium | Per-user COM keys may not be collected; silence is not proof |
| Egress to actor infrastructure | Outbound connection and DNS lookup with process (CrowdStrike: NetworkConnectIP4, DnsRequest. SentinelOne: IP Connect, DNS Resolved) | High | Proxy and firewall logs outside endpoint telemetry carry the User-Agent strings |
| Vendor distribution servers | Origin web server and storage bucket integrity logs | Vendor only | Outside endpoint telemetry; the cached ini on the host is the only local evidence |
| NTLM credential exposure | NTLM operational audit log, domain controller logons | High | Outside endpoint telemetry |
ATT&CK Mapping
| Tactic | Technique | Name | Observed behavior | Query or control |
|---|---|---|---|---|
| Resource Development | T1583.001 | Acquire Infrastructure: Domains | storage-data.com and promotion. and aomei. subdomains | Q10 |
| Resource Development | T1583.006 | Acquire Infrastructure: Web Services | Alibaba Cloud OSS bucket and a public Google Cloud Storage bucket as staging | Q10, native N1 |
| Resource Development | T1608.001 | Stage Capabilities: Upload Malware | upgrade-now.zip, component.zip, en.zip staged before each window | IOC blocklist |
| Initial Access | T1195.002 | Compromise Software Supply Chain | Ephemeral edits to AOMEI .ini files | Q1, N1 |
| Execution | T1059.001 | PowerShell | Hidden Start-Process of NitroPromotion.exe | Q11 |
| Execution | T1059.006 | Python | PocketPy bytecode run in memory from RuntimeComponent.dll | Gap (in memory); Q6 |
| Execution | T1559.001 | Component Object Model | BlkScrDl.dll registers an in-process COM server | Q7 |
| Persistence | T1053.005 | Scheduled Task | RadminComponentRuntime every 3 hours; NitroRuntime | Q3, Q4 |
| Defense Evasion | T1574.001 | Hijack Execution Flow: DLL | FamItrfc.exe loads BlkScrDl.dll; Eztwain4.dll sideload | Q4, Q5 |
| Defense Evasion | T1480 | Execution Guardrails | AOMEI path check, external IP lookup, CN abort, mutex | Q8 |
| Defense Evasion | T1036.005 | Masquerading | AOMEI metadata on the dropper; Nitro PDF names; Radmin DLL names | Q1, Q5, Q11 |
| Defense Evasion | T1027.013, T1140 | Encrypted file; Deobfuscate/Decode | metadata.dat ChaCha20 decrypted in memory | Gap (in memory) |
| Defense Evasion | T1070.004 | File Deletion | Self-deletion via --nitrod | Q2 |
| Defense Evasion | T1564.004 | NTFS File Attributes | :wtfbbq alternate data stream rename | Gap; native N3 |
| Credential Access | T1187 | Forced Authentication | 1x1 popup to a file:// UNC WebDAV path | Q12, N4, N5 |
| Discovery | T1016, T1614 | Network Configuration; System Location Discovery | External IP reflection lookups, abort on CN | Q8 |
| Discovery | T1082, T1033 | System Information; Owner/User Discovery | Hostname and username fingerprint for the agent ID | Gap (in memory) |
| Command and Control | T1105 | Ingress Tool Transfer | component.zip and en.zip download to %TEMP% | Q5, Q9, Q10 |
| Command and Control | T1571, T1573.001 | Non-Standard Port; Symmetric Cryptography | TCP 41397 and 6800; handshake key then per-host key | Q9, Q14 |
| Command and Control | T1001.001 | Junk Data | Traffic chaffing | Gap (network layer) |
| Command and Control | T1219.002 | Remote Desktop Software | Genuine Radmin 3.x bundle | Q4, Q15 |
Native Audit-Log Hunts
| # | Check |
|---|---|
| N1 | Cached AOMEI configuration. Preserve and hash down_html.ini and Notifycfg.ini under %PROGRAMFILES(X86)%\AOMEI\*\Tmphtml\ before anything is cleaned. Look for a non-default URL host, a thrid above the installed build and the shipped hashes. Do not re-fetch the vendor URL as evidence: it reverted within minutes. |
| N2 | Config bloat. A Notifycfg.ini with more than 35,000 lines or many [dummy_ sections, or a [PopWindow1] section with a file:// address and Width and Height of 1. |
| N3 | NTFS stream telemetry. Sysmon stream-hash events (Event ID 15) or equivalent for a stream named :wtfbbq on upgrade-now.exe. |
| N4 | NTLM audit. Enable outgoing NTLM auditing and review the NTLM Operational log for authentication to external addresses from user workstations, especially 8.210.84.56. |
| N5 | WebClient service. Check for the WebClient (WebDAV) service starting on workstations and for outbound DAV traffic to public addresses in firewall or proxy logs. |
| N6 | Proxy and gateway. Search for User-Agent Anyviewer-loader/1.0 and Anyviewer-ipcheck/1.0, requests to serv.oss-cn-hongkong.aliyuncs.com, and to the Google Cloud Storage path storage.googleapis.com/aomeiupdate/. Correlate with the second an updater-child alert fired. |
| N7 | Windows Security and Task Scheduler logs. Event 4698 or Task Scheduler Operational 106 for tasks RadminComponentRuntime and NitroRuntime. |
| N8 | Registry. COM keys under Software\Classes\CLSID\ and AppID for the identifiers in section 10, and unusual binary values under HKLM\SYSTEM\Software\Microsoft\TIP\AggregateResults\data as the report advises. |
| N9 | Named objects. Handle or object-manager review for the mutex and shared-memory section names in section 10 on suspect hosts (live response only). |
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.
All queries are Falcon CQL. Set the time picker to the longest retention you have: the campaign ran April to July 2026 and the payload persists after the vendor reverts its files.
Looks for: ABNotify.exe or Backupper.exe as parent of any .exe whose image path sits under an html or tmphtml directory. This is the CROSSROAD execution step (upgrade-now.exe launched from the html directory by the notification agent).
Known limitations and deployment notes
- PowerQuery: An executable the sensor did not see being created is not captured.
- PowerQuery assumes: src.process.name holds the creating updater's file name on Process Creation events.
- PowerQuery assumes: tgt.process.image.path holds the full path of the created image.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A process the sensor did not observe starting is not captured.
- KQL – Defender XDR assumes: FolderPath holds the full image path of the created process.
- KQL – Defender XDR assumes: The updater's file name is reported without a path in InitiatingProcessFileName.
// HUNT: Updater process launching an executable from its own staging or rendering directory // MITRE: T1195.002, T1036.005 // CONF: high // FP: low // COST: low // REQUIRES: Process creation with parent and image path // FALSE POSITIVES: none expected; legitimate AOMEI bundles contain no executables in these directories (ManageEngine) // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | ParentBaseFileName=/^(ABNotify|Backupper)(\.exe)?$/i | ImageFileName=/\\(html|tmphtml)\\.*\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
3 required fields · View requirements
Required telemetry
- SentinelOne Process Creation events
Required fields
event.type: Event typesrc.process.name: Creator process nametgt.process.image.path: Created image path
Recommended fields
tgt.process.cmdline: Created command lineendpoint.name: Host
Optional fields
tgt.process.image.sha256: Created image hash
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' src.process.name = * tgt.process.image.path = * | limit 5 | columns event.time, src.process.name, tgt.process.image.path
// HUNT: Updater process launching an executable from its own staging or rendering directory // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1195.002, T1036.005 // CONF: high | FP: low // FALSE POSITIVES: none expected; legitimate AOMEI bundles contain no executables in these directories (ManageEngine) // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: an AOMEI updater (ABNotify or Backupper) as creator of an executable under an html or tmphtml directory. The creator is src.process.name and the created image is tgt.process.image.path, per the Process Creation lineage. contains and matches are case-insensitive, which reproduces the CQL i flag. // REQUIRES: SentinelOne process-creation telemetry with creator name and created image path // TUNING: None expected: legitimate AOMEI bundles contain no executables in these directories. Review every hit. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: An executable the sensor did not see being created is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' src.process.name matches '^(ABNotify|Backupper)(\\.exe)?$' tgt.process.image.path matches '\\.exe$' (tgt.process.image.path contains '\\html\\' or tgt.process.image.path contains '\\tmphtml\\') | columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.image.path, tgt.process.cmdline, tgt.process.image.sha256 | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeInitiatingProcessFileName: Creating process nameFolderPath: Created process path
Recommended fields
ProcessCommandLine: Created command line
Optional fields
SHA1: Hash. SHA256 is often unpopulated in DeviceProcessEvents; SHA1 is the safer pivot.
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, InitiatingProcessFileName, FolderPath | take 5
// HUNT: Updater process launching an executable from its own staging or rendering directory
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1195.002, T1036.005
// CONF: high | FP: low
// FALSE POSITIVES: none expected; legitimate AOMEI bundles contain no executables in these directories (ManageEngine)
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate on DeviceProcessEvents: the updater is InitiatingProcessFileName and the created image path sits under an html or tmphtml directory and ends in .exe. contains and in~ are case-insensitive, which reproduces the CQL i flag.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: None expected: legitimate AOMEI bundles contain no executables in these directories. Review every hit.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. 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(30d)
| where InitiatingProcessFileName in~ ("ABNotify.exe", "ABNotify", "Backupper.exe", "Backupper")
| where FolderPath endswith ".exe"
| where FolderPath contains "\\html\\" or FolderPath contains "\\tmphtml\\"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256
| sort by Timestamp desc
| take 200Looks for: Any process started with the --nitrod argument. CROSSROAD copies itself to the temp directory and relaunches with --nitrod, passing the parent PID and original path through environment values rather than the command line.
Known limitations and deployment notes
- PowerQuery: A process started by a sensor-invisible parent is not captured.
- PowerQuery assumes: tgt.process.cmdline holds the full command line of the created process.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A process the sensor did not observe starting is not captured.
- KQL – Defender XDR assumes: ProcessCommandLine holds the full command line of the created process.
// HUNT: CROSSROAD self-deletion child (--nitrod) // MITRE: T1070.004, T1564.004 // CONF: high // FP: low // COST: low // REQUIRES: Process creation with command line // FALSE POSITIVES: none expected; the flag is specific to this dropper // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | CommandLine=/(^|\s)--nitrod(\s|$)/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
2 required fields · View requirements
Required telemetry
- SentinelOne Process Creation events
Required fields
event.type: Event typetgt.process.cmdline: Created command line
Recommended fields
tgt.process.name: Created process nameendpoint.name: Host
Optional fields
tgt.process.image.sha256: Created image hash
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline
// HUNT: CROSSROAD self-deletion child (--nitrod) // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1070.004, T1564.004 // CONF: high | FP: low // FALSE POSITIVES: none expected; the flag is specific to this dropper // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: the --nitrod flag as a whole command-line token. The created process is tgt.process.*, so the command line is tgt.process.cmdline. matches is case-insensitive, which reproduces the CQL i flag. // REQUIRES: SentinelOne process-creation telemetry with the created process command line // TUNING: None expected: the flag is specific to this dropper. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A process started by a sensor-invisible parent is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' tgt.process.cmdline matches '(^|\\s)--nitrod(\\s|$)' | columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.image.path, tgt.process.cmdline, tgt.process.image.sha256 | limit 200
Automated validation: static-passed
2 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeProcessCommandLine: Created command line
Recommended fields
FolderPath: Created process path
Optional fields
SHA1: Hash. SHA256 is often unpopulated in DeviceProcessEvents; SHA1 is the safer pivot.
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, ProcessCommandLine | take 5
// HUNT: CROSSROAD self-deletion child (--nitrod) // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1070.004, T1564.004 // CONF: high | FP: low // FALSE POSITIVES: none expected; the flag is specific to this dropper // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: the --nitrod flag as a whole command-line token on DeviceProcessEvents. The (?i) prefix reproduces the CQL i flag. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: None expected: the flag is specific to this dropper. // LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. 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(30d) | where ProcessCommandLine matches regex @"(?i)(^|\s)--nitrod(\s|$)" | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256 | sort by Timestamp desc | take 200
Looks for: Scheduled task registration with either campaign task name. The task action (FamItrfc.exe --runtimechk) is caught when it runs by Q4.
Known limitations and deployment notes
- PowerQuery: A task registered through the Task Scheduler API with no such command line is not captured; the CQL tab sees the registration event itself.
- PowerQuery: The CQL anchors the match at the end of the task name; this pane matches the name anywhere in the command line.
- PowerQuery assumes: The task name appears in the command line of the process that registers the task.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A task registered without a ScheduledTaskCreated event is not captured.
- KQL – Defender XDR assumes: AdditionalFields is a string in Defender XDR and carries a TaskName key for ScheduledTaskCreated events.
- KQL – Defender XDR assumes: ScheduledTaskCreated is attested in Microsoft-owned hunting repositories rather than documented on a Learn table page.
// HUNT: Persistence tasks RadminComponentRuntime and NitroRuntime // MITRE: T1053.005 // CONF: high // FP: low // COST: low // REQUIRES: Scheduled task registration // FALSE POSITIVES: none expected; neither task name is a stock Radmin or Nitro artifact // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=ScheduledTaskRegistered | TaskName=/(RadminComponentRuntime|NitroRuntime)$/i | table([@timestamp, ComputerName, UserName, TaskName, TaskAuthor])
2 required fields · View requirements
Required telemetry
- SentinelOne Process Creation events
Required fields
event.type: Event typetgt.process.cmdline: Created command line
Recommended fields
tgt.process.name: Created process nameendpoint.name: Host
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline
// HUNT: Persistence tasks RadminComponentRuntime and NitroRuntime // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1053.005 // CONF: high | FP: low // FALSE POSITIVES: none expected; neither task name is a stock Radmin or Nitro artifact // EQUIVALENCE: approximate-analytic - SentinelOne telemetry in this field model has no scheduled-task registration event, so the hunt pivots on the command line of any created process that names RadminComponentRuntime or NitroRuntime. That catches schtasks.exe and PowerShell task registration by command line, but not a task registered through the Task Scheduler API with no such command line. // REQUIRES: SentinelOne process-creation telemetry with the created process command line // TUNING: None expected: neither task name is a stock Radmin or Nitro artifact. Confirm the registering process on any hit. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A task registered through the Task Scheduler API with no such command line is not captured; the CQL tab sees the registration event itself. The CQL anchors the match at the end of the task name; this pane matches the name anywhere in the command line. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' tgt.process.cmdline matches '(RadminComponentRuntime|NitroRuntime)' | columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.cmdline | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeAdditionalFields: Task name carrier
Recommended fields
InitiatingProcessFileName: Registering process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "ScheduledTaskCreated" | project Timestamp, ActionType, AdditionalFields | take 5
// HUNT: Persistence tasks RadminComponentRuntime and NitroRuntime // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1053.005 // CONF: high | FP: low // FALSE POSITIVES: none expected; neither task name is a stock Radmin or Nitro artifact // EQUIVALENCE: equivalent-with-schema-assumptions - Same scheduled-task registration predicate: DeviceEvents records ScheduledTaskCreated and the task name is read from AdditionalFields. The task name is matched at its end, as the CQL does. // REQUIRES: Defender for Endpoint scheduled-task telemetry (DeviceEvents, ActionType ScheduledTaskCreated) // TUNING: None expected: neither task name is a stock Radmin or Nitro artifact. Confirm the registering process on any hit. // LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. A task registered without a ScheduledTaskCreated event is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceEvents | where Timestamp >= ago(30d) | where ActionType == "ScheduledTaskCreated" | extend TaskName = tostring(parse_json(AdditionalFields).TaskName) | where TaskName matches regex @"(?i)(RadminComponentRuntime|NitroRuntime)$" | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, TaskName | sort by Timestamp desc | take 200
Looks for: FamItrfc.exe or FamItrf2.exe is authentic Famatech software abused for DLL search-order hijacking. The signal is the argument and the user-profile location, never the binary itself. Do not block the hash.
Known limitations and deployment notes
- PowerQuery: A launcher renamed to another file name is not captured.
- PowerQuery assumes: tgt.process.name holds the created file name with its extension.
- PowerQuery assumes: tgt.process.image.path holds the full path of the created image.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A launcher renamed to another file name is not captured.
- KQL – Defender XDR assumes: FolderPath holds the full image path of the created process.
// HUNT: Signed Radmin launcher run with --runtimechk or from a user profile // MITRE: T1574.001, T1053.005 // CONF: high // FP: low // COST: low // REQUIRES: Process creation with command line and image path // FALSE POSITIVES: Sanctioned Radmin deployments should not use --runtimechk or run from AppData; confirm with the Radmin owner // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | FileName=/^FamItrf[c2]\.exe$/i | CommandLine=/--runtimechk/i or ImageFileName=/\\AppData\\Local\\Radmin\\.*\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
4 required fields · View requirements
Required telemetry
- SentinelOne Process Creation events
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.cmdline: Created command linetgt.process.image.path: Created image path
Recommended fields
src.process.name: Creator processendpoint.name: Host
Optional fields
tgt.process.image.sha256: Created image hash
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.name = * tgt.process.cmdline = * tgt.process.image.path = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline, tgt.process.image.path
// HUNT: Signed Radmin launcher run with --runtimechk or from a user profile // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1574.001, T1053.005 // CONF: high | FP: low // FALSE POSITIVES: Sanctioned Radmin deployments should not use --runtimechk or run from AppData; confirm with the Radmin owner // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: the signed Radmin launcher FamItrfc.exe or FamItrf2.exe, run with --runtimechk or from the user-profile Radmin directory. The created process is tgt.process.*. The CQL requires the image to end in .exe, which the launcher name already guarantees. // REQUIRES: SentinelOne process-creation telemetry with created name, image path and command line // TUNING: Sanctioned Radmin deployments should not use --runtimechk or run from the user profile; confirm with the Radmin owner. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: A launcher renamed to another file name is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' tgt.process.name matches '^FamItrf[c2]\\.exe$' (tgt.process.cmdline contains '--runtimechk' or tgt.process.image.path contains '\\AppData\\Local\\Radmin\\') | columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.image.path, tgt.process.cmdline, tgt.process.image.sha256 | limit 200
Automated 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
Optional fields
SHA1: Hash. SHA256 is often unpopulated in DeviceProcessEvents; SHA1 is the safer pivot.
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, FolderPath, ProcessCommandLine | take 5
// HUNT: Signed Radmin launcher run with --runtimechk or from a user profile
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1574.001, T1053.005
// CONF: high | FP: low
// FALSE POSITIVES: Sanctioned Radmin deployments should not use --runtimechk or run from AppData; confirm with the Radmin owner
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate on DeviceProcessEvents: the launcher by name, and either the --runtimechk flag or the user-profile Radmin directory. contains and in~ are case-insensitive, which reproduces the CQL i flag.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: Sanctioned Radmin deployments should not use --runtimechk or run from the user profile; confirm with the Radmin owner.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. A launcher 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(30d)
| where FileName in~ ("FamItrfc.exe", "FamItrf2.exe")
| where ProcessCommandLine contains "--runtimechk" or FolderPath contains "\\AppData\\Local\\Radmin\\"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256
| sort by Timestamp desc
| take 200Looks for: PE files written to AppData\Local\Radmin (BlkScrDl.dll, csvc\RuntimeComponent.dll) or AppData\Local\Nitro Pro (Eztwain4.dll, NitroPromotion.exe). The legitimate Nitro Pro product installs under Program Files, not the user profile.
Known limitations and deployment notes
- PowerQuery: Whether the written file is a PE image is not tested; the exact paths carry that meaning.
- PowerQuery: The match is a substring match, not anchored at the end of the path.
- PowerQuery assumes: tgt.file.path holds the full path of the written file.
- PowerQuery assumes: File Modification events are included so a drop that lands as a modification is not missed.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: Whether the written file is a PE image is not tested; the exact paths carry that meaning.
- KQL – Defender XDR assumes: FolderPath holds the full path including the file name on DeviceFileEvents.
// HUNT: Sideload DLL and launcher drops in user-profile Radmin and Nitro Pro folders // MITRE: T1574.001, T1036.005 // CONF: high // FP: low // COST: low // REQUIRES: Executable or file written with path // FALSE POSITIVES: BlkScrDl.dll is described as masquerading as a Radmin component name; if a genuine file of that name exists in your Radmin build, confirm with the hash from section 4 // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(PeFileWritten|NewExecutableWritten)$/ | TargetFileName=/\\AppData\\Local\\(Radmin\\(BlkScrDl\.dll|csvc\\RuntimeComponent\.dll)|Nitro Pro\\(Eztwain4\.dll|NitroPromotion\.exe))$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, TargetFileName, SHA256HashData])
2 required fields · View requirements
Required telemetry
- SentinelOne File Creation and File Modification events
Required fields
event.type: Event typetgt.file.path: Written file path
Recommended fields
src.process.name: Writing processendpoint.name: Host
Optional fields
tgt.file.sha256: Written file hash
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'File Creation' tgt.file.path = * | limit 5 | columns event.time, src.process.name, tgt.file.path
// HUNT: Sideload DLL and launcher drops in user-profile Radmin and Nitro Pro folders
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1574.001, T1036.005
// CONF: high | FP: low
// FALSE POSITIVES: BlkScrDl.dll is described as masquerading as a Radmin component name; if a genuine file of that name exists in your Radmin build, confirm with the hash from section 4
// EQUIVALENCE: approximate-analytic - The CQL matches PE-file-written events at four exact paths. SentinelOne has no PE-specific write event here, so this pane matches File Creation and File Modification at the same paths. The paths are matched as substrings rather than anchored at the end, so a longer name that merely starts with a listed file name also matches.
// REQUIRES: SentinelOne file creation and modification telemetry with the target path
// TUNING: BlkScrDl.dll is described as masquerading as a Radmin component name; if a genuine file of that name exists in your Radmin build, confirm with the hash from section 4.
// LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Whether the written file is a PE image is not tested; the exact paths carry that meaning. The match is a substring match, not anchored at the end of the path.
// VALIDATION LEVEL: static-reviewed
event.type in ('File Creation', 'File Modification') (tgt.file.path contains '\\AppData\\Local\\Radmin\\BlkScrDl.dll' or tgt.file.path contains '\\AppData\\Local\\Radmin\\csvc\\RuntimeComponent.dll' or tgt.file.path contains '\\AppData\\Local\\Nitro Pro\\Eztwain4.dll' or tgt.file.path contains '\\AppData\\Local\\Nitro Pro\\NitroPromotion.exe')
| columns event.time, endpoint.name, src.process.user, src.process.name, tgt.file.path, tgt.file.sha256
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceFileEvents
Required fields
Timestamp: Event timeActionType: Action typeFolderPath: Written file path
Recommended fields
InitiatingProcessFileName: Writing process
Optional fields
SHA1: Hash
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceFileEvents | where Timestamp > ago(1d) | project Timestamp, ActionType, FolderPath | take 5
// HUNT: Sideload DLL and launcher drops in user-profile Radmin and Nitro Pro folders
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1574.001, T1036.005
// CONF: high | FP: low
// FALSE POSITIVES: BlkScrDl.dll is described as masquerading as a Radmin component name; if a genuine file of that name exists in your Radmin build, confirm with the hash from section 4
// EQUIVALENCE: approximate-analytic - The CQL matches PE-file-written events at four exact paths. DeviceFileEvents has no PE-specific write action, so this pane matches FileCreated and FileModified at the same paths. The paths are matched as substrings rather than anchored at the end.
// REQUIRES: Defender for Endpoint file telemetry (DeviceFileEvents)
// TUNING: BlkScrDl.dll is described as masquerading as a Radmin component name; if a genuine file of that name exists in your Radmin build, confirm with the hash from section 4.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. Whether the written file is a PE image is not tested; the exact paths carry that meaning.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceFileEvents
| where Timestamp >= ago(30d)
| where ActionType in ("FileCreated", "FileModified")
| where FolderPath contains "\\AppData\\Local\\Radmin\\BlkScrDl.dll" or FolderPath contains "\\AppData\\Local\\Radmin\\csvc\\RuntimeComponent.dll" or FolderPath contains "\\AppData\\Local\\Nitro Pro\\Eztwain4.dll" or FolderPath contains "\\AppData\\Local\\Nitro Pro\\NitroPromotion.exe"
| project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, FileName, FolderPath, SHA1, SHA256
| sort by Timestamp desc
| take 200Looks for: SHINYROAD exposes an interactive remote shell (os.system) from inside the Radmin launcher process or a dllhost COM surrogate. A command interpreter whose parent is one of those is worth triage. FP: dllhost.exe legitimately spawns children for some COM servers. Exclude known admin tooling and the Windows components you baseline in your estate.
Known limitations and deployment notes
- PowerQuery: Shells launched through an intermediate process are not captured.
- PowerQuery assumes: src.process.name holds the creator's file name on Process Creation events.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: Shells launched through an intermediate process are not captured.
- KQL – Defender XDR assumes: InitiatingProcessFileName carries the creator's file name without a path.
// HUNT: Radmin launcher or dllhost spawning a command shell // MITRE: T1059.006, T1559.001 // CONF: medium // FP: medium // COST: low // REQUIRES: Process creation with parent // FALSE POSITIVES: COM surrogate children from legitimate software // TUNING: exclude dllhost parents whose command line carries a known-good /Processid and that match your baselined software; review FamItrf parents without exclusion // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | ParentBaseFileName=/^(FamItrf[c2]|dllhost)(\.exe)?$/i | FileName=/^(cmd|powershell|pwsh)\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
3 required fields · View requirements
Required telemetry
- SentinelOne Process Creation events
Required fields
event.type: Event typesrc.process.name: Creator process nametgt.process.name: Created process name
Recommended fields
tgt.process.cmdline: Created command lineendpoint.name: Host
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' src.process.name = * tgt.process.name = * | limit 5 | columns event.time, src.process.name, tgt.process.name
// HUNT: Radmin launcher or dllhost spawning a command shell // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1059.006, T1559.001 // CONF: medium | FP: medium // FALSE POSITIVES: COM surrogate children from legitimate software // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: a Radmin launcher or dllhost as creator of cmd, powershell or pwsh. The creator is src.process.name and the created shell is tgt.process.name, per the Process Creation lineage. // REQUIRES: SentinelOne process-creation telemetry with creator and created process names // TUNING: Exclude dllhost creators whose command line carries a known-good /Processid and that match your baselined software; review FamItrf creators without exclusion. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Shells launched through an intermediate process are not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' src.process.name matches '^(FamItrf[c2]|dllhost)(\\.exe)?$' tgt.process.name matches '^(cmd|powershell|pwsh)\\.exe$' | columns event.time, endpoint.name, src.process.user, src.process.name, src.process.cmdline, tgt.process.name, tgt.process.cmdline | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameInitiatingProcessFileName: Initiating process
Recommended fields
ProcessCommandLine: Created command line
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, InitiatingProcessFileName | take 5
// HUNT: Radmin launcher or dllhost spawning a command shell
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1059.006, T1559.001
// CONF: medium | FP: medium
// FALSE POSITIVES: COM surrogate children from legitimate software
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate on DeviceProcessEvents: the Radmin launcher or dllhost is the initiating process and the created process is a command shell. The (?i) prefix and in~ reproduce the CQL i flag.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: Exclude dllhost parents whose command line carries a known-good /Processid and that match your baselined software; review FamItrf parents without exclusion.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. Shells launched through an intermediate process are not captured.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(30d)
| where InitiatingProcessFileName matches regex @"(?i)^(FamItrf[c2]|dllhost)(\.exe)?$"
| where FileName in~ ("cmd.exe", "powershell.exe", "pwsh.exe")
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine
| sort by Timestamp desc
| take 200Looks for: Registry writes that carry the CLSID or AppID BlkScrDl.dll registers, or a value pointing to csvc\RuntimeComponent.dll. Sensor registry coverage of per-user COM keys varies, so a silent query is not proof of absence.
Known limitations and deployment notes
- PowerQuery: Registry activity on keys the sensor does not monitor is not captured.
- PowerQuery assumes: registry.keyPath holds the full key path including the CLSID or AppID segment.
- PowerQuery assumes: registry.value holds the data written to the value.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: Registry activity on keys the sensor does not monitor is not captured.
- KQL – Defender XDR assumes: RegistryKey holds the full key path including the CLSID or AppID segment.
// HUNT: COM server registration for the SHINYROAD CLSID and AppID // MITRE: T1559.001 // CONF: medium // FP: low // COST: low // REQUIRES: Registry value set (CrowdStrike registry events) // FALSE POSITIVES: none expected; the identifiers are specific to this campaign // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(RegGenericValueUpdate|AsepValueUpdate)$/ | RegObjectName=/21191641-0D7F-4CD9-9572-01AAE6E7851B|9AD3E98A-4C7E-4F37-982D-D73D41E4F91B/i or RegStringValue=/\\Radmin\\csvc\\RuntimeComponent\.dll/i | table([@timestamp, ComputerName, UserName, RegObjectName, RegValueName, RegStringValue])
3 required fields · View requirements
Required telemetry
- SentinelOne Registry Value Modified events
Required fields
event.type: Event typeregistry.keyPath: Registry key pathregistry.value: Registry value data
Recommended fields
src.process.name: Writing processendpoint.name: Host
Optional 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 = * registry.value = * | limit 5 | columns event.time, registry.keyPath, registry.value
// HUNT: COM server registration for the SHINYROAD CLSID and AppID // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1559.001 // CONF: medium | FP: low // FALSE POSITIVES: none expected; the identifiers are specific to this campaign // EQUIVALENCE: equivalent-with-schema-assumptions - Same registry-value predicate: either campaign GUID in the key path, or the RuntimeComponent.dll path as the value data. SentinelOne reports one Registry Value Modified event type where the CQL lists two event names. // REQUIRES: SentinelOne registry value modification telemetry // TUNING: None expected: the identifiers are specific to this campaign. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Registry activity on keys the sensor does not monitor is not captured. // VALIDATION LEVEL: static-reviewed event.type = 'Registry Value Modified' (registry.keyPath matches '21191641-0D7F-4CD9-9572-01AAE6E7851B|9AD3E98A-4C7E-4F37-982D-D73D41E4F91B' or registry.value contains '\\Radmin\\csvc\\RuntimeComponent.dll') | columns event.time, endpoint.name, src.process.user, src.process.name, registry.keyPath, registry.valueName, registry.value | limit 200
Automated 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
InitiatingProcessFileName: Writing process
Optional fields
RegistryValueName: Registry value name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceRegistryEvents | where Timestamp > ago(1d) | project Timestamp, ActionType, RegistryKey, RegistryValueData | take 5
// HUNT: COM server registration for the SHINYROAD CLSID and AppID // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1559.001 // CONF: medium | FP: low // FALSE POSITIVES: none expected; the identifiers are specific to this campaign // EQUIVALENCE: equivalent-with-schema-assumptions - Same registry-value predicate on DeviceRegistryEvents: RegistryValueSet with either campaign GUID in the key or the RuntimeComponent.dll path as the value data. contains is case-insensitive, which reproduces the CQL i flag. // REQUIRES: Defender for Endpoint registry telemetry (DeviceRegistryEvents) // TUNING: None expected: the identifiers are specific to this campaign. // LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. Registry activity on keys the sensor does not monitor is not captured. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceRegistryEvents | where Timestamp >= ago(30d) | where ActionType == "RegistryValueSet" | where RegistryKey contains "21191641-0D7F-4CD9-9572-01AAE6E7851B" or RegistryKey contains "9AD3E98A-4C7E-4F37-982D-D73D41E4F91B" or RegistryValueData contains "\\Radmin\\csvc\\RuntimeComponent.dll" | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, RegistryKey, RegistryValueName, RegistryValueData | sort by Timestamp desc | take 200
Looks for: DNS lookups for the IP-reflection services CROSSROAD queries before deciding whether to continue (it aborts on country code CN), made by upgrade-now.exe or NitroPromotion.exe.
Known limitations and deployment notes
- PowerQuery: Tenants that populate dns.request instead of event.dns.request return nothing until the field is swapped; run the field check in the readiness test.
- PowerQuery assumes: event.dns.request is the populated DNS name field; some tenants populate dns.request instead.
- PowerQuery assumes: src.process.name holds the requesting process on DNS events.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A tenant whose DnsQueryResponse rows carry the name under another key returns nothing; check the readiness test output.
- KQL – Defender XDR assumes: AdditionalFields is a string in Defender XDR and carries the queried name under DnsQueryString or QueryName.
- KQL – Defender XDR assumes: DnsQueryResponse is attested in Microsoft-owned hunting repositories rather than documented on a Learn table page.
// HUNT: External IP lookup by campaign binaries (CROSSROAD geolocation guardrail) // MITRE: T1016, T1614, T1480 // CONF: medium // FP: low // COST: low // REQUIRES: DNS lookup with requesting process // FALSE POSITIVES: none expected for these two process names // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=DnsRequest | DomainName=/^(myip\.opendns\.com|o-o\.myaddr\.l\.google\.com|checkip\.amazonaws\.com|api\.ipify\.org|ifconfig\.me|icanhazip\.com)$/i | ContextBaseFileName=/^(upgrade-now|NitroPromotion)(\.exe)?$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, DomainName])
3 required fields · View requirements
Required telemetry
- SentinelOne DNS Resolved events
Required fields
event.type: Event typeevent.dns.request: Requested namesrc.process.name: Requesting process
Recommended fields
endpoint.name: Host
Optional fields
dns.request: Requested name, alternate spelling. Documented by the vendor; check which spelling your tenant populates.
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'DNS Resolved' event.dns.request = * src.process.name = * | limit 5 | columns event.time, src.process.name, event.dns.request, dns.request
// HUNT: External IP lookup by campaign binaries (CROSSROAD geolocation guardrail)
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1016, T1614, T1480
// CONF: medium | FP: low
// FALSE POSITIVES: none expected for these two process names
// EQUIVALENCE: equivalent-with-schema-assumptions - Same DNS predicate: one of six public IP-lookup hostnames requested by upgrade-now or NitroPromotion. On a DNS Resolved event the requesting process is src.process.*. SentinelOne documents two DNS field spellings; this pane uses the one the independent Sigma backend maps.
// REQUIRES: SentinelOne DNS telemetry with the requesting process
// TUNING: None expected for these two process names.
// LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Tenants that populate dns.request instead of event.dns.request return nothing until the field is swapped; run the field check in the readiness test.
// VALIDATION LEVEL: static-reviewed
event.type = 'DNS Resolved' event.dns.request in:anycase ('myip.opendns.com', 'o-o.myaddr.l.google.com', 'checkip.amazonaws.com', 'api.ipify.org', 'ifconfig.me', 'icanhazip.com') src.process.name matches '^(upgrade-now|NitroPromotion)(\\.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: Requesting processAdditionalFields: Queried name carrier
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: External IP lookup by campaign binaries (CROSSROAD geolocation guardrail)
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1016, T1614, T1480
// CONF: medium | FP: low
// FALSE POSITIVES: none expected for these two process names
// EQUIVALENCE: equivalent-with-schema-assumptions - Same DNS predicate: one of six public IP-lookup hostnames requested by upgrade-now or NitroPromotion. Defender has no endpoint DNS table, so the name is read from DeviceEvents DnsQueryResponse AdditionalFields.
// REQUIRES: Defender for Endpoint DNS telemetry (DeviceEvents, ActionType DnsQueryResponse)
// TUNING: None expected for these two process names.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. A tenant whose DnsQueryResponse rows carry the name under another key returns nothing; check the readiness test output.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceEvents
| where Timestamp >= ago(30d)
| where ActionType == "DnsQueryResponse"
| where InitiatingProcessFileName matches regex @"(?i)^(upgrade-now|NitroPromotion)(\.exe)?$"
| extend DnsName = tostring(coalesce(parse_json(AdditionalFields)["DnsQueryString"], parse_json(AdditionalFields)["QueryName"]))
| where DnsName in~ ("myip.opendns.com", "o-o.myaddr.l.google.com", "checkip.amazonaws.com", "api.ipify.org", "ifconfig.me", "icanhazip.com")
| project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, DnsName
| sort by Timestamp desc
| take 200Looks for: Any IPv4 connection to the three campaign IP addresses (WebDAV and Adaptix target, SHINYROAD C2, secondary infrastructure). Hosting IPs are perishable, so absence now does not clear a host that connected in the April to July window; run over the longest retention you have.
Known limitations and deployment notes
- PowerQuery: Connections through a proxy show the proxy address, not the campaign address.
- PowerQuery assumes: dst.ip.address holds the remote IPv4 address on IP Connect events.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: Connections through a proxy show the proxy address, not the campaign address.
- KQL – Defender XDR assumes: RemoteIP holds the remote IPv4 address as a string.
// HUNT: Outbound connections to campaign IP addresses // MITRE: T1571, T1573.001, T1187 // CONF: high // FP: low // COST: low // REQUIRES: Outbound network connection with process // FALSE POSITIVES: none expected // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=NetworkConnectIP4 | in(RemoteAddressIP4, values=["8.210.84.56","8.218.65.184","47.239.60.117"]) | table([@timestamp, ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort])
2 required fields · View requirements
Required telemetry
- SentinelOne IP Connect events
Required fields
event.type: Event typedst.ip.address: Remote address
Recommended fields
src.process.name: Connecting processdst.port.number: Remote portendpoint.name: Host
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'IP Connect' dst.ip.address = * | limit 5 | columns event.time, src.process.name, dst.ip.address
// HUNT: Outbound connections to campaign IP addresses
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1571, T1573.001, T1187
// CONF: high | FP: low
// FALSE POSITIVES: none expected
// EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate: an outbound IPv4 connection whose destination is one of the three campaign addresses. The destination is dst.ip.address on an IP Connect event.
// REQUIRES: SentinelOne IP Connect network telemetry
// TUNING: None expected: the addresses are campaign infrastructure. Treat any hit as a lead and check the address is still attributed to the campaign.
// LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Connections through a proxy show the proxy address, not the campaign address.
// VALIDATION LEVEL: static-reviewed
event.type = 'IP Connect' dst.ip.address in ('8.210.84.56', '8.218.65.184', '47.239.60.117')
| columns event.time, endpoint.name, src.process.user, src.process.name, dst.ip.address, dst.port.number
| limit 200Automated validation: static-passed
2 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeRemoteIP: Remote address
Recommended fields
InitiatingProcessFileName: Connecting processRemotePort: Remote port
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, RemoteIP, RemotePort | take 5
// HUNT: Outbound connections to campaign IP addresses
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1571, T1573.001, T1187
// CONF: high | FP: low
// FALSE POSITIVES: none expected
// EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate on DeviceNetworkEvents: an outbound connection whose RemoteIP is one of the three campaign addresses.
// REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents)
// TUNING: None expected: the addresses are campaign infrastructure. Treat any hit as a lead and check the address is still attributed to the campaign.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. Connections through a proxy show the proxy address, not the campaign address.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceNetworkEvents
| where Timestamp >= ago(30d)
| where RemoteIP in ("8.210.84.56", "8.218.65.184", "47.239.60.117")
| project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, RemoteIP, RemotePort
| sort by Timestamp desc
| take 200Looks for: Resolution of storage-data.com and its subdomains, or the specific Alibaba Cloud OSS bucket host used for April and May staging.
Known limitations and deployment notes
- PowerQuery: Tenants that populate dns.request instead of event.dns.request return nothing until the field is swapped; run the field check in the readiness test.
- PowerQuery assumes: event.dns.request is the populated DNS name field; some tenants populate dns.request instead.
- PowerQuery assumes: src.process.name holds the requesting process on DNS events.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A tenant whose DnsQueryResponse rows carry the name under another key returns nothing; check the readiness test output.
- KQL – Defender XDR assumes: AdditionalFields is a string in Defender XDR and carries the queried name under DnsQueryString or QueryName.
- KQL – Defender XDR assumes: DnsQueryResponse is attested in Microsoft-owned hunting repositories rather than documented on a Learn table page.
// HUNT: DNS lookups for campaign domains and the Alibaba OSS staging bucket // MITRE: T1583.001, T1583.006, T1105 // CONF: high // FP: low // COST: low // REQUIRES: DNS lookup with requesting process // FALSE POSITIVES: none expected; the OSS match is on the single bucket host, not on aliyuncs.com as a whole // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=DnsRequest | DomainName=/(^|\.)storage-data\.com$/i or DomainName=/^serv\.oss-cn-hongkong\.aliyuncs\.com$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, DomainName])
2 required fields · View requirements
Required telemetry
- SentinelOne DNS Resolved events
Required fields
event.type: Event typeevent.dns.request: Requested name
Recommended fields
src.process.name: Requesting processendpoint.name: Host
Optional fields
dns.request: Requested name, alternate spelling. Documented by the vendor; check which spelling your tenant populates.
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'DNS Resolved' event.dns.request = * | limit 5 | columns event.time, src.process.name, event.dns.request, dns.request
// HUNT: DNS lookups for campaign domains and the Alibaba OSS staging bucket
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1583.001, T1583.006, T1105
// CONF: high | FP: low
// FALSE POSITIVES: none expected; the OSS match is on the single bucket host, not on aliyuncs.com as a whole
// EQUIVALENCE: equivalent-with-schema-assumptions - Same DNS predicate: the storage-data.com domain or any subdomain of it, or the single Alibaba OSS bucket host. The bucket is matched by exact host, not by aliyuncs.com as a whole. SentinelOne documents two DNS field spellings; this pane uses the one the independent Sigma backend maps.
// REQUIRES: SentinelOne DNS telemetry with the requesting process
// TUNING: None expected: the OSS match is on the single bucket host, not on aliyuncs.com as a whole.
// LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Tenants that populate dns.request instead of event.dns.request return nothing until the field is swapped; run the field check in the readiness test.
// VALIDATION LEVEL: static-reviewed
event.type = 'DNS Resolved' (event.dns.request in:anycase ('storage-data.com', 'serv.oss-cn-hongkong.aliyuncs.com') or event.dns.request matches '\\.storage-data\\.com$')
| columns event.time, endpoint.name, src.process.user, src.process.name, event.dns.request
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeAdditionalFields: Queried name carrier
Recommended fields
InitiatingProcessFileName: Requesting 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 lookups for campaign domains and the Alibaba OSS staging bucket
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1583.001, T1583.006, T1105
// CONF: high | FP: low
// FALSE POSITIVES: none expected; the OSS match is on the single bucket host, not on aliyuncs.com as a whole
// EQUIVALENCE: equivalent-with-schema-assumptions - Same DNS predicate: the storage-data.com domain or any subdomain of it, or the single Alibaba OSS bucket host. Defender has no endpoint DNS table, so the name is read from DeviceEvents DnsQueryResponse AdditionalFields.
// REQUIRES: Defender for Endpoint DNS telemetry (DeviceEvents, ActionType DnsQueryResponse)
// TUNING: None expected: the OSS match is on the single bucket host, not on aliyuncs.com as a whole.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. A tenant whose DnsQueryResponse rows carry the name under another key returns nothing; check the readiness test output.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceEvents
| where Timestamp >= ago(30d)
| where ActionType == "DnsQueryResponse"
| extend DnsName = tostring(coalesce(parse_json(AdditionalFields)["DnsQueryString"], parse_json(AdditionalFields)["QueryName"]))
| where DnsName in~ ("storage-data.com", "serv.oss-cn-hongkong.aliyuncs.com") or DnsName endswith ".storage-data.com"
| project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, DnsName
| sort by Timestamp desc
| take 200Looks for: NitroPromotion.exe execution, or a Start-Process command line naming it. The report documents a hidden PowerShell Start-Process against NitroPromotion.exe in the user temp directory.
Known limitations and deployment notes
- PowerQuery: The hidden-window flag in the card title is not tested by the CQL or by this pane.
- PowerQuery assumes: tgt.process.name holds the created file name with its extension.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: The hidden-window flag in the card title is not tested by the CQL or by this pane.
- KQL – Defender XDR assumes: FileName holds the created file name with its extension.
// HUNT: Hidden PowerShell launching NitroPromotion.exe (ADAPTAGENT staging) // MITRE: T1059.001, T1036.005 // CONF: high // FP: low // COST: low // REQUIRES: Process creation with command line // FALSE POSITIVES: none expected; confirm with the Nitro Software owner that no sanctioned tool uses this name // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | FileName=/^NitroPromotion\.exe$/i or CommandLine=/Start-Process.*NitroPromotion\.exe/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
3 required fields · View requirements
Required telemetry
- SentinelOne Process Creation events
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creator processendpoint.name: Host
Optional fields
tgt.process.image.sha256: Created image hash
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.name = * tgt.process.cmdline = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline
// HUNT: Hidden PowerShell launching NitroPromotion.exe (ADAPTAGENT staging)
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1059.001, T1036.005
// CONF: high | FP: low
// FALSE POSITIVES: none expected; confirm with the Nitro Software owner that no sanctioned tool uses this name
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: a process named NitroPromotion.exe, or a command line that runs Start-Process against NitroPromotion.exe. The created process is tgt.process.*. The CQL does not test for a hidden window, so neither does this pane.
// REQUIRES: SentinelOne process-creation telemetry with created name and command line
// TUNING: Confirm with the Nitro Software owner that no sanctioned tool uses this name.
// LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: The hidden-window flag in the card title is not tested by the CQL or by this pane.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' (tgt.process.name in:anycase ('NitroPromotion.exe') or tgt.process.cmdline matches 'Start-Process.*NitroPromotion\\.exe')
| columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.image.path, tgt.process.cmdline, tgt.process.image.sha256
| 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
Optional fields
SHA1: Hash. SHA256 is often unpopulated in DeviceProcessEvents; SHA1 is the safer pivot.
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: Hidden PowerShell launching NitroPromotion.exe (ADAPTAGENT staging) // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1059.001, T1036.005 // CONF: high | FP: low // FALSE POSITIVES: none expected; confirm with the Nitro Software owner that no sanctioned tool uses this name // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate on DeviceProcessEvents: a process named NitroPromotion.exe, or a command line that runs Start-Process against NitroPromotion.exe. The (?i) prefix and =~ reproduce the CQL i flag. The CQL does not test for a hidden window, so neither does this pane. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Confirm with the Nitro Software owner that no sanctioned tool uses this name. // LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. The hidden-window flag in the card title is not tested by the CQL or by this pane. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(30d) | where FileName =~ "NitroPromotion.exe" or ProcessCommandLine matches regex @"(?i)Start-Process.*NitroPromotion\.exe" | project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256 | sort by Timestamp desc | take 200
Looks for: TCP 445 connections to public addresses, grouped by host and process. The 1x1 popup coerces an outbound SMB or WebDAV session that leaks NetNTLMv2 material. WebDAV normally rides HTTP, so pair this with the native hunts for the WebClient service. FP: Cloud file shares and some VPN-less remote file access use 445 to public addresses. Exclude your documented storage endpoints.
Known limitations and deployment notes
- PowerQuery: IPv6 destinations are not covered, as in the CQL.
- PowerQuery assumes: dst.port.number is numeric on IP Connect events.
- PowerQuery assumes: src.process.name holds the connecting process.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: IPv6 destinations are not covered, as in the CQL.
- KQL – Defender XDR assumes: RemoteIP holds an IPv4 address string for the rows of interest; ipv4_is_private returns null for IPv6, which drops those rows.
// HUNT: Outbound SMB to non-private addresses (WebDAV and NTLM coercion fallout) // MITRE: T1187 // CONF: medium // FP: medium // COST: medium // REQUIRES: Outbound network connection with process // FALSE POSITIVES: legitimate SMB to cloud or partner storage // TUNING: exclude sanctioned remote storage IP ranges with !cidr() and known backup appliance hosts; the hosts left are the review set // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=NetworkConnectIP4 | RemotePort=445 | !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8","169.254.0.0/16","100.64.0.0/10"]) | groupBy([ComputerName, ContextBaseFileName, RemoteAddressIP4], function=count(as=conns)) | sort(conns, order=desc)
5 required fields · View requirements
Required telemetry
- SentinelOne IP Connect events
Required fields
event.type: Event typedst.port.number: Remote portdst.ip.address: Remote addresssrc.process.name: Connecting processendpoint.name: Host
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'IP Connect' dst.port.number = 445 dst.ip.address = * | limit 5 | columns event.time, endpoint.name, src.process.name, dst.ip.address, dst.port.number
// HUNT: Outbound SMB to non-private addresses (WebDAV and NTLM coercion fallout) // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1187 // CONF: medium | FP: medium // FALSE POSITIVES: legitimate SMB to cloud or partner storage // EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate: outbound connections to port 445 whose destination is outside RFC1918, loopback, link-local and carrier-grade NAT space, counted per host, process and destination. SentinelOne keys the destination as dst.ip.address and the process as src.process.name. // REQUIRES: SentinelOne IP Connect network telemetry // TUNING: Exclude sanctioned remote storage IP ranges with net_ipsubnet and known backup appliance hosts; the hosts left are the review set. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: IPv6 destinations are not covered, as in the CQL. // VALIDATION LEVEL: static-reviewed event.type = 'IP Connect' dst.port.number = 445 !(net_rfc1918(dst.ip.address) or net_ipsubnet(dst.ip.address, '127.0.0.0/8') or net_ipsubnet(dst.ip.address, '169.254.0.0/16') or net_ipsubnet(dst.ip.address, '100.64.0.0/10')) | group conns = count() by endpoint.name, src.process.name, dst.ip.address | sort -conns | limit 200
Automated validation: static-passed
5 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeRemotePort: Remote portRemoteIP: Remote addressInitiatingProcessFileName: Connecting processDeviceName: Host
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | where RemotePort == 445 | project Timestamp, DeviceName, InitiatingProcessFileName, RemoteIP, RemotePort | take 5
// HUNT: Outbound SMB to non-private addresses (WebDAV and NTLM coercion fallout) // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1187 // CONF: medium | FP: medium // FALSE POSITIVES: legitimate SMB to cloud or partner storage // EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate on DeviceNetworkEvents: connections to port 445 whose RemoteIP is outside RFC1918, loopback, link-local and carrier-grade NAT space, counted per host, process and destination. // REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents) // TUNING: Exclude sanctioned remote storage IP ranges with ipv4_is_in_range and known backup appliance hosts; the hosts left are the review set. // LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. IPv6 destinations are not covered, as in the CQL. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed-with-warnings; fixtures=0/0 DeviceNetworkEvents | where Timestamp >= ago(30d) | where RemotePort == 445 | where not(ipv4_is_private(RemoteIP)) and not(ipv4_is_in_range(RemoteIP, "127.0.0.0/8")) and not(ipv4_is_in_range(RemoteIP, "169.254.0.0/16")) and not(ipv4_is_in_range(RemoteIP, "100.64.0.0/10")) | summarize conns = count(), LastSeen = max(Timestamp) by DeviceName, InitiatingProcessFileName, RemoteIP | sort by conns desc | take 200
Looks for: SHA-256 match against the executable and DLL hashes in section 10. Config files, archives and metadata.dat are left out because the sensor hashes PE and process images. Hash coverage is the weakest signal in this campaign: the dropper self-deletes and is rebuilt per window.
Known limitations and deployment notes
- PowerQuery: A file whose hash the sensor did not compute is not matched.
- PowerQuery assumes: tgt.process.image.sha256 and tgt.file.sha256 are populated on the tenant; an unpopulated hash field returns nothing.
- PowerQuery assumes: File Modification events are included so a drop that lands as a modification is not missed.
- KQL – Defender XDR: Process executions of these hashes are not covered; the CQL tab covers them.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A file whose hash the sensor did not compute is not matched.
- KQL – Defender XDR assumes: SHA256 is populated on DeviceFileEvents rows for the tenant.
// HUNT: Known campaign file hashes executed or written // MITRE: T1195.002, T1574.001 // CONF: high // FP: low // COST: medium // REQUIRES: Process creation and executable written with file hash // FALSE POSITIVES: none expected // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|PeFileWritten|NewExecutableWritten)$/ | in(SHA256HashData, values=[ "11e576df44e9c9eef72b9f5b699291bcdb200a19130778ec231e5b0987b913ad", "130c3970f1c900191615b99d8be522d6a488a3d3b73938896ed937092007d96a", "9a58e8f49edf5644e5728f74cf36f560d3d486fa51db4db69d927963c7aca450", "b360b6db0c2b668317edc1504a7c39ef047f0d925c8f113b521f6724c157ae21", "5837bc26a73cfb46ffd466cf889f03ce2f6a48f1f3fd42ec8be22189d51cab1e", "8fbaa74b27dc1e89abfc694e834378746f0ad37a688098c5f8f511410157c8b8", "100657d3e205bfd0e96f1c088ae1739b50657592a696201467b4c65b7d36289a", "53b7d01163ccb679001f20e4a8a5e1f33d5d2837c375315f6514cc820f81bd12", "14b31918075e6130b9123158743db0a81e2d7bfcf6d39731ee7d9200a98cd591", "8c8090aca8ddb1cf968d2b810457e0cfce97c03cd721f70d2eca7f37f2681fc4", "7b252be444dee2fb2a57043a823dc5b5e1fae970aea91ee2c8339aa70329969d" ]) | table([@timestamp, ComputerName, UserName, FileName, ImageFileName, TargetFileName, SHA256HashData])
3 required fields · View requirements
Required telemetry
- SentinelOne Process Creation, File Creation and File Modification events
Required fields
event.type: Event typetgt.process.image.sha256: Created image hashtgt.file.sha256: Written file hash
Recommended fields
endpoint.name: Hosttgt.file.path: Written file path
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.image.sha256 = * | limit 5 | columns event.type, tgt.process.image.sha256, tgt.file.sha256
// HUNT: Known campaign file hashes executed or written
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1195.002, T1574.001
// CONF: high | FP: low
// FALSE POSITIVES: none expected
// EQUIVALENCE: equivalent-with-schema-assumptions - Same hash predicate: the eleven campaign SHA-256 values against the created process image on Process Creation events and against the written file on File Creation and File Modification events. in:anycase keeps the match case-insensitive.
// REQUIRES: SentinelOne process-creation and file telemetry with SHA-256 populated
// TUNING: None expected. Hash hits are exact; confirm the sample against section 4 before acting.
// LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A file whose hash the sensor did not compute is not matched.
// VALIDATION LEVEL: static-reviewed
event.type in ('Process Creation', 'File Creation', 'File Modification') (tgt.process.image.sha256 in:anycase ('11e576df44e9c9eef72b9f5b699291bcdb200a19130778ec231e5b0987b913ad', '130c3970f1c900191615b99d8be522d6a488a3d3b73938896ed937092007d96a', '9a58e8f49edf5644e5728f74cf36f560d3d486fa51db4db69d927963c7aca450', 'b360b6db0c2b668317edc1504a7c39ef047f0d925c8f113b521f6724c157ae21', '5837bc26a73cfb46ffd466cf889f03ce2f6a48f1f3fd42ec8be22189d51cab1e', '8fbaa74b27dc1e89abfc694e834378746f0ad37a688098c5f8f511410157c8b8', '100657d3e205bfd0e96f1c088ae1739b50657592a696201467b4c65b7d36289a', '53b7d01163ccb679001f20e4a8a5e1f33d5d2837c375315f6514cc820f81bd12', '14b31918075e6130b9123158743db0a81e2d7bfcf6d39731ee7d9200a98cd591', '8c8090aca8ddb1cf968d2b810457e0cfce97c03cd721f70d2eca7f37f2681fc4', '7b252be444dee2fb2a57043a823dc5b5e1fae970aea91ee2c8339aa70329969d') or tgt.file.sha256 in:anycase ('11e576df44e9c9eef72b9f5b699291bcdb200a19130778ec231e5b0987b913ad', '130c3970f1c900191615b99d8be522d6a488a3d3b73938896ed937092007d96a', '9a58e8f49edf5644e5728f74cf36f560d3d486fa51db4db69d927963c7aca450', 'b360b6db0c2b668317edc1504a7c39ef047f0d925c8f113b521f6724c157ae21', '5837bc26a73cfb46ffd466cf889f03ce2f6a48f1f3fd42ec8be22189d51cab1e', '8fbaa74b27dc1e89abfc694e834378746f0ad37a688098c5f8f511410157c8b8', '100657d3e205bfd0e96f1c088ae1739b50657592a696201467b4c65b7d36289a', '53b7d01163ccb679001f20e4a8a5e1f33d5d2837c375315f6514cc820f81bd12', '14b31918075e6130b9123158743db0a81e2d7bfcf6d39731ee7d9200a98cd591', '8c8090aca8ddb1cf968d2b810457e0cfce97c03cd721f70d2eca7f37f2681fc4', '7b252be444dee2fb2a57043a823dc5b5e1fae970aea91ee2c8339aa70329969d'))
| columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.image.path, tgt.file.path, tgt.process.image.sha256, tgt.file.sha256
| limit 200Automated validation: static-passed
2 required fields · View requirements
Required telemetry
- DeviceFileEvents
Required fields
Timestamp: Event timeSHA256: File hash
Recommended fields
FolderPath: Written file pathInitiatingProcessFileName: Writing process
Optional fields
SHA1: Hash. SHA1 is shown for pivoting; the listed values are SHA-256 only.
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceFileEvents | where Timestamp > ago(1d) | where isnotempty(SHA256) | project Timestamp, FolderPath, SHA256 | take 5
// HUNT: Known campaign file hashes executed or written
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1195.002, T1574.001
// CONF: high | FP: low
// FALSE POSITIVES: none expected
// EQUIVALENCE: telemetry-gap - A single Advanced Hunting query reads one table. This pane pivots the eleven SHA-256 values on DeviceFileEvents, which sees the written half of the CQL. The executed half would need DeviceProcessEvents, where SHA256 is often unpopulated, so it is left to the CQL tab.
// REQUIRES: Defender for Endpoint file telemetry (DeviceFileEvents) with SHA256 populated
// TUNING: None expected. Hash hits are exact; confirm the sample against section 4 before acting.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Process executions of these hashes are not covered; the CQL tab covers them. Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. A file whose hash the sensor did not compute is not matched.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceFileEvents
| where Timestamp >= ago(30d)
| where SHA256 in~ ("11e576df44e9c9eef72b9f5b699291bcdb200a19130778ec231e5b0987b913ad", "130c3970f1c900191615b99d8be522d6a488a3d3b73938896ed937092007d96a", "9a58e8f49edf5644e5728f74cf36f560d3d486fa51db4db69d927963c7aca450", "b360b6db0c2b668317edc1504a7c39ef047f0d925c8f113b521f6724c157ae21", "5837bc26a73cfb46ffd466cf889f03ce2f6a48f1f3fd42ec8be22189d51cab1e", "8fbaa74b27dc1e89abfc694e834378746f0ad37a688098c5f8f511410157c8b8", "100657d3e205bfd0e96f1c088ae1739b50657592a696201467b4c65b7d36289a", "53b7d01163ccb679001f20e4a8a5e1f33d5d2837c375315f6514cc820f81bd12", "14b31918075e6130b9123158743db0a81e2d7bfcf6d39731ee7d9200a98cd591", "8c8090aca8ddb1cf968d2b810457e0cfce97c03cd721f70d2eca7f37f2681fc4", "7b252be444dee2fb2a57043a823dc5b5e1fae970aea91ee2c8339aa70329969d")
| project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, ActionType, FileName, FolderPath, SHA1, SHA256
| sort by Timestamp desc
| take 200Looks for: Connections to TCP 41397 (SHINYROAD raw TCP C2) or 6800 (Adaptix beacon) from the Radmin launcher, dllhost, the Nitro masquerade binaries or rundll32. FP: Port 6800 is used by unrelated software, hence the process restriction. A hit from dllhost needs the destination reviewed, not auto-escalation.
Known limitations and deployment notes
- PowerQuery: Other software using port 6800 can match when it runs under one of the listed process names.
- PowerQuery assumes: dst.port.number is numeric on IP Connect events.
- PowerQuery assumes: src.process.name holds the connecting process on IP Connect events.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: Other software using port 6800 can match when it runs under one of the listed process names.
- KQL – Defender XDR assumes: InitiatingProcessFileName carries the connecting process name without a path.
// HUNT: SHINYROAD and ADAPTAGENT C2 ports from sideload hosts // MITRE: T1571, T1219.002 // CONF: medium // FP: medium // COST: low // REQUIRES: Outbound network connection with process // FALSE POSITIVES: other software using port 6800 // TUNING: restrict to non-RFC1918 destinations with !cidr() if internal services use 6800, and exclude baselined dllhost destinations // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=NetworkConnectIP4 | in(RemotePort, values=[41397, 6800]) | ContextBaseFileName=/^(FamItrf[c2]|dllhost|NitroPromotion|Nitro_Scan2PDFApp|rundll32)(\.exe)?$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort])
3 required fields · View requirements
Required telemetry
- SentinelOne IP Connect events
Required fields
event.type: Event typedst.port.number: Remote portsrc.process.name: Connecting process
Recommended fields
dst.ip.address: Remote addressendpoint.name: Host
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'IP Connect' dst.port.number = * src.process.name = * | limit 5 | columns event.time, src.process.name, dst.ip.address, dst.port.number
// HUNT: SHINYROAD and ADAPTAGENT C2 ports from sideload hosts // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1571, T1219.002 // CONF: medium | FP: medium // FALSE POSITIVES: other software using port 6800 // EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate: an outbound connection to port 41397 or 6800 from a Radmin launcher, dllhost, NitroPromotion, Nitro_Scan2PDFApp or rundll32. The connecting process is src.process.name on an IP Connect event. // REQUIRES: SentinelOne IP Connect network telemetry with the connecting process // TUNING: Restrict to non-private destinations with net_rfc1918 if internal services use port 6800, and exclude baselined dllhost destinations. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Other software using port 6800 can match when it runs under one of the listed process names. // VALIDATION LEVEL: static-reviewed event.type = 'IP Connect' dst.port.number in (41397, 6800) src.process.name matches '^(FamItrf[c2]|dllhost|NitroPromotion|Nitro_Scan2PDFApp|rundll32)(\\.exe)?$' | columns event.time, endpoint.name, src.process.user, src.process.name, dst.ip.address, dst.port.number | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeRemotePort: Remote portInitiatingProcessFileName: Connecting process
Recommended fields
RemoteIP: Remote address
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, InitiatingProcessFileName, RemotePort | take 5
// HUNT: SHINYROAD and ADAPTAGENT C2 ports from sideload hosts // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1571, T1219.002 // CONF: medium | FP: medium // FALSE POSITIVES: other software using port 6800 // EQUIVALENCE: equivalent-with-schema-assumptions - Same network predicate on DeviceNetworkEvents: an outbound connection to port 41397 or 6800 from one of the five named processes. The (?i) prefix reproduces the CQL i flag. // REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents) // TUNING: Restrict to non-private destinations with ipv4_is_private if internal services use port 6800, and exclude baselined dllhost destinations. // LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. Other software using port 6800 can match when it runs under one of the listed process names. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceNetworkEvents | where Timestamp >= ago(30d) | where RemotePort in (41397, 6800) | where InitiatingProcessFileName matches regex @"(?i)^(FamItrf[c2]|dllhost|NitroPromotion|Nitro_Scan2PDFApp|rundll32)(\.exe)?$" | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, RemoteIP, RemotePort | sort by Timestamp desc | take 200
Looks for: rsetup64.exe run with the intsetup argument. CROSSROAD invokes the genuine Radmin Server installer this way; ManageEngine found it staged but never armed. Triage on the parent: an unsigned or AOMEI-path parent is the campaign. FP: Sanctioned Radmin rollouts use the same installer. Exclude your deployment tool as parent.
Known limitations and deployment notes
- PowerQuery: The creator image path is not projected; pivot on src.process.name and the host to find it.
- PowerQuery assumes: tgt.process.name holds the created file name with its extension; src.process.name holds the creator and is shown for triage.
- KQL – Defender XDR: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab.
- KQL – Defender XDR: A process the sensor did not observe starting is not captured.
- KQL – Defender XDR assumes: FileName holds the created file name; InitiatingProcessFolderPath is projected so the AOMEI-directory check in the tuning note can be made.
// HUNT: Silent Radmin Server install (rsetup64.exe /intsetup) // MITRE: T1219.002 // CONF: medium // FP: medium // COST: low // REQUIRES: Process creation with command line and parent // FALSE POSITIVES: IT-managed Radmin deployments // TUNING: exclude ParentBaseFileName values for your software deployment agent; keep any hit whose parent image path sits under an AOMEI directory // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | FileName=/^rsetup64(\.exe)?$/i | CommandLine=/intsetup/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine])
3 required fields · View requirements
Required telemetry
- SentinelOne Process Creation events
Required fields
event.type: Event typetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creator processendpoint.name: Host
Fallbacks
endpoint.name→agent.uuid: Changes the displayed host identifier only.
Readiness test
event.type = 'Process Creation' tgt.process.name = * tgt.process.cmdline = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline
// HUNT: Silent Radmin Server install (rsetup64.exe /intsetup) // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1219.002 // CONF: medium | FP: medium // FALSE POSITIVES: IT-managed Radmin deployments // EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: a created process named rsetup64 whose command line contains intsetup. The created process is tgt.process.*. contains and matches are case-insensitive, which reproduces the CQL i flag. // REQUIRES: SentinelOne process-creation telemetry with created name and command line // TUNING: Exclude creator names for your software deployment agent; keep any hit whose creator image path sits under an AOMEI directory. // LOOKBACK: 120d (covers the April to July 2026 campaign window; set with the console time picker) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: The creator image path is not projected; pivot on src.process.name and the host to find it. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' tgt.process.name matches '^rsetup64(\\.exe)?$' tgt.process.cmdline contains 'intsetup' | columns event.time, endpoint.name, src.process.user, src.process.name, tgt.process.name, tgt.process.image.path, tgt.process.cmdline | limit 200
Automated 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 processInitiatingProcessFolderPath: Initiating process path
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: Silent Radmin Server install (rsetup64.exe /intsetup)
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1219.002
// CONF: medium | FP: medium
// FALSE POSITIVES: IT-managed Radmin deployments
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate on DeviceProcessEvents: a created process named rsetup64 whose command line contains intsetup. contains and in~ are case-insensitive, which reproduces the CQL i flag.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: Exclude initiating process names for your software deployment agent; keep any hit whose initiating process path sits under an AOMEI directory.
// LOOKBACK: 30d (Advanced Hunting retention; the CQL tab covers 120d)
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Advanced Hunting keeps 30 days, so the 120-day campaign window is covered only on the CQL tab. 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(30d)
| where FileName in~ ("rsetup64.exe", "rsetup64")
| where ProcessCommandLine contains "intsetup"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, FileName, FolderPath, ProcessCommandLine
| sort by Timestamp desc
| take 200Detection Rules by Platform
CrowdStrike: Custom IOA
| IOA name | Pattern | Exclusions | Path |
|---|---|---|---|
| Updater launches executable from html or tmphtml | Parent ABNotify.exe or Backupper.exe, child image under an html or tmphtml directory (Promote Q1) | None expected | Detect; Prevent candidate after 14 clean days |
| CROSSROAD self-delete flag | CommandLine contains --nitrod (Promote Q2) | None expected | Detect |
| Campaign scheduled tasks | Task name RadminComponentRuntime or NitroRuntime, or arguments with --runtimechk (Promote Q3) | None expected | Detect |
| Radmin launcher run for the sideload | FamItrfc.exe or FamItrf2.exe with --runtimechk or from AppData\Local\Radmin (Promote Q4) | Sanctioned Radmin deployments | Detect only (signed legitimate binary, never Prevent) |
| Sideload DLL drop in user profile | PE write of BlkScrDl.dll, csvc\RuntimeComponent.dll, Eztwain4.dll, NitroPromotion.exe under AppData\Local (Promote Q5) | None expected | Detect |
| Hidden PowerShell launch of NitroPromotion.exe | NitroPromotion.exe, or Start-Process naming it (Promote Q11) | None expected | Detect |
prevent. Every other indicator is detect. Nothing that is a legitimate Famatech or Nitro product binary is imported.SentinelOne: custom detection rules
| Rule | Source query | Run as | Notes |
|---|---|---|---|
| Updater launches executable from html or tmphtml | Q1, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device" |
| CROSSROAD self-delete flag | Q2, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device" |
| Campaign scheduled tasks | Q3, PowerQuery tab | Hunt only until the gap is closed | Q3 is only an approximation here. SentinelOne telemetry in this field model has no scheduled-task registration event, so the hunt pivots on the command line of any created process that names RadminComponentRuntime or NitroRuntime. That catches schtasks.exe and PowerShell task registration by command line, but not a task registered... |
| Radmin launcher run for the sideload | Q4, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device" |
| Sideload DLL drop in user profile | Q5, PowerQuery tab | Hunt only until the gap is closed | Q5 is only an approximation here. The CQL matches PE-file-written events at four exact paths. SentinelOne has no PE-specific write event here, so this pane matches File Creation and File Modification at the same paths. The paths are matched as substrings rather than anchored at the end, so a longer name that merely starts with a... |
| Hidden PowerShell launch of NitroPromotion.exe | Q11, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device" |
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 |
|---|---|---|---|
| Updater launches executable from html or tmphtml | 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 |
| CROSSROAD self-delete flag | 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 |
| Campaign scheduled tasks | Q3, 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 |
| Radmin launcher run for the sideload | Q4, 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 |
| Sideload DLL drop in user profile | Q5, KQL – Defender XDR tab | Hunt only until the gap is closed | Q5 is only an approximation here. The CQL matches PE-file-written events at four exact paths. DeviceFileEvents has no PE-specific write action, so this pane matches FileCreated and FileModified at the same paths. The paths are matched as substrings rather than anchored at the end. |
| Hidden PowerShell launch of NitroPromotion.exe | Q11, 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 |
Q6, Q7, Q8, Q9, Q10, Q12, Q13, Q14 and Q15 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
type,value,action,severity,expiration,description,tags sha256,6670ab3f0434a42f33416f8354150b1701e7615f3cfa2f1635e54f8df0f6eec7,detect,high,2027-04-18,upgrade-now.zip (May 20 CROSSROAD archive) CROSSROAD archive,campaign:UNC7017 sha256,11e576df44e9c9eef72b9f5b699291bcdb200a19130778ec231e5b0987b913ad,detect,high,2027-04-18,upgrade-now.exe (May 20 CROSSROAD loader) CROSSROAD loader,campaign:UNC7017 sha256,130c3970f1c900191615b99d8be522d6a488a3d3b73938896ed937092007d96a,detect,high,2027-04-18,upgrade-now.exe (28 May endpoint sample) CROSSROAD loader seen on endpoint,campaign:UNC7017 sha256,9a58e8f49edf5644e5728f74cf36f560d3d486fa51db4db69d927963c7aca450,detect,high,2027-04-18,upgrade-now.exe (July 16 CROSSROAD loader) CROSSROAD loader,campaign:UNC7017 sha256,b360b6db0c2b668317edc1504a7c39ef047f0d925c8f113b521f6724c157ae21,detect,high,2027-04-18,upgrade-now.exe (July 20 CROSSROAD loader) CROSSROAD loader,campaign:UNC7017 sha256,9ed503c6a9a3a493fb859401e9ed469eed77ab9927341964d6dbfbfa6255aeba,detect,high,2027-04-18,upgrade-now.zip (July 20 CROSSROAD archive) CROSSROAD archive,campaign:UNC7017 sha256,d167bc898e856da8a4003cb4dfc7de55fea109eee3cd64fc8999710314109464,prevent,critical,2027-04-18,component.zip / en.zip (SHINYROAD package) SHINYROAD package; two sources,campaign:UNC7017 sha256,5837bc26a73cfb46ffd466cf889f03ce2f6a48f1f3fd42ec8be22189d51cab1e,prevent,critical,2027-04-18,BlkScrDl.dll (malicious sideloaded DLL) SHINYROAD loader DLL; two sources,campaign:UNC7017 sha256,8fbaa74b27dc1e89abfc694e834378746f0ad37a688098c5f8f511410157c8b8,detect,high,2027-04-18,RuntimeComponent.dll (PocketPy engine) SHINYROAD implant DLL,campaign:UNC7017 sha256,4c0a75e0f62ddc0bb350c224fe996f3b2ab08dcb0d288231c61ffe9406c7045b,detect,high,2027-04-18,config.zip (Python bytecode archive) SHINYROAD payload archive,campaign:UNC7017 sha256,4b4d25b3501519476f9a62a88a781e4f446f669280eb5e98c6b96eb6d3743431,detect,high,2027-04-18,metadata.dat (ChaCha20 payload) SHINYROAD encrypted payload,campaign:UNC7017 sha256,100657d3e205bfd0e96f1c088ae1739b50657592a696201467b4c65b7d36289a,detect,high,2027-04-18,WNTestdll.dll Related DLL named in ManageEngine hash table,campaign:UNC7017 sha256,53b7d01163ccb679001f20e4a8a5e1f33d5d2837c375315f6514cc820f81bd12,detect,high,2027-04-18,Sample listed as hunted VirusTotal sample ManageEngine hunted sample; function not stated,campaign:UNC7017 sha256,14b31918075e6130b9123158743db0a81e2d7bfcf6d39731ee7d9200a98cd591,detect,high,2027-04-18,NitroPromotion.exe (ADAPTAGENT launcher) ADAPTAGENT launcher,campaign:UNC7017 sha256,8c8090aca8ddb1cf968d2b810457e0cfce97c03cd721f70d2eca7f37f2681fc4,detect,high,2027-04-18,Eztwain4.dll (ADAPTAGENT helper DLL) ADAPTAGENT sideloaded DLL,campaign:UNC7017 sha256,7b252be444dee2fb2a57043a823dc5b5e1fae970aea91ee2c8339aa70329969d,detect,high,2027-04-18,abe.exe (secondary payload) Secondary payload; role not described,campaign:UNC7017 sha256,924ad92d89dde577d5858b2ebc5703a485467842d78e98b75019b2e2e0ac3fe6,detect,medium,2027-04-18,down_html.ini (tampered July 16) Tampered AOMEI update config,campaign:UNC7017 sha256,f2a7d0a4c8613a9cdd31ede6131b27040c0571ca97970b245a710191806d7ac4,detect,medium,2027-04-18,down_html.ini (tampered July 20) Tampered AOMEI update config,campaign:UNC7017 sha256,a984fdb0830760446d2b599b263506e01fcf1e560a31a057c3bbe7b3f8ac155d,detect,medium,2027-04-18,down_html.ini (ManageEngine volatile hash) Tampered AOMEI update config,campaign:UNC7017 sha256,d2f9ef2429312667f0512504b6a19ba4a9cc5514f1b2933923dd42f75a1c5934,detect,medium,2027-04-18,Notifycfg.ini (tampered July 16) Tampered notification config with dummy sections,campaign:UNC7017 sha256,286e0d8c7bdfaa7c251a3e8f0f2fefbe25c67586c0149d39b1095c89dc6023c8,detect,medium,2027-04-18,Notifycfg.ini (tampered July 20 WebDAV) Tampered notification config with WebDAV popup,campaign:UNC7017 sha256,2e3b4bfb86224f079b8762ce78bb7ff10afdbf8421b08b23e96429f2e1ee7b50,detect,medium,2027-04-18,Notifycfg.ini (ManageEngine volatile hash) Tampered notification config,campaign:UNC7017 md5,31323e7e738230fa6a8deef2b7894666,detect,medium,2027-04-18,July 16 declared MD5 for the malicious upgrade-now.zip,campaign:UNC7017 md5,bf73c4391b58ca95220ce3ae03f66dcf,detect,medium,2027-04-18,July 20 declared MD5 for the malicious upgrade-now.zip,campaign:UNC7017 ipv4,8.210.84.56,detect,high,2027-04-18,WebDAV NTLM target and Adaptix C2,campaign:UNC7017 ipv4,8.218.65.184,detect,critical,2027-04-18,SHINYROAD C2 ports 41397 and 443,campaign:UNC7017 ipv4,47.239.60.117,detect,high,2027-04-18,Secondary infrastructure,campaign:UNC7017 domain,storage-data.com,detect,high,2027-04-18,Threat actor domain,campaign:UNC7017 domain,promotion.storage-data.com,detect,high,2027-04-18,Stage-2 dropper and component staging,campaign:UNC7017 domain,aomei.storage-data.com,detect,high,2027-04-18,Stage-3 payload staging,campaign:UNC7017 domain,serv.oss-cn-hongkong.aliyuncs.com,detect,high,2027-04-18,Alibaba Cloud OSS bucket used for April and May staging,campaign:UNC7017
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert FileSha256,6670ab3f0434a42f33416f8354150b1701e7615f3cfa2f1635e54f8df0f6eec7,Audit,upgrade-now.zip,upgrade-now.zip (May 20 CROSSROAD archive) CROSSROAD archive,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,11e576df44e9c9eef72b9f5b699291bcdb200a19130778ec231e5b0987b913ad,Audit,upgrade-now.exe,upgrade-now.exe (May 20 CROSSROAD loader) CROSSROAD loader,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,130c3970f1c900191615b99d8be522d6a488a3d3b73938896ed937092007d96a,Audit,upgrade-now.exe,upgrade-now.exe (28 May endpoint sample) CROSSROAD loader seen on endpoint,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,9a58e8f49edf5644e5728f74cf36f560d3d486fa51db4db69d927963c7aca450,Audit,upgrade-now.exe,upgrade-now.exe (July 16 CROSSROAD loader) CROSSROAD loader,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,b360b6db0c2b668317edc1504a7c39ef047f0d925c8f113b521f6724c157ae21,Audit,upgrade-now.exe,upgrade-now.exe (July 20 CROSSROAD loader) CROSSROAD loader,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,9ed503c6a9a3a493fb859401e9ed469eed77ab9927341964d6dbfbfa6255aeba,Audit,upgrade-now.zip,upgrade-now.zip (July 20 CROSSROAD archive) CROSSROAD archive,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,d167bc898e856da8a4003cb4dfc7de55fea109eee3cd64fc8999710314109464,Block,component.zip / en.zip,component.zip / en.zip (SHINYROAD package) SHINYROAD package; two sources,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,5837bc26a73cfb46ffd466cf889f03ce2f6a48f1f3fd42ec8be22189d51cab1e,Block,BlkScrDl.dll,BlkScrDl.dll (malicious sideloaded DLL) SHINYROAD loader DLL; two sources,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,8fbaa74b27dc1e89abfc694e834378746f0ad37a688098c5f8f511410157c8b8,Audit,RuntimeComponent.dll,RuntimeComponent.dll (PocketPy engine) SHINYROAD implant DLL,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,4c0a75e0f62ddc0bb350c224fe996f3b2ab08dcb0d288231c61ffe9406c7045b,Audit,config.zip,config.zip (Python bytecode archive) SHINYROAD payload archive,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,4b4d25b3501519476f9a62a88a781e4f446f669280eb5e98c6b96eb6d3743431,Audit,metadata.dat,metadata.dat (ChaCha20 payload) SHINYROAD encrypted payload,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,100657d3e205bfd0e96f1c088ae1739b50657592a696201467b4c65b7d36289a,Audit,WNTestdll.dll Related DLL named in ManageEngine hash table,WNTestdll.dll Related DLL named in ManageEngine hash table,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,53b7d01163ccb679001f20e4a8a5e1f33d5d2837c375315f6514cc820f81bd12,Audit,Sample listed as hunted VirusTotal sample ManageEngine hunte,Sample listed as hunted VirusTotal sample ManageEngine hunted sample; function not stated,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,14b31918075e6130b9123158743db0a81e2d7bfcf6d39731ee7d9200a98cd591,Audit,NitroPromotion.exe,NitroPromotion.exe (ADAPTAGENT launcher) ADAPTAGENT launcher,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,8c8090aca8ddb1cf968d2b810457e0cfce97c03cd721f70d2eca7f37f2681fc4,Audit,Eztwain4.dll,Eztwain4.dll (ADAPTAGENT helper DLL) ADAPTAGENT sideloaded DLL,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,7b252be444dee2fb2a57043a823dc5b5e1fae970aea91ee2c8339aa70329969d,Audit,abe.exe,abe.exe (secondary payload) Secondary payload; role not described,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,924ad92d89dde577d5858b2ebc5703a485467842d78e98b75019b2e2e0ac3fe6,Audit,down_html.ini,down_html.ini (tampered July 16) Tampered AOMEI update config,2027-04-18T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True FileSha256,f2a7d0a4c8613a9cdd31ede6131b27040c0571ca97970b245a710191806d7ac4,Audit,down_html.ini,down_html.ini (tampered July 20) Tampered AOMEI update config,2027-04-18T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True FileSha256,a984fdb0830760446d2b599b263506e01fcf1e560a31a057c3bbe7b3f8ac155d,Audit,down_html.ini,down_html.ini (ManageEngine volatile hash) Tampered AOMEI update config,2027-04-18T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True FileSha256,d2f9ef2429312667f0512504b6a19ba4a9cc5514f1b2933923dd42f75a1c5934,Audit,Notifycfg.ini,Notifycfg.ini (tampered July 16) Tampered notification config with dummy sections,2027-04-18T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True FileSha256,286e0d8c7bdfaa7c251a3e8f0f2fefbe25c67586c0149d39b1095c89dc6023c8,Audit,Notifycfg.ini,Notifycfg.ini (tampered July 20 WebDAV) Tampered notification config with WebDAV popup,2027-04-18T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True FileSha256,2e3b4bfb86224f079b8762ce78bb7ff10afdbf8421b08b23e96429f2e1ee7b50,Audit,Notifycfg.ini,Notifycfg.ini (ManageEngine volatile hash) Tampered notification config,2027-04-18T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True IpAddress,8.210.84.56,Audit,WebDAV NTLM target and Adaptix C2,WebDAV NTLM target and Adaptix C2,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True IpAddress,8.218.65.184,Audit,SHINYROAD C2 ports 41397 and 443,SHINYROAD C2 ports 41397 and 443,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True IpAddress,47.239.60.117,Audit,Secondary infrastructure,Secondary infrastructure,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,storage-data.com,Audit,Threat actor domain,Threat actor domain,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,promotion.storage-data.com,Audit,Stage-2 dropper and component staging,Stage-2 dropper and component staging,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,aomei.storage-data.com,Audit,Stage-3 payload staging,Stage-3 payload staging,2027-04-18T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True DomainName,serv.oss-cn-hongkong.aliyuncs.com,Audit,Alibaba Cloud OSS bucket used for April and May staging,Alibaba Cloud OSS bucket used for April and May staging,2027-04-18T00: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. Left out because this block maps only SHA-1, SHA-256, IPv4, domain and URL indicators: md5 2. Prevent rows import as Block, without remediation; network indicators cannot use BlockAndRemediate. 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.
# Proxy / DNS / web gateway blocklist (not importable to Falcon IOC Management) https://promotion.storage-data.com/download/abpopup/upgrade-now.zip https://promotion.storage-data.com/config/component.zip https://promotion.storage-data.com/config/en.zip https://promotion.storage-data.com/config/whitelist.ini https://storage.googleapis.com/aomeiupdate/upgrade-now.zip https://storage.googleapis.com/aomeiupdate/component.zip # Block outbound SMB (TCP 445) and WebDAV to external addresses \\8.210.84.56\DavWWWRoot\campaign\24hour-flash-sale.html # User-Agent strings to search in proxy logs Anyviewer-loader/1.0 Anyviewer-ipcheck/1.0 # Reflection services used by the geolocation guardrail (legitimate; hunt by process, do not block) myip.opendns.com o-o.myaddr.l.google.com checkip.amazonaws.com api.ipify.org ifconfig.me icanhazip.com
# Behavioral signatures (all from the Mandiant report unless marked)
Mutex: Global\{F9E6E3C0-E11A-4F85-9944-AF4AD6E05883}
Shared memory: Local\963d3c60-d406-4ad6-aa1e-dc8e45061ccf
COM CLSID: {21191641-0D7F-4CD9-9572-01AAE6E7851B}
COM AppID: {9AD3E98A-4C7E-4F37-982D-D73D41E4F91B}
Scheduled task: \RadminComponentRuntime (description: Radmin Component runtime checkable.)
Scheduled task: \NitroRuntime
Task command: %LOCALAPPDATA%\Radmin\FamItrfc.exe --runtimechk (every 3 hours)
Self-delete flag: --nitrod (env NITRODP, NITRODT)
ADS name: :wtfbbq
Product string: AOMEI Update Compent (typo; dropper version info)
Install helper: rsetup64.exe /intsetup
Config bloat: Notifycfg.ini with 35,000+ [dummy_XXXXXX] sections
Popup coercion: [PopWindow1] Address="file://...DavWWWRoot..." Width=1 Height=1
Staging paths: %LOCALAPPDATA%\Radmin\ | %LOCALAPPDATA%\Radmin\csvc\ | %LOCALAPPDATA%\Nitro Pro\ | %TEMP%\svc*.tmp
AOMEI paths: ...\AOMEI\*\Tmphtml\ | ...\AOMEI\*\html\
# Named malware and tooling CROSSROAD downloader (upgrade-now.exe) SHINYROAD backdoor (BlkScrDl.dll loader, RuntimeComponent.dll PocketPy engine, config.zip, metadata.dat) ADAPTAGENT Adaptix C2 agent (Eztwain4.dll, launched via NitroPromotion.exe) Adaptix C2 post-exploitation framework UNC7017 tracking name (Google Threat Intelligence Group / Mandiant) # Abused LEGITIMATE binaries: hunt the behavior, never prevent on the file FamItrfc.exe / FamItrf2.exe Famatech Radmin launcher (signed) WinLpcDl.dll / WinLpcDl2.dll Radmin components Nitro_Scan2PDFApp.exe Nitro signed binary mfc140u.dll Microsoft MFC runtime
# Read-only configuration audit (run per endpoint)
Get-Service WebClient | Select-Object Status, StartType
Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' -Name RestrictSendingNTLMTraffic -ErrorAction SilentlyContinue
Get-NetFirewallRule -DisplayName 'HuntPack Block*' -ErrorAction SilentlyContinue | Select-Object DisplayName, Enabled, Action
(Get-MpPreference).AttackSurfaceReductionRules_Ids
Get-ScheduledTask -TaskName RadminComponentRuntime,NitroRuntime -ErrorAction SilentlyContinue
Get-ChildItem "${env:ProgramFiles(x86)}\AOMEI" -Recurse -Include *.exe -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match '\\(html|tmphtml)\\' }
Falcon IOC Management does not import URLs or UNC paths, which is why they sit in the gateway block. Expiration is set six months out because the infrastructure rotated across three windows.
Detection Validation Gates
| Gate | Requirement | Evidence |
|---|---|---|
| 1 · Telemetry ready | Process creation with parent and command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents), scheduled task registration, executable writes, outbound connections and DNS flowing from AOMEI hosts; 120 days of retention or the longest available | Event counts per source for AOMEI hosts |
| 2 · Benign baseline | Run Q1 to Q15 in hunt mode; catalogue sanctioned Radmin deployments, dllhost children and port 6800 users | Per-query exclusion list |
| 3 · Positive tests | In a lab, copy a harmless executable named like the dropper into an html folder under a test AOMEI path and launch it from a renamed test parent; register a test task named RadminComponentRuntime; run a benign binary with --nitrod | Q1, Q2, Q3 fire |
| 4 · Promotion | Promote IOA candidates after zero unexplained hits over the baseline window; keep every legitimate-binary IOA at Detect | Change record |
Hardening: Tiered
- Block outbound SMB (TCP 445) and WebDAV to external addresses, and block the campaign IPs and domains at the perimeter and in the host firewall. Authority: MITRE M1037 Filter Network Traffic; also the report's own network guidance. Verify: playbook PB1 audit commands, or a failed test connection.
- Disable the WebClient (WebDAV redirector) service on endpoints that do not need WebDAV. Authority: MITRE M1042 Disable or Remove Feature or Program. best-practice, no formal benchmark cited here. Verify:
Get-Service WebClient. - Audit, then restrict, outgoing NTLM (Network security: Restrict NTLM: Outgoing NTLM traffic to remote servers). Authority: M1037 and Microsoft NTLM restriction guidance. Verify: the registry value and the NTLM Operational log (PB2).
- Inventory AOMEI Backupper and AnyViewer and run the read-only triage in PB4 on every host that has them. Authority: M1047 Audit.
- Application control that denies execution and DLL loads from user-profile Radmin and Nitro Pro folders and from AOMEI html and tmphtml folders (WDAC or AppLocker, audit first). Authority: M1038 Execution Prevention; Microsoft WDAC and AppLocker documentation. Verify: audit events for blocked paths, then a CQL spot-check that Q1 and Q5 return nothing.
- Defender ASR rule: block executable files that do not meet a prevalence, age or trusted-list criterion, audit mode first (PB3). Authority: Microsoft attack surface reduction rules reference; M1038. Verify:
Get-MpPreference. - Constrain the AOMEI notification agent's egress with a per-program firewall rule where the promotional channel is not business-critical (PB3). Authority: M1037, M1042. best-practice, no formal benchmark.
- Promote Q1, Q2, Q3, Q5, Q11 to Custom IOA at Detect, Prevent only after baseline. Authority: M1049 Antivirus/Antimalware (behavioral).
- Update-channel trust. Prefer software that signs its remote configuration and verifies Authenticode on downloads, pins update domains and refuses file:// and UNC handlers in embedded web views, as the report recommends to vendors. Weigh this in procurement and vendor risk reviews. Authority: ATT&CK T1195.002 mitigations (M1013, M1016, M1051) and the report's vendor guidance.
- Proxy policy for update clients. Allowlist the vendor's official update hostnames for background updaters and deny arbitrary public cloud storage buckets and newly registered domains. The July 20 window used a public Google Cloud Storage bucket specifically to pass newly-registered-domain filters. Authority: M1037.
- Credential hygiene. For users on hosts that ran AOMEI products during a window, assume NetNTLMv2 exposure: reset passwords, prefer Kerberos, enforce SMB signing and consider Protected Users membership for privileged accounts. Authority: M1027 Password Policies, M1026 Privileged Account Management.
- Retrospective-hunt capability. Retain endpoint process, network and DNS telemetry long enough to hunt a four-month-old ephemeral window. Authority: M1047 Audit.
Deployable Playbooks
PB1 · Block egress to the campaign and external SMB (host firewall)
Prerequisites: local administrator; confirm no business file share is reached over a public address (the Internet keyword also covers VPN-assigned public ranges). Reboot required: no. Rollback: remove the two rules with the last command.
# Block outbound SMB to non-private addresses New-NetFirewallRule -DisplayName "HuntPack Block SMB Egress" -Direction Outbound -Action Block -Protocol TCP -RemotePort 445 -RemoteAddress Internet -Profile Any # Block the campaign IP addresses (WebDAV/Adaptix, SHINYROAD C2, secondary) New-NetFirewallRule -DisplayName "HuntPack Block UNC7017 IPs" -Direction Outbound -Action Block -RemoteAddress 8.210.84.56,8.218.65.184,47.239.60.117 -Profile Any # Verify Get-NetFirewallRule -DisplayName "HuntPack Block*" | Format-Table DisplayName, Enabled, Direction, Action # Rollback # Remove-NetFirewallRule -DisplayName "HuntPack Block*"
PB2 · Audit outgoing NTLM and disable the WebClient service
Prerequisites: local administrator; confirm no application depends on WebDAV or outbound NTLM (value 1 only audits, it does not block). Reboot required: no for the service; a policy refresh or restart may be needed for the NTLM value to take effect. Rollback: restore the service start type and delete the registry value.
# Audit all outgoing NTLM (1 = audit, 2 = deny all). Start with audit; review the NTLM Operational log first. New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' -Name RestrictSendingNTLMTraffic -PropertyType DWord -Value 1 -Force # Disable the WebDAV redirector service Stop-Service WebClient -ErrorAction SilentlyContinue Set-Service WebClient -StartupType Disabled # Verify Get-Service WebClient | Select-Object Status, StartType Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' -Name RestrictSendingNTLMTraffic # Rollback # Set-Service WebClient -StartupType Manual # Remove-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' -Name RestrictSendingNTLMTraffic
PB3 · ASR prevalence rule (audit) and AOMEI notification agent egress
Prerequisites: Microsoft Defender Antivirus is the active engine and not already governed by Intune or GPO for ASR; local administrator; AOMEI installed under Program Files (x86). Reboot required: no. Rollback: remove the ASR rule and the firewall rule. Blocking ABNotify.exe disables promotional popups only; confirm the product still updates through your approved channel.
# ASR: executables that do not meet prevalence/age/trusted-list criteria. Audit first (2 = audit, 1 = block)
Add-MpPreference -AttackSurfaceReductionRules_Ids 01443614-cd74-433a-b99e-2ecdc07bfc25 -AttackSurfaceReductionRules_Actions AuditMode
# Constrain the AOMEI notification agent
Get-ChildItem "${env:ProgramFiles(x86)}\AOMEI" -Recurse -Filter ABNotify.exe -ErrorAction SilentlyContinue | ForEach-Object {
New-NetFirewallRule -DisplayName "HuntPack Block ABNotify Egress" -Direction Outbound -Action Block -Program $_.FullName -Profile Any
}
# Verify
(Get-MpPreference).AttackSurfaceReductionRules_Ids
Get-NetFirewallRule -DisplayName "HuntPack Block ABNotify*" | Format-Table DisplayName, Enabled, Action
# Rollback
# Remove-MpPreference -AttackSurfaceReductionRules_Ids 01443614-cd74-433a-b99e-2ecdc07bfc25
# Remove-NetFirewallRule -DisplayName "HuntPack Block ABNotify*" PB4 · Read-only endpoint triage for AOMEI hosts
Prerequisites: local administrator. Reboot required: no. Rollback: none required, the script only reads and hashes files, tasks and services; it changes nothing. Run it before any cleanup so the cached configuration is preserved.
$ErrorActionPreference = 'SilentlyContinue'
# 1. Campaign tasks
Get-ScheduledTask -TaskName RadminComponentRuntime,NitroRuntime | Select-Object TaskName, State, @{n='Action';e={$_.Actions.Execute + ' ' + $_.Actions.Arguments}}
# 2. Sideload artifacts in every user profile
Get-ChildItem 'C:\Users\*\AppData\Local\Radmin','C:\Users\*\AppData\Local\Nitro Pro' -Recurse -Include BlkScrDl.dll,RuntimeComponent.dll,config.zip,metadata.dat,Eztwain4.dll,NitroPromotion.exe |
Get-FileHash -Algorithm SHA256 | Format-Table Hash, Path -AutoSize
# 3. Cached AOMEI configuration (preserve copies before cleanup) and bloat check
$aomei = "${env:ProgramFiles(x86)}\AOMEI"
Get-ChildItem $aomei -Recurse -Include down_html.ini,Notifycfg.ini | ForEach-Object {
[pscustomobject]@{ Path = $_.FullName; Sha256 = (Get-FileHash $_.FullName -Algorithm SHA256).Hash; Lines = (Get-Content $_.FullName | Measure-Object -Line).Lines; DummySections = (Select-String -Path $_.FullName -Pattern '^\[dummy_').Count }
}
# 4. Executables inside the rendering or staging directories
Get-ChildItem $aomei -Recurse -Include *.exe | Where-Object { $_.FullName -match '\\(html|tmphtml)\\' } | Select-Object FullName, LastWriteTime
# 5. WebClient and NTLM posture
Get-Service WebClient | Select-Object Status, StartType
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain hosts with a Q1 to Q5 or Q9 hit while keeping them powered on; block the campaign IPs and domains at perimeter and proxy; block outbound SMB and WebDAV to external addresses | SOC / IR lead | Containment timestamps; block change records |
| 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 / IR lead | Containment timestamps; block change records |
| Isolate: by platform | Platform-specific isolation actions are added where the SentinelOne and Defender tabs exist | SOC | Isolation logs |
| Preserve | Before cleanup, copy and hash down_html.ini, Notifycfg.ini, the Tmphtml and html directories, the Radmin and Nitro Pro user-profile folders and the temp svc*.tmp files; capture proxy and firewall records for the second each alert fired. The live vendor URL is not evidence | DFIR | Evidence hash list |
| Scope | Run Q1 to Q15 across the whole retention window, April to July 2026 at minimum, on every host that has or had AOMEI Backupper or AnyViewer; check DNS and proxy for the campaign domains | Threat hunt | Host list with first and last seen |
| Credentials | Treat users on exposed hosts as NetNTLMv2-exposed: reset passwords, review sign-ins from 8.210.84.56 or other unfamiliar sources, rotate service credentials used on those hosts | Identity team | Reset log; sign-in review |
| Eradicate | Delete tasks RadminComponentRuntime and NitroRuntime after exporting their XML; end the Radmin launcher and any dllhost surrogate tied to the CLSID; remove the COM registration and the Radmin and Nitro Pro user-profile folders; uninstall Radmin Server if it is not sanctioned; confirm AOMEI is on a build the vendor has remediated | IR / IT ops | Removal list; task XML export |
| Recover | Rebuild hosts where the in-memory implant ran with elevated rights (the chain inherits the AOMEI process privileges and can fetch more code); restore from known-good images | IT ops | Rebuild records |
| Post-incident | Promote validated IOAs; add the cached-config check to endpoint baselines; report the finding to the vendor; review procurement of auto-updating utilities | Detection eng | Lessons-learned record |
Detection Coverage Map
| Technique / behavior | CQL | IOA | CrowdStrike coverage | SentinelOne | Defender XDR |
|---|---|---|---|---|---|
| Updater child from html or tmphtml (T1195.002, T1036.005) | Q1 | Yes | Good | Good, with assumptions | Good, with assumptions |
| Self-deletion flag (T1070.004) | Q2 | Yes | Good | Good, with assumptions | Good, with assumptions |
| NTFS stream rename (T1564.004) | none | No | GAP | GAP | GAP |
| Scheduled task persistence (T1053.005) | Q3, Q4 | Yes | Good | Partial: Q3 approximate | Good, with assumptions |
| DLL sideload (T1574.001) | Q4, Q5 | Yes | Good | Partial: Q5 approximate | Partial: Q5 approximate |
| COM server registration (T1559.001) | Q7 | No | Partial | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| Shell from launcher or dllhost (T1059.006) | Q6 | No | Partial | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| Guardrail IP lookup (T1016, T1614, T1480) | Q8 | No | Partial | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| Named mutex and shared memory (T1480) | none | No | GAP | GAP | GAP |
| In-memory PocketPy and ChaCha20 stage (T1027.013, T1140) | none | No | GAP | GAP | GAP |
| C2 infrastructure (T1571, T1583.001) | Q9, Q10, Q14 | IOC Mgmt | Good, perishable | Good, with assumptions | Good, with assumptions |
| ADAPTAGENT staging (T1059.001) | Q11 | Yes | Good | Good, with assumptions | Good, with assumptions |
| WebDAV/NTLM coercion (T1187) | Q12 | No | Partial | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| Known hashes | Q13 | IOC Mgmt | Partial | Partial: same limit as CrowdStrike | Partial: Q13 telemetry gap |
| Radmin Server install (T1219.002) | Q15 | No | Partial | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| Traffic chaffing (T1001.001) | none | No | GAP | GAP | GAP |
| Server-side config tampering (T1195.002) | none | No | GAP | GAP | GAP |
Gaps are stated honestly: the vendor-side edits happen outside endpoint telemetry; the mutex, shared memory, in-memory decryption and traffic chaffing have no default endpoint event; the stream rename needs Sysmon or equivalent (native hunt N3). Validation gates are in section 11.
Hunt Summary Ticket
TITLE: AOMEI Backupper Supply-Chain Tampering (UNC7017): CROSSROAD, SHINYROAD, ADAPTAGENT
SEVERITY: HIGH (software supply chain, post-compromise persistence and credential exposure)
SCOPE: Windows endpoints with AOMEI Backupper or AnyViewer, April to July 2026 and any persisting payload
HYPOTHESIS: A host polled AOMEI update or notification infrastructure during a 5 to 10 minute tampering
window, ran upgrade-now.exe from the AOMEI html directory, and now carries a Radmin
launcher sideloading BlkScrDl.dll, a COM-hosted PocketPy implant, and possibly ADAPTAGENT.
QUERIES RUN: Q1 updater child | Q2 --nitrod | Q3 tasks | Q4 Radmin launcher | Q5 sideload drops |
Q6 shell child | Q7 COM keys | Q8 IP lookup | Q9 C2 IPs | Q10 C2 DNS | Q11 Nitro masquerade |
Q12 SMB egress | Q13 hashes | Q14 C2 ports | Q15 rsetup64 (+9 native hunts)
DATA SOURCES: Process creation, executable writes, scheduled task registration, registry writes, outbound
connections and DNS (CrowdStrike: ProcessRollup2, PeFileWritten, ScheduledTaskRegistered,
NetworkConnectIP4, DnsRequest. SentinelOne and Defender equivalents in section 5)
DO FIRST: Preserve cached down_html.ini and Notifycfg.ini; run Q1, Q3, Q5, Q9 over the longest retention.
FINDINGS: <pending analyst execution>
GAPS: Server-side tampering, named mutex, shared memory, in-memory decryption and traffic chaffing
are outside endpoint telemetry; the :wtfbbq stream rename needs native stream telemetry.
ACTIONS: Block egress to the campaign IPs and external SMB/WebDAV; disable WebClient; audit outgoing NTLM;
reset credentials for exposed users; promote Q1, Q3, Q5 to Custom IOA (Detect); prevent only the
two hashes seen in both sources.
OWNER: HuntPack
VERSION: v0.1 (Draft) - 2026-10-18Changelog
References
| Tier | Source | Used for | Accessed |
|---|---|---|---|
| 1 | Google Threat Intelligence Group / Mandiant: Investigating Ephemeral Supply Chain Tampering | Campaign, mechanics, IOCs, YARA, ATT&CK, guidance | 2026-10-18 |
| 1 | ManageEngine Threat Analysis Lab: AOMEI Backupper supply-chain compromise | Discovery telemetry, process lineage, hash corroboration, Sigma rules | 2026-10-18 |
| 2 | MITRE ATT&CK and mitigations | Technique and mitigation mapping | 2026-10-18 |
| 2 | Microsoft: Attack surface reduction rules reference | ASR rule used in PB3 | 2026-10-18 |