Armorsteal: SEO-Poisoned Chocolatey and Massgrave Installers

Python infostealer, remote access and clipboard hijacker delivered by typosquat lure sites. Hunt, detection and hardening pack.
Threat
Armorsteal SEO poisoning (Chocolatey / Massgrave lures)
Severity
HIGH
Type
Infostealer / RAT / clipboard hijacker
Access
Elevated PowerShell paste (user-assisted)
Version
v0.1 (2026-10-30)
Author
HuntPack
Confidence
Medium (single primary source)
01

Executive Summary

Armorsteal is a Python-based information stealer, remote access tool and clipboard hijacker that Zscaler Threat Hunting tracked through an SEO poisoning campaign first identified in September 2026 (report published 2026-10-22). Typosquatted sites impersonating the Chocolatey package manager (choclatey[.]org) and the Microsoft Activation Scripts project (massgravel[.]net) ranked in search results for install commands and showed a PowerShell one-liner for the victim to paste into an elevated prompt. The attacker objective is credential, session and cryptocurrency theft: browser data, Discord tokens, wallets, seed phrases, SSH keys and Steam data, plus keylogging, screenshots and wallet-address swapping.

The malware installs a standalone Python 3.11 runtime and Pyarmor-protected modules under C:\Windows\SystemHealth\Update\, persists with scheduled tasks SysCheck2 (user logon, highest privileges) and SysCheckX (system boot, SYSTEM, 45-second delay), and only runs C2 tasks whose RSA-2048 signature verifies against an embedded key. The highest-value defensive angle is behavioral and does not depend on rotating infrastructure: the fake SystemHealth\Update directory, the two task names, and browsers started with --remote-debugging-port=9222.

Defender priority: hunt the fake SystemHealth directory and the SysCheck2 / SysCheckX tasks first (Q4 to Q7), then block the lure and C2 domains. The campaign's two lure sites were reported down by early October 2026, so infrastructure hits are most likely historical: a clean domain sweep does not clear a host.

02

Source Review & Web Hunter Notes

TierSourceKey findingCarry forward
1 (primary)Zscaler Threat Hunting: SEO Poisoning Campaign Delivers New Armorsteal Malware (2026-10-22)Full chain, persistence, module and C2 detail, infrastructure timeline, detection guidance and all atomic indicatorsyes
3 (secondary)SecurityOnline: Armorsteal malware via Python SEO poisoningRestates the Zscaler report and its guidance. No independent analysis and no indicator valuespartial (corroborates guidance only)

Single-source indicators. Every atomic IOC comes from the Zscaler report alone, and the second source is derivative. Under the pack's two-source rule, no indicator ships at prevent or critical severity: every Falcon IOC row is detect, severity high or lower.

  • All IOC values were re-derived from the saved snapshot 01-zscaler-armorsteal.txt, not from search summaries.
  • Zscaler attributes both lures to one operation through shared malware components, a shared RSA-2048 task-signing key and shared C2 infrastructure. It names no threat actor and no known group, so this pack names none.
  • Search results also surfaced vendor detection-rule pages. They were not fetched or used, so nothing in this pack derives from them.
  • The lure sites were reported down on 2026-09-25 (Massgrave) and about 2026-10-01 (Chocolatey).
03

Hunt Brief & Attack Chain

Hypotheses (ordered by fidelity)

#HypothesisTechniqueTelemetry (plain terms, then platform)ConfQueries
H1Armorsteal runtime or modules execute from the fake SystemHealth\Update directoryT1036.005, T1059.006Process creation with image path and command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); file writes (CrowdStrike: PeFileWritten, NewExecutableWritten. Defender: DeviceFileEvents)highQ6, Q7
H2Tasks SysCheck2 and SysCheckX are registeredT1053.005Scheduled task registration (CrowdStrike: ScheduledTaskRegistered. Defender: DeviceEvents ScheduledTaskCreated) plus process command linehighQ4, Q5
H3Endpoint resolves or contacts a lure, C2 or payload hostT1608.006, T1071.001, T1105DNS lookup with requesting host (CrowdStrike: DnsRequest. Defender: DeviceEvents DnsQueryResponse); outbound connection (CrowdStrike: NetworkConnectIP4. Defender: DeviceNetworkEvents)highQ1, Q2, Q8
H4Known Armorsteal files appear by hashT1059.001, T1059.006Process and file events carrying SHA256 (CrowdStrike: SHA256HashData. Defender: prefer SHA1 where SHA256 is empty)highQ3
H5Victim ran a hidden-window PowerShell installer that downloaded a ZIPT1204.004, T1059.001, T1564.003Process creation with command linelowQ9
H6Browser launched with remote debugging port 9222 for cookie theftT1539, T1555.003Process creation with command line and parentmediumQ10
H7Defender exclusions added or present, especially for the Windows directoryT1562.001Process command line; registry value writes (CrowdStrike: RegGenericValueUpdate. Defender: DeviceRegistryEvents)mediumQ11

Attack chain

StepBehaviorTelemetryHunt angle
1 LureTyposquat sites rank for 'chocolatey install windows' and MAS/Massgrave searchesProxy and DNS logs, browser history (outside endpoint telemetry for search ranking)Q1, Q8
2 PasteVictim pastes PowerShell one-liner into an elevated promptProcess creation with command lineQ8, Q9
3 Installinstall.ps1 requests elevation, hides the window, downloads a ZIP to C:\Windows\SystemHealth\Update\; the Chocolatey variant also runs the real installerProcess creation, file writes, network connectionsQ2, Q3, Q7
4 PersistTasks SysCheck2 and SysCheckX launch the remote access moduleScheduled task registration, process creationQ4, Q5, Q6
5 EvadePublic IP check via ipinfo[.]io; cloud-provider orgs get a fake memory error. Pyarmor 9.1.7 trial obfuscationNetwork connection from a Python imageQ6 (indirect)
6 Task60-second C2 polling over HTTPS; RSA-2048 signed Python tasks run in memoryDNS and connections; in-memory execution not visibleQ1, Q2 (execution is a gap)
7 CollectKeylogging, clipboard monitoring and swapping, screenshots, browser/Discord/wallet/SSH/Steam theft, CDP cookie extraction on 9222Process creation; in-process APIs not visibleQ10 (rest is a gap)
04

Consolidated IOC Table

All values are from the Zscaler report (snapshot 01). Network indicators are defanged below and refanged in the import CSV in section 10. Confidence is medium for every row because the report is the only source.

TypeValueConfidenceActionContext
sha256f3846ec2a6ff0a27d9ef2b9bf7908743b81d295ef29344acfb96ff83fff994f9mediumdetectinstall.ps1 (MAS lure installer)
sha256fb1181160e9f10ce1eb5e5dc9bc23e1498a39110ee9ea361fd150454cb01b292mediumdetectinstall.ps1 (Chocolatey lure installer)
sha2563e3b62a9836697d978cbb620dc8af8f6eefdf8cb73fc30d16bb394d1c58f4c97mediumdetectUpdate.zip / Updatef.zip (remote access module)
sha256237ba698d7dc8936c43e0e78e7644002375e51e8baf9b3a7d4e18683f871ee31mediumdetectnig20084.zip (surveillance module)
sha256be0329d0795dd62ae587faf97e704bb36413e5c98ddd8fe575f5077db3c84e5fmediumdetectgetthem20084.zip (stealer module)
sha25604d2828155ed2a276cfd3c97d4f966d328391dd818e04e773212cd601e49fa74mediumdetectnig20083.zip (older stealer module per source)
sha25656f05c4fb1c25c04e0dde60e2e5e41ac164a6dc53688d0f9eb52a55f82106389mediumdetectgetthem20083.zip (older stealer module)
sha2560ea99fbbe29af6f797e3866ed5f822c7a5d3e9cdb990a8c382a715069f156499mediumdetectSysMon.py (remote access module)
sha2568fb7aaf2b9270b8b75f6844aaefd5242b4f926fb89cba10fff3d9b908a769788mediumdetectbasrq.py (surveillance module)
sha2565410c2f239c3649928383c4478dea182f90b21f5b258081fec07e9d297ae4b1cmediumdetectgot.py (stealer module)
domainchoclatey[.]orgmediumdetectFake Chocolatey lure (typosquat), registered 2026-09-01
domainmassgravel[.]netmediumdetectFake Massgrave/MAS lure (typosquat), registered 2026-08-30
domainworkerstats[.]netmediumdetectPrimary C2 (registered 2026-01-28), became unavailable early October 2026
domaindatamons[.]orgmediumdetectStandby C2, later active C2
domainsupertrap[.]netmediumdetectStandby C2
domainniceer[.]netmediumdetectStandby C2
domainpymons[.]ismediumdetectC2 domain used from early October 2026
ipv482[.]153[.]135[.]80mediumdetectPayload host for lure sites (mid-September onward, AKILE LTD, UK)
ipv4209[.]99[.]186[.]187mediumdetectPayload host for fake Chocolatey site (early September)
ipv4186[.]2[.]175[.]183mediumdetectC2 IP (workerstats.net after leaving Cloudflare)
ipv4186[.]2[.]163[.]185mediumdetectC2 IP
ipv4190[.]115[.]31[.]69mediumdetectC2 IP
05

Affected Surface & Telemetry Matrix

SurfaceTelemetry (plain terms, then platform)PriorityGap
Windows endpoints, elevated user sessionProcess creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)P1None for launch; in-memory task code is outside endpoint telemetry
Windows persistenceScheduled task registration (CrowdStrike: ScheduledTaskRegistered. Defender: DeviceEvents ScheduledTaskCreated)P1Task event field names vary by sensor version: validate in tenant
Network egressDNS lookup (CrowdStrike: DnsRequest. Defender: DeviceEvents DnsQueryResponse) and outbound connection (CrowdStrike: NetworkConnectIP4. Defender: DeviceNetworkEvents)P1URL paths such as /receive_infof are outside endpoint telemetry: use proxy or TLS-inspecting gateway logs
File stagingExecutable and archive writes (CrowdStrike: PeFileWritten, ZipFileWritten. Defender: DeviceFileEvents)P2Script and Python module hashes are often not recorded
Browser data, wallets, Discord, SSH, SteamProcess creation for CDP launch; file reads are not recordedP2Credential file access is outside endpoint telemetry
Keylogging, clipboard hijack, screenshotsNone directlyP3In-process API use is outside endpoint telemetry; detect via the host and network footprint
06

ATT&CK Mapping

TacticTechniqueBehaviorQuery / control
Resource DevelopmentT1608.006 SEO PoisoningTyposquat lure sites ranked for install-command searchesQ1, Q8, DNS filtering
ExecutionT1204.004 Malicious Copy and PasteVictim pastes the displayed PowerShell commandQ8, Q9, training
ExecutionT1059.001 PowerShell; T1059.006 Pythoninstall.ps1; bundled Python 3.11 running Pyarmor modulesQ6, Q9
Defense EvasionT1564.003 Hidden WindowInstaller hides the PowerShell consoleQ9
Defense EvasionT1036.005 Match Legitimate Name or LocationC:\Windows\SystemHealth\Update\ and task names SysCheck2 / SysCheckXQ4, Q5, Q6, Q7
Defense EvasionT1027 Obfuscated Files or InformationPyarmor 9.1.7 trial obfuscation of modulesQ3 (hashes)
Defense EvasionT1497 Virtualization/Sandbox Evasionipinfo[.]io organization check, fake memory error on cloud IPsGap (indirect via Q6)
Defense EvasionT1562.001 Impair DefensesAdvisory: review and remove unauthorized Defender exclusionsQ11
PersistenceT1053.005 Scheduled TaskSysCheck2 (logon) and SysCheckX (boot, SYSTEM, 45 s)Q4, Q5
Command and ControlT1071.001 Web Protocols; T1008 Fallback ChannelsHTTPS polling every 60 s, hardcoded domain list, standby C2sQ1, Q2
Command and ControlT1105 Ingress Tool TransferZIP modules fetched from C2 and payload hostsQ2, Q3, Q7
CollectionT1056.001 Keylogging; T1115 Clipboard Data; T1113 Screen CaptureSurveillance module (basrq.py)Gap (host and network footprint only)
ImpactT1565.002 Transmitted Data ManipulationClipboard wallet-address replacement for nine currenciesGap
Credential AccessT1555.003 Credentials from Web Browsers; T1555.005 Password Managers; T1539 Steal Web Session CookieStealer module (got.py), CDP on port 9222, chrome-decryptorQ10
Credential AccessT1528 Steal Application Access Token; T1552.004 Private KeysDiscord tokens from 22 clients; SSH keysGap
ExfiltrationT1041 Exfiltration Over C2 ChannelUploads to /infame/receive/, /black-watch/, /ws/, /pwd, /upload/stQ1, Q2; paths via proxy logs
07

Native Audit-Log Hunts

SourceWhat to look for
Windows Security log, event 4698Scheduled task created with TaskName \SysCheck2 or \SysCheckX (needs Audit Other Object Access Events)
Microsoft-Windows-TaskScheduler/Operational, events 106 and 140Task registered or updated with the same names
Microsoft-Windows-PowerShell/Operational, event 4104Script blocks referencing choclatey, massgravel, SystemHealth\Update or a hidden-window ZIP download
Sysmon (if deployed): events 1, 3, 11, 22Process, network, file creation and DNS query matching Q1, Q2, Q6, Q7
Proxy, secure web gateway or TLS-inspecting firewallRequests to the lure and C2 domains, and URL paths /receive_infof, /get-global, /black-watch/, /infame/receive/, /get-onetime-code/
Microsoft Defender (Get-MpPreference, Operational log event 5007)Exclusion list contents and changes, particularly C:\Windows
Browser and shell historyPasted install commands; users who searched for Chocolatey or MAS install commands in late 2026

Read-only triage checks (run in an elevated session on a suspect host)

Get-ScheduledTask -TaskName SysCheck2,SysCheckX -ErrorAction SilentlyContinue |
  Select-Object TaskName, State, @{n='Run';e={$_.Actions.Execute + ' ' + $_.Actions.Arguments}}
Test-Path 'C:\Windows\SystemHealth\Update'
Get-ChildItem 'C:\Windows\SystemHealth\Update' -Force -ErrorAction SilentlyContinue |
  Select-Object Name, Length, LastWriteTime
(Get-MpPreference).ExclusionPath
Get-Process | Where-Object { $_.Path -like 'C:\Windows\SystemHealth\*' } | Select-Object Id, ProcessName, Path
08

Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL

Show all queries in
Pick your tenant's cloud first: every "Open in Falcon" button below uses this selection.
Q1 · Armorsteal lure and C2 domain resolution
CONF HIGHFP LOWCOST LOW

Looks for: Any endpoint resolving a lure or C2 domain named in the Zscaler report (includes subdomains such as the community. host on the Chocolatey lure).

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: DNS resolved by a process the sensor did not observe is not captured.
  • PowerQuery assumes: event.dns.request holds the requested name; some schema versions name it dns.request, so run the readiness test first.
  • KQL – Defender XDR: Advanced Hunting keeps 30 days; the CQL tab covers the CQL window.
  • KQL – Defender XDR: DNS answered from a cache the sensor did not observe is not captured.
  • KQL – Defender XDR assumes: DnsQueryResponse is populated on the tenant and AdditionalFields carries the name under DnsQueryString or QueryName; the readiness test prints both so you can see which.
// HUNT: Armorsteal lure and C2 domain resolution
// MITRE: T1608.006, T1204.004, T1071.001, T1008
// CONF: high  FP: low  COST: low
// REQUIRES: DNS lookups with the requesting host (endpoint DNS telemetry)
// FALSE POSITIVES: researchers or sandboxes resolving the published domains
#event_simpleName=DnsRequest
| DomainName=/(^|\.)(choclatey\.org|massgravel\.net|workerstats\.net|datamons\.org|supertrap\.net|niceer\.net|pymons\.is)$/i
| groupBy([ComputerName, aid, DomainName], function=[count(as=lookups), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen)])
| table([ComputerName, aid, DomainName, lookups, firstSeen, lastSeen])
Q2 · Connections to Armorsteal payload hosts and C2 IPs
CONF MEDIUMFP LOWCOST LOW

Looks for: Outbound IPv4 connections to the two payload hosts and three C2 IPs. Hosting IPs can be reassigned, so treat a hit as a lead and confirm with Q1, Q6 and Q7.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Connections made before the sensor started, or from an unmonitored host, are not captured.
  • PowerQuery assumes: On an IP Connect event the initiating process is src.process.*.
  • KQL – Defender XDR: Advanced Hunting keeps 30 days; the CQL tab covers the CQL window.
  • KQL – Defender XDR assumes: DeviceNetworkEvents records outbound connection attempts to these addresses with RemoteIP populated.
// HUNT: Armorsteal payload host and C2 IP connections
// MITRE: T1105, T1071.001
// CONF: medium  FP: low  COST: low
// REQUIRES: outbound network connections with the initiating process
// FALSE POSITIVES: unrelated tenants later assigned the same hosting IPs
#event_simpleName=NetworkConnectIP4
| in(field=RemoteAddressIP4, values=["82.153.135.80","209.99.186.187","186.2.175.183","186.2.163.185","190.115.31.69"])
| table([@timestamp, ComputerName, aid, ContextBaseFileName, RemoteAddressIP4, RemotePort, LocalAddressIP4])
Q3 · Armorsteal file hash match
CONF HIGHFP LOWCOST LOW

Looks for: SHA256 of the lure installers, module archives and Python modules. Sensor hash coverage of scripts and archives varies, so a miss does not clear a host: rely on Q4 to Q7 for behavior.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analytic, telemetry-gapProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: An event that carries neither hash field is not matched, and the hash of a process that is not the created or written object is not checked.
  • PowerQuery assumes: tgt.process.image.sha256 and tgt.file.sha256 are populated on the tenant; hash population varies by event type and policy.
  • KQL – Defender XDR: Started processes with these hashes are not matched here; DeviceProcessEvents would be needed.
  • KQL – Defender XDR: SHA256 is often unpopulated, and the source offers no SHA1 to pivot on, so an empty result is not proof of absence.
  • KQL – Defender XDR: Advanced Hunting keeps 30 days; the CQL tab covers the CQL window.
  • KQL – Defender XDR assumes: SHA256 is populated in DeviceFileEvents on the tenant.
// HUNT: Armorsteal known file hashes
// MITRE: T1059.001, T1059.006, T1105
// CONF: high  FP: low  COST: low
// REQUIRES: file and process events carrying SHA256HashData
// FALSE POSITIVES: none expected for these exact hashes
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|PeFileWritten|NewExecutableWritten|ZipFileWritten)$/
| in(field=SHA256HashData, values=[
    "f3846ec2a6ff0a27d9ef2b9bf7908743b81d295ef29344acfb96ff83fff994f9",
    "fb1181160e9f10ce1eb5e5dc9bc23e1498a39110ee9ea361fd150454cb01b292",
    "3e3b62a9836697d978cbb620dc8af8f6eefdf8cb73fc30d16bb394d1c58f4c97",
    "237ba698d7dc8936c43e0e78e7644002375e51e8baf9b3a7d4e18683f871ee31",
    "be0329d0795dd62ae587faf97e704bb36413e5c98ddd8fe575f5077db3c84e5f",
    "04d2828155ed2a276cfd3c97d4f966d328391dd818e04e773212cd601e49fa74",
    "56f05c4fb1c25c04e0dde60e2e5e41ac164a6dc53688d0f9eb52a55f82106389",
    "0ea99fbbe29af6f797e3866ed5f822c7a5d3e9cdb990a8c382a715069f156499",
    "8fb7aaf2b9270b8b75f6844aaefd5242b4f926fb89cba10fff3d9b908a769788",
    "5410c2f239c3649928383c4478dea182f90b21f5b258081fec07e9d297ae4b1c"])
| table([@timestamp, ComputerName, aid, #event_simpleName, FileName, TargetFileName, SHA256HashData])
Q4 · SysCheck2 / SysCheckX scheduled task registered
CONF HIGHFP LOWCOST LOW

Looks for: Task registration events for the two Armorsteal persistence task names.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analytic, equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: This is a registry-side approximation, not the scheduled-task registration event itself.
  • PowerQuery: A task created while registry telemetry is suppressed is not captured.
  • PowerQuery assumes: Registering a task writes values under a Schedule TaskCache Tree key named after the task, and registry.keyPath carries that key path.
  • PowerQuery assumes: The match is case-insensitive by default, which reproduces the CQL /i intent.
  • KQL – Defender XDR: A task registered where the sensor does not raise a creation event is not captured.
  • KQL – Defender XDR assumes: ScheduledTaskCreated is populated on the tenant and AdditionalFields carries the task name under TaskName.
// HUNT: Armorsteal scheduled task persistence (registration event)
// MITRE: T1053.005, T1036.005
// CONF: high  FP: low  COST: low
// REQUIRES: scheduled task registration telemetry
// FALSE POSITIVES: an unrelated product that happens to use the same task names (none known)
// VALIDATE: confirm TaskName is populated on ScheduledTaskRegistered in your tenant; Q5 covers the same behavior
#event_simpleName=ScheduledTaskRegistered
| TaskName=/(^|\\)SysCheck(2|X)$/i
| table([@timestamp, ComputerName, aid, UserName, TaskName])
Q5 · schtasks or PowerShell creating SysCheck2 / SysCheckX
CONF HIGHFP LOWCOST LOW

Looks for: Process-level view of the same persistence: any shell or task tool whose command line names the two tasks. Covers sensors where the registration event is not populated.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A task created through the Task Scheduler API without a command line is not captured; the registry hunt covers that.
  • PowerQuery assumes: On a Process Creation event src.process.user is the user of the creating process.
  • PowerQuery assumes: Image names are matched with the .exe suffix as SentinelOne reports them on Windows.
  • KQL – Defender XDR: A task created through the Task Scheduler API without a command line is not captured.
  • KQL – Defender XDR assumes: FileName holds the bare image name with the .exe suffix on Windows sensors.
// HUNT: Armorsteal persistence via command line
// MITRE: T1053.005
// CONF: high  FP: low  COST: low
// REQUIRES: process creation with command line
// FALSE POSITIVES: administrators removing or querying the tasks during response
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^(schtasks|powershell|pwsh|cmd)(\.exe)?$/i
| CommandLine=/SysCheck(2|X)\b/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName])
Q6 · Execution from C:\Windows\SystemHealth\Update
CONF HIGHFP LOWCOST LOW

Looks for: Any process image or command line under the fake system directory Armorsteal uses for its Python 3.11 runtime, Pyarmor files and modules. Strongest behavioral signal in the pack.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A process the sensor did not observe starting is not captured.
  • PowerQuery assumes: tgt.process.image.path holds the full path of the created process image.
  • 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.
// HUNT: Armorsteal runtime executing from fake SystemHealth directory
// MITRE: T1036.005, T1059.006, T1053.005
// CONF: high  FP: low  COST: low
// REQUIRES: process creation with image path and command line
// FALSE POSITIVES: none known; the directory is not a standard Windows path
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| (ImageFileName=/\\Windows\\SystemHealth\\Update\\/i or CommandLine=/\\Windows\\SystemHealth\\Update\\/i)
| table([@timestamp, ComputerName, aid, UserName, FileName, ImageFileName, CommandLine, ParentBaseFileName, SHA256HashData])
Q7 · Files written under C:\Windows\SystemHealth\Update
CONF HIGHFP LOWCOST LOW

Looks for: Executables, DLLs and archives written into the staging directory: the Python runtime, Pyarmor runtime and module ZIPs landing on disk.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analyticProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Non-executable and non-archive writes are included, so a hit needs a glance at the file type.
  • PowerQuery assumes: tgt.file.path holds the full path of the written file.
  • KQL – Defender XDR: Non-executable and non-archive writes are included, so a hit needs a glance at the file type.
  • KQL – Defender XDR assumes: FolderPath holds the directory or full path of the written file, so the match omits the trailing backslash and also catches a directory that merely starts with the same name.
// HUNT: Armorsteal payload staging in fake SystemHealth directory
// MITRE: T1036.005, T1105
// CONF: high  FP: low  COST: low
// REQUIRES: file write telemetry for executables and archives
// FALSE POSITIVES: none known
#event_simpleName=/^(PeFileWritten|NewExecutableWritten|ZipFileWritten)$/
| TargetFileName=/\\Windows\\SystemHealth\\Update\\/i
| table([@timestamp, ComputerName, aid, ContextBaseFileName, TargetFileName, SHA256HashData])
Q8 · Lure domain or payload host named in a command line
CONF HIGHFP LOWCOST LOW

Looks for: Any process whose command line contains a lure domain, C2 domain or payload host: catches the pasted PowerShell one-liner and the install.ps1 download.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The two addresses match as plain substrings, so a longer address that ends in the same digits also matches.
  • PowerQuery assumes: On a Process Creation event src.process.user is the user of the creating process.
  • KQL – Defender XDR: A longer address that ends in the same digits also matches.
  • KQL – Defender XDR: Advanced Hunting keeps 30 days; the CQL tab covers the CQL window.
  • KQL – Defender XDR assumes: The two addresses match as plain substrings.
// HUNT: Armorsteal lure one-liner (domain or host in command line)
// MITRE: T1204.004, T1059.001, T1105
// CONF: high  FP: low  COST: low
// REQUIRES: process creation with command line
// FALSE POSITIVES: analysts testing the published domains
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/(choclatey\.org|massgravel\.net|workerstats\.net|datamons\.org|supertrap\.net|niceer\.net|pymons\.is|82\.153\.135\.80|209\.99\.186\.187)/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName])
Q9 · Hidden PowerShell downloading a ZIP or install.ps1
CONF LOWFP MEDIUMCOST LOW

Looks for: Behavioral approximation of the installer stage (hidden console, web download, ZIP or install.ps1). Zscaler does not publish the exact command line, so this is a hunt lead for lures that have rotated away from the known domains. FP: software deployment tools and legitimate installers use the same pattern.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: An installer stage that downloads through another binary, or builds the command line at run time, is not captured.
  • PowerQuery assumes: On a Process Creation event src.process.user is the user of the creating process.
  • KQL – Defender XDR: An installer stage that downloads through another binary, or builds the command line at run time, is not captured.
  • KQL – Defender XDR assumes: FileName holds the bare image name with the .exe suffix on Windows sensors.
// HUNT: Hidden PowerShell downloading a ZIP or install.ps1 (installer-stage approximation)
// MITRE: T1059.001, T1564.003, T1105
// CONF: low  FP: medium  COST: low
// REQUIRES: process creation with command line
// FALSE POSITIVES: software deployment agents, package managers, legitimate hidden-window installers
// TUNING: exclude known deployment parents and internal download hosts, for example append
// TUNING: | ParentBaseFileName!=/^(ccmexec|intunemanagementextension|ninjarmmagent|ltsvc)(\.exe)?$/i
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^(powershell|pwsh)(\.exe)?$/i
| CommandLine=/(-w(indowstyle)?\s+hidden|-win\s+h)/i
| CommandLine=/(invoke-webrequest|invoke-restmethod|downloadfile|downloadstring|start-bitstransfer|\biwr\b|\birm\b)/i
| CommandLine=/(\.zip|install\.ps1)/i
| table([@timestamp, ComputerName, aid, UserName, CommandLine, ParentBaseFileName])
Q10 · Browser started with remote debugging port 9222
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: The stealer extracts cookies over the Chrome DevTools Protocol on port 9222. Zscaler recommends reviewing browser processes started with this flag. FP: developers and QA automation launch browsers this way.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A browser launched under a renamed binary, or with a different debugging port, is not captured.
  • PowerQuery assumes: Image names are matched with the .exe suffix as SentinelOne reports them on Windows.
  • KQL – Defender XDR: A browser launched under a renamed binary, or with a different debugging port, is not captured.
  • KQL – Defender XDR assumes: FileName holds the bare image name with the .exe suffix on Windows sensors.
// HUNT: Browser launched with --remote-debugging-port=9222 (cookie theft via CDP)
// MITRE: T1539, T1555.003
// CONF: medium  FP: medium  COST: low
// REQUIRES: process creation with command line and parent
// FALSE POSITIVES: web developers, test automation, screenshot and PDF tooling
// TUNING: exclude automation parents and developer groups, for example append
// TUNING: | ParentBaseFileName!=/^(node|chromedriver|msedgedriver|code|pwsh)(\.exe)?$/i
// TUNING: prioritise hits whose parent is a python interpreter or runs from a non-standard path
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^(chrome|msedge|brave|opera|vivaldi)(\.exe)?$/i
| CommandLine=/--remote-debugging-port=9222/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName, ImageFileName])
Q11 · Microsoft Defender exclusion added
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: Defender exclusion changes by command line or registry write. Zscaler advises removing unauthorized exclusions, particularly for C:\Windows. The report does not say Armorsteal adds one, so this is a posture and triage query. FP: administrators and security tooling add exclusions legitimately.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analytic, equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: An exclusion set through Group Policy, Intune or the Windows Security UI without a matching command line or sensor-visible registry write is not captured.
  • PowerQuery assumes: On a Process Creation event src.process.user is the user of the creating process.
  • PowerQuery assumes: registry.keyPath carries the key path of the written value, including the Exclusions subkey.
  • KQL – Defender XDR: The registry-write half of the source, values written under the Defender Exclusions key, is not covered; DeviceRegistryEvents would be needed.
  • KQL – Defender XDR: An exclusion set through Group Policy, Intune or the Windows Security UI is not captured.
  • KQL – Defender XDR assumes: ProcessCommandLine carries the full PowerShell command line.
// HUNT: Microsoft Defender exclusion added (posture and triage)
// MITRE: T1562.001
// CONF: medium  FP: medium  COST: low
// REQUIRES: process creation with command line, registry value writes
// FALSE POSITIVES: administrators, software deployment, backup and database products adding vendor-documented exclusions
// TUNING: exclude approved exclusion paths and the change-management account, and escalate any exclusion for C:\Windows or a parent of it
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|RegGenericValueUpdate)$/
| (CommandLine=/(Add-MpPreference|Set-MpPreference).*-Exclusion(Path|Process|Extension)/i or RegObjectName=/Windows Defender\\Exclusions\\(Paths|Processes|Extensions)/i)
| table([@timestamp, ComputerName, aid, UserName, CommandLine, RegObjectName, RegValueName])
09

Detection Rules by Platform

CrowdStrike: Custom IOA

IOA namePromotesPatternExclusionsDeployment
Armorsteal: process from SystemHealth\UpdateQ6Process Creation, image filename .*\\Windows\\SystemHealth\\Update\\.* (or command line match)None needed; not a standard Windows pathDetect first. Prevent only after the same tenant shows zero legitimate hits for 14 days, and only on a path rule, never a hash
Armorsteal: SysCheck2 / SysCheckX taskQ4, Q5Process Creation, command line .*SysCheck(2|X)\b.* with image schtasks.exe, powershell.exe or pwsh.exeResponse runbooks that delete the tasksDetect. Pair with Q4 for the registration event
Defender exclusion for C:\WindowsQ11Process Creation, command line Add-MpPreference or Set-MpPreference with -ExclusionPath and a C:\Windows valueApproved change-management accountDetect with high severity review; do not block (administrators use this legitimately)

No IOA in this pack is set to prevent. The only indicators are single-sourced, and Q6 is a path heuristic that should be baselined first.

SentinelOne: custom detection rules

RuleSource queryRun asNotes
Armorsteal: process from SystemHealth\UpdateQ6, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
Armorsteal: SysCheck2 / SysCheckX taskQ4, Q5, PowerQuery tabHunt only until the gap is closedQ4 is only an approximation here. SentinelOne has no attested scheduled-task registration field here, so the same behaviour is hunted through the registry write that registering a task leaves under the Task Scheduler cache: a key path ending in the task name SysCheck2 or SysCheckX.
Defender exclusion for C:\WindowsQ11, PowerQuery tabScheduled custom detection ruleBind 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

RuleSource queryRun asNotes
Armorsteal: process from SystemHealth\UpdateQ6, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe 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
Armorsteal: SysCheck2 / SysCheckX taskQ4, Q5, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe 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
Defender exclusion for C:\WindowsQ11, KQL – Defender XDR tabHunt only until the gap is closedQ11 is only an approximation here. Partial pivot on the process half only: a process whose command line adds a Defender exclusion through Add-MpPreference or Set-MpPreference. The registry-write half lives in another table and one query can read only one table.

Q1, Q2, Q3, Q7, Q8, Q9 and Q10 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.

10

Machine-Readable IOC Appendix

Import posture. Every row is detect: the indicators are single-sourced (Zscaler only), so none ships at prevent or critical. The Chocolatey lure hash is the attacker's own install.ps1, not the legitimate Chocolatey installer; the legitimate installer is deliberately absent and must never be blocked.

Falcon IOC Management CSVbulk import, all detect
type,value,action,severity,expiration,description,tags
sha256,f3846ec2a6ff0a27d9ef2b9bf7908743b81d295ef29344acfb96ff83fff994f9,detect,high,2027-04-30,Armorsteal install.ps1 (MAS lure installer),campaign:Armorsteal
sha256,fb1181160e9f10ce1eb5e5dc9bc23e1498a39110ee9ea361fd150454cb01b292,detect,high,2027-04-30,Armorsteal install.ps1 (Chocolatey lure installer),campaign:Armorsteal
sha256,3e3b62a9836697d978cbb620dc8af8f6eefdf8cb73fc30d16bb394d1c58f4c97,detect,high,2027-04-30,Armorsteal Update.zip / Updatef.zip (remote access module),campaign:Armorsteal
sha256,237ba698d7dc8936c43e0e78e7644002375e51e8baf9b3a7d4e18683f871ee31,detect,high,2027-04-30,Armorsteal nig20084.zip (surveillance module),campaign:Armorsteal
sha256,be0329d0795dd62ae587faf97e704bb36413e5c98ddd8fe575f5077db3c84e5f,detect,high,2027-04-30,Armorsteal getthem20084.zip (stealer module),campaign:Armorsteal
sha256,04d2828155ed2a276cfd3c97d4f966d328391dd818e04e773212cd601e49fa74,detect,high,2027-04-30,Armorsteal nig20083.zip (older stealer module per source),campaign:Armorsteal
sha256,56f05c4fb1c25c04e0dde60e2e5e41ac164a6dc53688d0f9eb52a55f82106389,detect,high,2027-04-30,Armorsteal getthem20083.zip (older stealer module),campaign:Armorsteal
sha256,0ea99fbbe29af6f797e3866ed5f822c7a5d3e9cdb990a8c382a715069f156499,detect,high,2027-04-30,Armorsteal SysMon.py (remote access module),campaign:Armorsteal
sha256,8fb7aaf2b9270b8b75f6844aaefd5242b4f926fb89cba10fff3d9b908a769788,detect,high,2027-04-30,Armorsteal basrq.py (surveillance module),campaign:Armorsteal
sha256,5410c2f239c3649928383c4478dea182f90b21f5b258081fec07e9d297ae4b1c,detect,high,2027-04-30,Armorsteal got.py (stealer module),campaign:Armorsteal
domain,choclatey.org,detect,high,2027-04-30,Armorsteal Fake Chocolatey lure (typosquat); registered 2026-09-01,campaign:Armorsteal
domain,massgravel.net,detect,high,2027-04-30,Armorsteal Fake Massgrave/MAS lure (typosquat); registered 2026-08-30,campaign:Armorsteal
domain,workerstats.net,detect,high,2027-04-30,Armorsteal Primary C2 (registered 2026-01-28); became unavailable early October 2026,campaign:Armorsteal
domain,datamons.org,detect,high,2027-04-30,Armorsteal Standby C2; later active C2,campaign:Armorsteal
domain,supertrap.net,detect,high,2027-04-30,Armorsteal Standby C2,campaign:Armorsteal
domain,niceer.net,detect,high,2027-04-30,Armorsteal Standby C2,campaign:Armorsteal
domain,pymons.is,detect,high,2027-04-30,Armorsteal C2 domain used from early October 2026,campaign:Armorsteal
ipv4,82.153.135.80,detect,medium,2027-01-30,Armorsteal Payload host for lure sites (mid-September onward; AKILE LTD; UK),campaign:Armorsteal
ipv4,209.99.186.187,detect,medium,2027-01-30,Armorsteal Payload host for fake Chocolatey site (early September),campaign:Armorsteal
ipv4,186.2.175.183,detect,medium,2027-01-30,Armorsteal C2 IP (workerstats.net after leaving Cloudflare),campaign:Armorsteal
ipv4,186.2.163.185,detect,medium,2027-01-30,Armorsteal C2 IP,campaign:Armorsteal
ipv4,190.115.31.69,detect,medium,2027-01-30,Armorsteal C2 IP,campaign:Armorsteal
Microsoft Defender indicators CSVSettings, Endpoints, Indicators, Import: audit only
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert
FileSha256,f3846ec2a6ff0a27d9ef2b9bf7908743b81d295ef29344acfb96ff83fff994f9,Audit,Armorsteal install.ps1,Armorsteal install.ps1 (MAS lure installer),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,fb1181160e9f10ce1eb5e5dc9bc23e1498a39110ee9ea361fd150454cb01b292,Audit,Armorsteal install.ps1,Armorsteal install.ps1 (Chocolatey lure installer),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,3e3b62a9836697d978cbb620dc8af8f6eefdf8cb73fc30d16bb394d1c58f4c97,Audit,Armorsteal Update.zip / Updatef.zip,Armorsteal Update.zip / Updatef.zip (remote access module),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,237ba698d7dc8936c43e0e78e7644002375e51e8baf9b3a7d4e18683f871ee31,Audit,Armorsteal nig20084.zip,Armorsteal nig20084.zip (surveillance module),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,be0329d0795dd62ae587faf97e704bb36413e5c98ddd8fe575f5077db3c84e5f,Audit,Armorsteal getthem20084.zip,Armorsteal getthem20084.zip (stealer module),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,04d2828155ed2a276cfd3c97d4f966d328391dd818e04e773212cd601e49fa74,Audit,Armorsteal nig20083.zip,Armorsteal nig20083.zip (older stealer module per source),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,56f05c4fb1c25c04e0dde60e2e5e41ac164a6dc53688d0f9eb52a55f82106389,Audit,Armorsteal getthem20083.zip,Armorsteal getthem20083.zip (older stealer module),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,0ea99fbbe29af6f797e3866ed5f822c7a5d3e9cdb990a8c382a715069f156499,Audit,Armorsteal SysMon.py,Armorsteal SysMon.py (remote access module),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,8fb7aaf2b9270b8b75f6844aaefd5242b4f926fb89cba10fff3d9b908a769788,Audit,Armorsteal basrq.py,Armorsteal basrq.py (surveillance module),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,5410c2f239c3649928383c4478dea182f90b21f5b258081fec07e9d297ae4b1c,Audit,Armorsteal got.py,Armorsteal got.py (stealer module),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,choclatey.org,Audit,Armorsteal Fake Chocolatey lure,Armorsteal Fake Chocolatey lure (typosquat); registered 2026-09-01,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,massgravel.net,Audit,Armorsteal Fake Massgrave/MAS lure,Armorsteal Fake Massgrave/MAS lure (typosquat); registered 2026-08-30,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,workerstats.net,Audit,Armorsteal Primary C2,Armorsteal Primary C2 (registered 2026-01-28); became unavailable early October 2026,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,datamons.org,Audit,Armorsteal Standby C2; later active C2,Armorsteal Standby C2; later active C2,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,supertrap.net,Audit,Armorsteal Standby C2,Armorsteal Standby C2,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,niceer.net,Audit,Armorsteal Standby C2,Armorsteal Standby C2,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,pymons.is,Audit,Armorsteal C2 domain used from early October 2026,Armorsteal C2 domain used from early October 2026,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,82.153.135.80,Audit,Armorsteal Payload host for lure sites,Armorsteal Payload host for lure sites (mid-September onward; AKILE LTD; UK),2027-01-30T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,209.99.186.187,Audit,Armorsteal Payload host for fake Chocolatey site,Armorsteal Payload host for fake Chocolatey site (early September),2027-01-30T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,186.2.175.183,Audit,Armorsteal C2 IP,Armorsteal C2 IP (workerstats.net after leaving Cloudflare),2027-01-30T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,186.2.163.185,Audit,Armorsteal C2 IP,Armorsteal C2 IP,2027-01-30T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,190.115.31.69,Audit,Armorsteal C2 IP,Armorsteal C2 IP,2027-01-30T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True

Column names and allowed values are from Microsoft's documented parameter table. Check each expirationTime before importing: an indicator whose date has passed is already expired. Download the sample CSV from the Import page to confirm the column order before a bulk import. Each file takes at most 500 indicators. Learn describes the import per entity type, so if a mixed file is rejected, split it by indicatorType. An imported indicator does nothing unless the matching Advanced features are on (for example Custom network indicators and Allow or block file).

SentinelOne IOC importnot included

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.

Behavioral Signatureshunt and rule seeds
# Behavioral signatures (no atomic IOC needed)
Directory      C:\Windows\SystemHealth\Update\
Scheduled task SysCheck2  (trigger: user logon, run level highest)
Scheduled task SysCheckX  (trigger: system boot, SYSTEM, 45 s delay)
Module files   SysMon.py (remote access), basrq.py (surveillance), got.py (stealer)
Archives       Update.zip, Updatef.zip, nig20083.zip, nig20084.zip, getthem20083.zip, getthem20084.zip
Runtime        Python 3.11 standalone, Pyarmor 9.1.7 (trial) runtime files
State files    onetime_version.txt, global_version.txt (in the install directory)
Upload name    keylogs.gz (multipart field keylogs)
C2 paths       /receive_infof /get-onetime-code/ /get-global /downloadables/configf.json
               /global_keywords /keywords/ /black-watch/ /black-watch/screenshot/
               /infame/receive/zip /infame/receive/dt /ws/ /pwd /upload/st
Download gate  /C2/?t=<token>451 (surveillance), /C2/?t=<token>452 (stealer); ZIPs from /p/<32 hex>.zip with X-Verify header
UA string      python-requests/2.31.0 (weak, common)
Browser flag   --remote-debugging-port=9222 on chrome/msedge/brave/opera/vivaldi
Network check  ipinfo.io lookup from a Python image under SystemHealth
Named Malware / Toolingcontext and exclusions
# Named malware and tooling
Malware family     Armorsteal (Zscaler: PS.Dropper.Armorsteal, Python.PWS.Armorsteal)
Vendor MDR rules   WIN-CONHOST-HEADLESS-CMD, WIN-SCHTASKS-CREATE-SHELL, WIN-PYTHON-CHROMIUM-HEADLESS
Obfuscator         Pyarmor 9.1.7 trial runtime
Browser decryptor  public chrome-decryptor tool (Chrome App-Bound Encryption Decryption project), downloaded by the stealer
Lure brands        fake Chocolatey package manager site, fake Microsoft Activation Scripts (Massgrave) site
Real software      the genuine Chocolatey installer is run by the Chocolatey lure: do NOT block or prevent it
Posture Auditread-only checks
# Posture audit (run per endpoint, read-only)
Get-MpPreference | Select-Object -ExpandProperty ExclusionPath      # remove unauthorized entries, especially C:\Windows
Get-MpComputerStatus | Select-Object IsTamperProtected, RealTimeProtectionEnabled
Get-ScheduledTask -TaskName SysCheck2,SysCheckX -ErrorAction SilentlyContinue
Test-Path 'C:\Windows\SystemHealth\Update'
# Policy check: PowerShell script block logging enabled?
Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' -ErrorAction SilentlyContinue
11

Detection Validation Gates

GateCheck
Telemetry readyProcess creation with command line, DNS lookup, outbound connection, file writes and scheduled task registration flow from the Windows fleet (CrowdStrike: ProcessRollup2, DnsRequest, NetworkConnectIP4, PeFileWritten, ScheduledTaskRegistered. Defender: DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents, DeviceEvents)
Benign baselineRun Q6, Q7, Q4, Q5 over 14 days: expect zero hits. Run Q9, Q10 and Q11 and record the legitimate parents (deployment agents, developer automation, security tooling) to build the TUNING exclusions
Positive testsIn an isolated lab only: create a harmless folder C:\Windows\SystemHealth\Update containing a renamed copy of cmd.exe and run it (Q6, Q7); register a benign task named SysCheck2 and delete it (Q4, Q5); resolve a published domain from a sandbox (Q1). Never execute the malware or its archives
Field checkConfirm TaskName is populated on ScheduledTaskRegistered in your tenant before promoting Q4 (Q5 covers the same behavior by command line); the other fields are standard process, network and DNS fields
PromotionQ1, Q3, Q4, Q5, Q6, Q7, Q8 to scheduled alerts after one clean baseline week; Q2 as a lead generator; Q9, Q10, Q11 stay hunts until tuned
12

Hardening: Tiered & Deployable

Immediate (this week, low compatibility risk)
  • Block lure and C2 domains at DNS and the web gateway (MITRE M1021 Restrict Web-Based Content). Verify: resolve each domain from a test client and confirm a block page. Authority: vendor DNS/SWG policy; no formal benchmark covers per-campaign blocks (best-practice).
  • Turn on PowerShell script block and module logging (M1047 Audit; CIS Microsoft Windows 11 Enterprise Benchmark, Administrative Templates > Windows PowerShell). Verify: Get-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging; then CQL/native check for event 4104.
  • Review and clean Microsoft Defender exclusions; enable Tamper Protection (M1038 Execution Prevention, M1047; Microsoft Defender for Endpoint guidance). Remove any exclusion for C:\Windows. Verify: Get-MpPreference and Get-MpComputerStatus.
  • ASR rule: block execution of potentially obfuscated scripts (GUID 5BEB7EFE-FD9A-4556-801D-275E5FFC04CC; M1049, M1038). Pilot in audit mode first. Verify: Get-MpPreference | Select AttackSurfaceReductionRules_Ids, AttackSurfaceReductionRules_Actions.
Near term (1 to 4 weeks, pilot on a ring first)
  • Application control deny rule for %WINDIR%\SystemHealth\* and for Python interpreters outside approved paths (M1038; Microsoft AppLocker/WDAC guidance). Default AppLocker allows everything under the Windows folder, so an explicit deny is needed. Playbook 2.
  • Remove local administrator rights from standard users so a pasted command cannot self-elevate (M1026 Privileged Account Management, M1018; CIS Controls v8 safeguard 5.4). Verify: net localgroup administrators across a ring.
  • Disable browser remote debugging by policy (M1054 Software Configuration): Chrome and Edge RemoteDebuggingAllowed set to 0. Confirm the exact policy name in your ADMX version. Playbook 3.
  • Audit scheduled task creation (M1047): enable Audit Other Object Access Events and forward event 4698. Playbook 1.
Strategic (1 to 3 months)
  • Give users a sanctioned software path (an internal package repository or endpoint management catalog) so nobody needs a pasted install command from a search result (M1017 User Training, M1021). Add awareness content on verifying download domains. Best-practice, no formal benchmark.
  • Move cryptocurrency and credential custody off the browser: hardware-backed wallets, no browser password storage, passkeys or hardware-key MFA for chat and exchange accounts (M1032 Multi-factor Authentication). Best-practice, no formal benchmark.
  • Constrained Language Mode with WDAC for standard users to stop interactive download-and-execute one-liners (M1038). Needs a full compatibility review.
13

Deployable Playbooks

Playbook 1: PowerShell logging, scheduled-task auditing and ASR audit mode

Prerequisites: Local administrator or a GPO/management console; Windows 10/11 or Server 2016+ with Microsoft Defender Antivirus active.

Reboot required: No. Policies apply at next policy refresh.

Rollback: Delete the three registry values and run auditpol /set /subcategory:"Other Object Access Events" /success:disable /failure:disable; run Set-MpPreference -AttackSurfaceReductionRules_Ids 5BEB7EFE-FD9A-4556-801D-275E5FFC04CC -AttackSurfaceReductionRules_Actions Disabled.

# 1. Script block + module logging
$p='HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell'
New-Item "$p\ScriptBlockLogging" -Force | Out-Null
Set-ItemProperty "$p\ScriptBlockLogging" EnableScriptBlockLogging 1 -Type DWord
New-Item "$p\ModuleLogging" -Force | Out-Null
Set-ItemProperty "$p\ModuleLogging" EnableModuleLogging 1 -Type DWord
# 2. Audit scheduled task creation (event 4698)
auditpol /set /subcategory:"Other Object Access Events" /success:enable /failure:enable
# 3. ASR: obfuscated scripts, AUDIT mode first (switch to Enabled after review)
Add-MpPreference -AttackSurfaceReductionRules_Ids 5BEB7EFE-FD9A-4556-801D-275E5FFC04CC -AttackSurfaceReductionRules_Actions AuditMode

Playbook 2: AppLocker audit-mode deny rule for the fake SystemHealth directory

Prerequisites: AppLocker (Enterprise/Education SKUs) with the Application Identity service running; an existing baseline policy. Keep the new rule in audit mode for two weeks.

Reboot required: No. The Application Identity service must be running.

Rollback: Remove the rule from the policy and re-import, or Set-AppLockerPolicy with the previously exported XML (export first with Get-AppLockerPolicy -Local -Xml).

# Export current policy for rollback
Get-AppLockerPolicy -Local -Xml | Out-File "$env:TEMP\applocker-before.xml"
# Rule fragment to merge into the Exe rule collection (deny, audit via EnforcementMode on the collection)
<FilePathRule Id="b6f1d0aa-0000-4000-8000-00000000a501" Name="Deny Armorsteal SystemHealth directory"
  Description="Fake system directory used by Armorsteal" UserOrGroupSid="S-1-1-0" Action="Deny">
  <Conditions><FilePathCondition Path="%WINDIR%\SystemHealth\*" /></Conditions>
</FilePathRule>

Playbook 3: Disable browser remote debugging by policy

Prerequisites: Chrome and Microsoft Edge managed by policy; confirm the RemoteDebuggingAllowed policy exists in your installed ADMX version before deploying. Developers who need the port need a scoped exception.

Reboot required: No. Browsers read policy at start.

Rollback: Delete the RemoteDebuggingAllowed value under each browser policy key and restart the browser.

foreach ($k in 'HKLM:\SOFTWARE\Policies\Google\Chrome','HKLM:\SOFTWARE\Policies\Microsoft\Edge') {
  New-Item $k -Force | Out-Null
  Set-ItemProperty $k RemoteDebuggingAllowed 0 -Type DWord
}
# Verify
Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Google\Chrome','HKLM:\SOFTWARE\Policies\Microsoft\Edge' | Select-Object PSPath, RemoteDebuggingAllowed

Playbook 4: Remove an unauthorized Defender exclusion for C:\Windows and confirm Tamper Protection

Prerequisites: Administrator; approval from the security owner that the exclusion is not required. Record the current exclusion list first.

Reboot required: No.

Rollback: Re-add the exclusion with Add-MpPreference -ExclusionPath <path> using the list saved in step 1 (only if the owner approves).

$before = (Get-MpPreference).ExclusionPath
$before | Out-File "$env:TEMP\defender-exclusions-before.txt"
foreach ($x in $before) { if ($x -match '^(?i)C:\\Windows\\?\*?$') { Remove-MpPreference -ExclusionPath $x } }
(Get-MpPreference).ExclusionPath
(Get-MpComputerStatus).IsTamperProtected
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateNetwork-isolate the host through EDR host containment. Block the lure, C2 and payload domains and IPs at DNS and the gateway. Do not power off before memory capture if policy allowsSOC / IRContainment timestamp, block change ticket
Isolate: by platformCrowdStrike: 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 / IRContainment timestamp, block change ticket
ScopeRun Q1 to Q8 fleet-wide for the last 30 days; pull proxy logs for the C2 URL paths; list every user who visited the lure domainsSOC / IRHost list, user list
Credential reset (assume theft)From a clean device: reset passwords and revoke sessions and tokens for browser-saved accounts, Discord, email, SSH keys, password-manager vault and exchange accounts. Move cryptocurrency to new wallets with new seeds. Invalidate any API keys stored on the hostIAM / affected userReset log, revoked session list
EradicatePreferred: reimage the host. If not: stop and delete tasks SysCheck2 and SysCheckX, kill Python processes under C:\Windows\SystemHealth\Update, delete the directory, remove unauthorized Defender exclusions, then rescan and re-run Q4 to Q7IR / endpointBefore/after listings, hashes of removed files
RecoverReturn the host to service after a clean 7-day Q1 to Q11 baseline; deploy hardening playbooks 1 to 4; brief the user on verifying install sourcesEndpoint / IRClean query results, sign-off
PreserveKeep the install directory, scheduled task XML, PowerShell history and browser history for analysis before cleanupIREvidence archive with hashes
15

Detection Coverage Map

TechniqueBehaviorCQLIOACrowdStrike coverageSentinelOneDefender XDR
T1608.006 / T1071.001Lure and C2 domain resolutionQ1noneGoodGood, with assumptionsGood, with assumptions
T1105 / T1071.001Payload host and C2 IP connectionsQ2nonePartialPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1059.001 / T1059.006Known file hashesQ3nonePartialPartial: Q3 approximatePartial: Q3 telemetry gap
T1053.005SysCheck2 / SysCheckX persistenceQ4, Q5IOA 2GoodPartial: Q4 approximateGood, with assumptions
T1036.005Execution and staging in fake SystemHealth directoryQ6, Q7IOA 1GoodPartial: Q7 approximatePartial: Q7 approximate
T1204.004Pasted one-liner naming lure hostsQ8noneGoodGood, with assumptionsGood, with assumptions
T1564.003 / T1105Hidden-window PowerShell ZIP downloadQ9nonePartialPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1539 / T1555.003Browser CDP launch on 9222Q10nonePartialPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1562.001Defender exclusionsQ11IOA 3PartialPartial: same limit as CrowdStrikePartial: Q11 approximate
T1056.001 / T1115 / T1113 / T1565.002Keylogging, clipboard hijack, screenshotsnonenoneGAPGAPGAP
T1497Cloud-IP sandbox evasion checknonenoneGAPGAPGAP
T1573 / in-memory tasksRSA-signed Python tasks executed in memorynonenoneGAPGAPGAP

Known gaps: in-process surveillance and in-memory task execution are outside endpoint telemetry, and URL path indicators need proxy or gateway logs. Validation gates (section 11): telemetry ready, 14-day baseline, lab positive tests, then promotion.

16

Hunt Summary Ticket

TITLE: Armorsteal SEO-poisoning stealer hunt (fake Chocolatey / Massgrave installers)
SEVERITY: High (credential and cryptocurrency theft; indicators single-sourced, none at prevent)
SCOPE: Windows endpoints where users install software from search results; web egress; Defender exclusion posture
HYPOTHESIS: A user pasted a lure PowerShell command and Armorsteal now runs from C:\Windows\SystemHealth\Update with tasks SysCheck2 / SysCheckX
QUERIES: Q1-Q11 (CQL). Highest fidelity: Q4, Q5, Q6, Q7
DO FIRST: run Q6 and Q4 fleet-wide for 30 days; sweep Q1 and Q8 for lure/C2 domains; check Defender exclusions for C:\Windows
FINDINGS: (fill in) host / user / first seen / tasks present / modules present
GAPS: keylogging, clipboard hijack and in-memory tasks are outside endpoint telemetry; C2 URL paths need proxy or gateway logs; DNS and connection hits may be historical (lure sites down by early October 2026)
ACTIONS: contain host, reset credentials and move crypto assets from a clean device, remove tasks and directory or reimage, deploy playbooks 1-4
OWNER: HuntPack
VERSION: v0.1 2026-10-30
17

Changelog

v0.12026-10-30Initial draft. Built from the Zscaler report (published 2026-10-22) with SecurityOnline as corroboration. 11 CQL queries, 10 hashes, 7 domains, 5 IPs, all at detect. SentinelOne PowerQuery and Microsoft Defender KQL tabs added to Q1-Q11: SIEM Query Rosetta release 0.1.0 (public-beta; static-reviewed translations).
18

References

TierSourceUsed forAccess date
1Zscaler: SEO Poisoning Campaign Delivers New Armorsteal MalwareChain, persistence, C2, infrastructure timeline, all atomic indicators, detection guidance2026-10-30
3SecurityOnline: Armorsteal malware via Python SEO poisoningCorroboration of guidance only (no IOC values)2026-10-30

Source snapshots are saved next to this file in Armorsteal-SEOPoisoning-Hunt-sources/. MITRE ATT&CK technique and mitigation identifiers are cited by ID only.