NetamLoader

A .NET assembly-loading framework Kaspersky GReAT found on one host running a customized, government-only build of Windows 10 in an Asian country. It rides a signed Patch My PC wrapper through .NET AppDomainManager injection, runs mostly in memory, patches AMSI, erases its traces and talks to a Cloudflare-hosted C2. Kaspersky links it to no known actor and did not observe the delivery.
Threat
NetamLoader (Kaspersky: HEUR:Trojan.MSIL.NetamLoader.gen)
Severity
HIGH
Type
Loader framework · AppDomainManager injection · possible supply chain
Access
Unknown (delivery of the CAB not observed)
Confidence
Medium (one vendor technical report, one victim)
Version
v0.1 · 2026-11-02
Author
HuntPack
01

Executive Summary

NetamLoader is a layered .NET loader that Kaspersky GReAT disclosed on 2026-10-29 (Securelist and a press release, presented at Security Analyst Summit 2026). It was found on a single host running a customized Windows 10 build used only by government organizations in an Asian country. Kaspersky has not attributed it to any known actor and did not see how the package arrived, but the location and contents point to a possible software supply chain attack.

The chain starts with a CAB archive in the Windows update download area. Its seven files unpack into C:\Windows\SoftwareDistribution\Download\Install. When a legitimate, signed PatchMyPC-ScriptRunner.exe starts, a planted PatchMyPC-ScriptRunner.exe.config makes the .NET runtime load a Microsoft-signed assembly, which reads fabric.config, which in turn makes Resources.resx deserialize nested payloads. Layers of encoding, compression and encryption lead to an AMSI patch, a cleanup stage that deletes the dropped files, CLR usage logs, Prefetch files and selected update-log lines, and finally a plugin that runs inside dllhost.exe, started through a remote COM object so that it leaves the original process tree. After a random 20 to 40 minute pause it polls an HTTPS C2 on openaigpt4v.com, which sits on Cloudflare's CDN.

Defensive angle. The files are legitimate or inert, so hashes alone will not carry this hunt. The durable signals are where and how things run: the Patch My PC wrapper and its companion DLLs under SoftwareDistribution\Download\Install, a dllhost.exe that makes outbound connections, DNS for the C2 domain, and gaps in locally kept logs. Kaspersky says Patch My PC and its distribution systems were not shown to be compromised, so none of its files or hashes ship as a block here.

Defender priority: run Q1, Q2 and Q6 over 30 to 90 days now, then check that update and CLR logs are forwarded off the endpoint. Everything in this pack ships at detect. The domain and file hashes come from one vendor report, and the stock Patch My PC and Windows binaries must never be blocked.

02

Source Review & Web Hunter Notes

TierSourceKey findingCarry forwardSnapshot
1Kaspersky GReAT, Securelist: Unknown APT uses NetamLoader to attack a Win10 OS customized for governments in Asia (2026-10-29)Full chain, config values, C2 domain and address, hashes, paths, detection names, victim count, attribution statementyes01-kaspersky-securelist.txt
1Kaspersky press release, GReAT uncovers attack targeting government-only Windows system in Asia (2026-10-29)Patch My PC and its distribution not shown to be compromised; possible supply chain; delays; Kaspersky mitigation advice. No IOCsyes02-kaspersky-press-release.txt
2The Hacker News, NetamLoader Malware Targets Government Windows 10 Systems in Asia via .NET Injection (2026-11-01)Secondary summary of the Kaspersky report; lists PatchMyPC-Enum.dll and PatchMyPC-Models.dll. Its guess about the country is its own and is not carried forwardpartial03-thn.txt
1Patch My PC, What Is Patch My PC ScriptRunner? (vendor knowledge base, undated)What the legitimate wrapper does, that it is signed, and its normal staging folders (CCMCache, IMECache); advises ASR exclusions by binary namepartial04-patchmypc-scriptrunner-kb.txt
2MITRE ATT&CK T1574.014 AppDomainManagerTechnique definition; M1022 mitigationpartial05-mitre-t1574-014.txt
2MITRE ATT&CK T1685 Disable or Modify Tools (T1562.001 now redirects here in v20)AMSI patching context; M1047, M1038, M1022, M1024, M1054, M1018partial06-mitre-t1685.txt
2MITRE ATT&CK T1070 Indicator RemovalLog and Prefetch deletion; M1029, M1022, M1041partial07-mitre-t1070.txt
2MITRE ATT&CK T1559.001 Component Object ModelCOM abuse; M1048, M1026 (AppID registry permissions)partial08-mitre-t1559-001.txt
2MITRE ATT&CK T1497.003 Time Based ChecksSandbox delay techniquepartial09-mitre-t1497-003.txt
2MITRE ATT&CK T1620 Reflective Code LoadingIn-memory loading techniquepartial10-mitre-t1620.txt

Decisions and judgment calls

  • One technical origin. The Hacker News and the press release restate the Securelist report, so they are not independent confirmation. Nothing in this pack is therefore eligible for prevent or critical severity.
  • Cloudflare is hosting, not fronting. Securelist says the C2 address belongs to Cloudflare's CDN and the domain is registered with Cloudflare. It does not describe domain fronting, so this pack does not claim it.
  • Country not named by Kaspersky. Kaspersky says only "an Asian country". The Hacker News speculates about which one. That is not carried into this pack.
  • Delivery and persistence unknown. Kaspersky did not observe how the CAB arrived and describes no persistence mechanism. The plugin ends itself after roughly 28 hours (config value wi of 100801 seconds), so a restart would need a fresh delivery.
  • CAB name length. Securelist prints the CAB name as 40 hex characters. The Hacker News prints 39. The Securelist value is used.
  • Hash reuse. The report lists MD5 values only. Seven of the eleven belong to in-memory payloads that normally never touch disk, so only the four on-disk items go into the import CSV.
  • Fetched content. No fetched page contained text directed at the analyst or the pipeline.
03

Hunt Brief & Attack Chain

Hypotheses

#HypothesisMITRETelemetryConfidenceQueries
H1The Patch My PC wrapper ran from the Windows update download folder, which is not where the product stages its files.T1574.014, T1195Process creation with command line and image path (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)HIGHQ1
H2Wrapper and companion DLLs were extracted into SoftwareDistribution\Download\Install.T1574.014, T1195Executable or DLL written with target path (CrowdStrike: PeFileWritten, NewExecutableWritten. SentinelOne: File Creation. Defender: DeviceFileEvents)HIGHQ2
H3Something else executes from the update download folder.T1574.014, T1195Process creation with image path (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)MEDIUMQ3
H4A dllhost.exe was started for the remote COM object or the DllSurrogate AppID the report names.T1559.001Process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)MEDIUMQ4
H5dllhost.exe polled an external address over HTTPS.T1071.001, T1497.003Outbound network connection with initiating process (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents)MEDIUMQ5
H6A host resolved the C2 domain.T1071.001DNS lookup with requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, DNS action type)HIGHQ6
H7A DllSurrogate value was written under a COM AppID key.T1559.001, T1112Registry value set (CrowdStrike: RegGenericValueUpdate. SentinelOne: Registry Value Modified. Defender: DeviceRegistryEvents)LOWQ7

Attack chain (as reported by Kaspersky)

StepBehaviorTelemetryHunt angle
1Delivery of a CAB into SoftwareDistribution\Download. Not observed by Kaspersky; possible supply chainOutside endpoint telemetry for the delivery itself. Update-channel and software-distribution logsNative N1, N4; hardening tier 3
2Seven files extracted to Download\Install; PatchMyPC-ScriptRunner.exe started with -updatepackage -applicationid=Process creation and executable write (see H1, H2)Q1, Q2, Q3
3Runtime reads PatchMyPC-ScriptRunner.exe.config, loads Microsoft.ApplicationServer.Caching.DataCacheFactoryConfiguration from Microsoft.HostIntegration.Connectors, which reads fabric.config, which names Resources.resxInside the process. Config and resx are not PE files, so no executable-write eventGap for the file writes: native N1, N2. Process side: Q1
4Resources.resx deserializes a TypeConfuseDelegate chain into XamlReader.Parse; XAML-1 decrypts XAML-2 using a key derived from the host command lineIn memoryGap: in-process only
5XAML-2-1 patches AMSI (CAmsiAntimalware::Scan pointer redirected to a patched Console.Clear stub)In memoryGap: in-process only
6.NET 1 Loader deletes dropped files, CLR usage log, Prefetch entries, CmitUpdateAgent log lines, and registry exception-state keysFile delete and log tamper. No verified delete event used hereGap in queries; native N3
7Remote COM object created over RPC; XAML-3 loads in dllhost.exe; COMPlus_ETWEnabled set to 0 firstProcess creation of dllhost.exe; DllSurrogate registry valueQ4, Q7
8.NET 2 Plugin waits 20 to 40 minutes, then polls the C2 over HTTPS; 5 to 6 minute loop on success, 22 to 28 hours on failure; exits after about 28 hoursOutbound connection and DNS from dllhost.exeQ5, Q6, native N4
04

Consolidated IOC Table

TypeValueConfidenceActionContext
domainopenaigpt4v.comhighdetectC2 domain in the plugin configuration (URL path /model_v4/ plus an instance GUID). Cloudflare-hosted. Securelist, repeated by THN. Single origin.
ipv4104.18.14.219lowenrich onlyAddress Securelist lists for the C2 domain. It sits in Cloudflare's CDN range and is shared with other sites, so it is not imported and not blocked.
md5f2458ed133f8d0ca19e22c10f577e98emediumdetectCAB archive saved as e4537a1762fcafd2fd96bfc825bad34f9db9a24d under SoftwareDistribution\Download. Single source.
md5e1dc6f1ed193594f2284be22ae08dfa4mediumdetectResources.resx, the only file in the CAB that carries malicious content
md567e3617f8709d34f806f8e02badba8a5mediumdetectfabric.config, crafted to name Resources.resx. Not a stock file
md5514ac0d73b8a1b73488ef5e2e55a75c3mediumdetectPatchMyPC-ScriptRunner.exe.config with appDomainManager settings. Not the vendor's config
md5514117d536e347560d049cfe0c609632 9000820d8153c24cd60d53001d3a97b6 b3c4a635179680edafb5d006cf0cffc6 4f28622c5e60c01319bf48cf742fe1a5 c86d53392a87dd3f3e62d62c9b92a9bd ab81564bcb8ca7e710c66832de747556 cfb8b281bdf5d9390e64b10c312fb2c1mediumhuntIn-memory payloads (XAML-1, XAML-2, XAML-2-1, XAML-2-2, XAML-3, .NET 1 Loader, .NET 2 Plugin, in that order). Not expected on disk; kept for memory or sandbox scanning. Not imported
filenamePatchMyPC-ScriptRunner.exe.confighighhuntPlanted config beside the wrapper. Look for an appDomainManagerAssembly element in it
filenamefabric.config Resources.resxhighhuntDropped beside the wrapper; deleted by the loader
filenamePatchMyPC-ScriptRunner.exelowhunt (legitimate)Stock signed product file. Context only: the folder it runs from is the signal. Never prevent
filenameMicrosoft.HostIntegration.Connectors.dlllowhunt (legitimate)Microsoft-signed assembly abused as the AppDomainManager host. Never prevent
pathC:\Windows\SoftwareDistribution\Download\InstallhighhuntExtraction folder for the CAB contents
path%LOCALAPPDATA%\Microsoft\CLR_v4.0\UsageLogs\PatchMyPC-ScriptRunner.exe.log %WINDIR%\Prefetch\PATCHMYPC-SCRIPTRUNNER.EXE-*.pfmediumhuntDeleted by the loader. Absence after a recorded execution is the signal
path%WINDIR%\Logs\CmitUpdateAgent\20????\20??????mediumhuntLines containing GetExtendedUpdateInfoHandler are filtered out. Path belongs to the customized build
registryHKLM\SOFTWARE\Classes\AppID\{ff259bcd-2f0a-44de-b34a-0092fec1d8a2} DllSurrogatelowhuntAppID derived from the C2 URL, so specific to this sample. Other nine exception-state GUIDs exist per C2
clsidab8902b4-09ca-4bb6-b78d-a8f59079a8d5mediumhuntRemote COM object created over RPC to start dllhost.exe
url path/model_v4/94aafd58-c00c-402a-aae6-858b4486a1a4mediumhuntRequest path shape with instance GUID. Search proxy logs for /model_v4/ on the C2 host. Query string carries b= and c= parameters
stringGetExtendedUpdateInfoHandlermediumhuntSubstring the loader strips from update logs. Missing lines in logs that normally have them

Nothing here ships at prevent. Every indicator traces to one vendor report, the hashes are of inert or in-memory content, and the two PatchMyPC and Microsoft filenames are legitimate signed files. Kaspersky found a single victim, so expect low coverage and perishable infrastructure.

05

Affected Surface & Telemetry Matrix

SurfaceTelemetry (plain terms, then platforms)PriorityGap risk
Windows 10 endpoints, especially customized or government builds with a non-standard update agentProcess creation with command line, path and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)CRITICALLow where the sensor runs. Sensor support on a customized OS build must be confirmed
Update download folder writesExecutable or DLL written with target path (CrowdStrike: PeFileWritten, NewExecutableWritten. SentinelOne: File Creation. Defender: DeviceFileEvents)HIGHMedium: .config and .resx files are not PE files and have no verified write event used here
COM surrogate (dllhost.exe) network behaviorOutbound network connection with initiating process (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents)HIGHMedium: shared CDN addresses make the destination weak on its own
DNSDNS lookup with requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, DNS action type)HIGHMedium: encrypted DNS on the endpoint blinds it; resolver logs are the second source
COM AppID registryRegistry value set (CrowdStrike: RegGenericValueUpdate. SentinelOne: Registry Value Modified. Defender: DeviceRegistryEvents)MEDIUMHigh: sensor coverage of Classes\AppID is unconfirmed
In-process activity (AMSI patch, XAML loading, ETW disable)No endpoint event for theseHIGHHigh: outside endpoint telemetry; covered only by behavior around them
Local logs the loader edits (CLR usage logs, Prefetch, update agent logs)File and log forwarded off the endpoint (SIEM or log pipeline)HIGHHigh unless logs are forwarded before the loader runs
Network egressProxy, firewall and resolver logs for the C2 host and a /model_v4/ URL pathMEDIUMDepends on TLS inspection and log retention
06

ATT&CK Mapping

TacticTechniqueObserved behaviorQuery or control
Initial AccessT1195 Supply Chain Compromise (possible, per Kaspersky)CAB placed in the Windows update download folder; Patch My PC components used; delivery not observedQ1, Q2, Q3; hardening tier 3. Gap for delivery
Defense EvasionT1574.014 Hijack Execution Flow: AppDomainManagerPlanted PatchMyPC-ScriptRunner.exe.config names a Microsoft-signed manager assemblyQ1, Q2, Q3; M1022; N1
ExecutionT1559.001 Component Object ModelRemote COM object created over RPC to start dllhost.exe and load XAML-3Q4, Q7; M1048, M1026
Defense EvasionT1620 Reflective Code LoadingPayloads decoded and loaded as XAML and .NET assemblies in memoryGap: in-process only
Defense EvasionT1562.001 (now T1685) Disable or Modify ToolsAMSI Scan pointer redirected; COMPlus_ETWEnabled set to 0Gap: in-process only; M1038, M1047
Defense EvasionT1070 Indicator RemovalDeletes dropped files, CLR usage logs, Prefetch files and update-log linesN3; M1029
Defense EvasionT1497.003 Time Based ChecksRandom 20 to 40 minute pause before first C2 contactQ5 (long-window view); sandbox runs must exceed 40 minutes
Command and ControlT1071.001 Web ProtocolsHTTPS polling of openaigpt4v.com from dllhost.exeQ5, Q6, N4
07

Native Audit-Log Hunts

IDSourceHuntWhy
N1Endpoint file inventory (PowerShell, read-only)List everything under C:\Windows\SoftwareDistribution\Download with extension .exe, .dll, .config or .resx; hash with MD5 and compare to section 10. Search for *.exe.config files that contain appDomainManagerAssembly. Command in playbook P1.Closes the gap on non-PE files. The loader deletes its own files, so a clean result after a hit window proves little.
N2Sysmon (where deployed)Event ID 1 where Image ends in PatchMyPC-ScriptRunner.exe and the path contains SoftwareDistribution; Event ID 11 for the three non-PE file names in Download\Install; Event ID 12 and 13 for DllSurrogate under Classes\AppID; Event ID 22 for the C2 domain.Mirrors Q1, Q2, Q7 and Q6 and covers file writes the sensor query cannot see.
N3Windows logs and file system(a) For any recorded ScriptRunner execution, check whether the matching Prefetch file and the CLR usage log exist; their absence fits the cleanup stage. (b) On the customized build, compare CmitUpdateAgent logs with the central copy for missing lines containing GetExtendedUpdateInfoHandler. (c) Look for ETW or AMSI telemetry from the host that stops after a ScriptRunner start.The only direct evidence of anti-forensics. Needs off-endpoint copies to compare against (hardening tier 1).
N4Proxy, firewall, resolverRequests to openaigpt4v.com and any URL path beginning /model_v4/ with a GUID segment. With TLS inspection, look for query parameters b and c. Also look for a host that connects every 5 to 6 minutes after a 20 to 40 minute delay, or once every 22 to 28 hours.Network view independent of the endpoint sensor. The C2 address is shared, so key on the host and path, not the address.
N5Update channel and software distribution logsReview how any package reached SoftwareDistribution\Download around the first sighting: which service wrote it, which management console or share served it, and its signature.Delivery is unknown. Kaspersky recommends auditing update channels and verifying package origin.
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 · PatchMyPC-ScriptRunner.exe running from the Windows update download folder
CONF HIGHFP LOWCOST LOW

Looks for: Process creation of the stock Patch My PC wrapper from a path under SoftwareDistribution\Download. Kaspersky saw the loader chain start only when the wrapper ran from Download\Install.

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 agent did not observe starting is not captured.
  • PowerQuery assumes: tgt.process.image.sha256 is populated on Process Creation events on this tenant.
  • PowerQuery assumes: tgt.process.image.path holds the full Windows path of the created 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: PatchMyPC-ScriptRunner.exe launched from the Windows update download folder
// MITRE: T1574.014, T1195
// CONF: high  FP: low  COST: low
// REQUIRES: process creation with image path and command line (ProcessRollup2)
// FALSE POSITIVES: none expected. Patch My PC documents CCMCache and IMECache as its staging folders, not SoftwareDistribution.
// SOURCE: Securelist 2026-10-29 (observed command line used -updatepackage from the Install subfolder)
// NOTE: the binary is a legitimate signed product file. Detect only, never prevent.
// LOOKBACK: 30d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^PatchMyPC-ScriptRunner\.exe$/i
| ImageFileName=/\\SoftwareDistribution\\Download\\/i
| table([@timestamp, ComputerName, aid, UserName, FileName, ImageFileName, CommandLine, ParentBaseFileName, SHA256HashData])
Q2 · Loader host components written into the update download Install folder
CONF HIGHFP LOWCOST LOW

Looks for: PE writes of ScriptRunner and its companion DLLs into SoftwareDistribution\Download\Install. The three non-PE files (.config, .resx) have no verified Falcon write event, so this hunts the PE half of the CAB extraction. See native hunt N1 for the rest.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A write the agent did not report as a file event is not captured, and the CQL's PE-specific event type has no separate equivalent.
  • PowerQuery assumes: File Creation and File Modification stand in for the CQL's PE-write events; the .exe or .dll name keeps the population to executables.
  • PowerQuery assumes: tgt.file.path holds the full target path including the file name.
  • KQL – Defender XDR: The CQL's PE-specific write event has no separate equivalent; the executable name is the filter.
  • KQL – Defender XDR assumes: FileCreated and FileModified stand in for the CQL's PE-write events.
  • KQL – Defender XDR assumes: FolderPath holds the folder of the written file.
// HUNT: Patch My PC and Microsoft connector components extracted into SoftwareDistribution\Download\Install
// MITRE: T1574.014, T1195
// CONF: high  FP: low  COST: low
// REQUIRES: PE file write events with target path (PeFileWritten, NewExecutableWritten)
// FALSE POSITIVES: none expected for these names in this folder.
// SOURCE: Securelist 2026-10-29 (files extracted from the CAB to Download\Install); THN 2026-11-01 (PatchMyPC-Enum.dll, PatchMyPC-Models.dll)
// NOTE: names are legitimate product files. The folder is the signal. Detect only.
// LOOKBACK: 30d
#event_simpleName=/PeFileWritten|NewExecutableWritten/
| TargetFileName=/\\SoftwareDistribution\\Download\\Install\\/i
| FileName=/^(PatchMyPC-ScriptRunner|PatchMyPC-Enum|PatchMyPC-Models|Microsoft\.HostIntegration\.Connectors)\.(exe|dll)$/i
| table([@timestamp, ComputerName, aid, FileName, TargetFileName, ContextBaseFileName, SHA256HashData])
Q3 · Any executable running from the Windows update download folder (rarity view)
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: Generic supply-chain angle from the Kaspersky advice to allow third-party management tools only from approved locations: every image that executes from SoftwareDistribution\Download, counted, rarest first. FP: Some servicing and driver packages briefly execute from Download. Baseline 30 days and exclude verified, vendor-signed installers by FileName.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Servicing stack and driver installers that run from the Download folder are expected noise until baselined.
  • PowerQuery assumes: estimate_distinct is an approximate host count, which is enough for ranking.
  • PowerQuery assumes: tgt.process.image.path holds the full Windows path of the created image.
  • KQL – Defender XDR: Servicing stack and driver installers that run from the Download folder are expected noise until baselined.
  • KQL – Defender XDR assumes: FolderPath holds the full image path of the created process.
// HUNT: executables running from SoftwareDistribution\Download, rarest first
// MITRE: T1574.014, T1195
// CONF: medium  FP: medium  COST: medium
// REQUIRES: process creation with image path (ProcessRollup2)
// FALSE POSITIVES: servicing stack and driver or firmware installers that unpack and run from the Download folder
// TUNING: baseline 30 days, then exclude verified signed installers, for example add: | FileName!=/^(approved-installer-1|approved-installer-2)\.exe$/i
// SOURCE: Kaspersky press release 2026-10-29 (monitor update folders for unexpected executables)
// LOOKBACK: 30d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ImageFileName=/\\SoftwareDistribution\\Download\\/i
| groupBy([FileName, ImageFileName], function=[count(aid, distinct=true, as=Hosts), count(as=Runs), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
| sort(Runs, order=asc, limit=200)
Q4 · dllhost.exe COM surrogate launched with the GUIDs from the NetamLoader report
CONF MEDIUMFP LOWCOST LOW

Looks for: dllhost.exe whose command line carries the remote COM object CLSID or the AppID GUID that Securelist names. Kaspersky does not print the dllhost command line, so the format is inferred from how COM surrogates are launched. The AppID GUID is derived from the C2 URL, so it matches this sample only.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The command line format is inferred, not published, so a zero result does not clear a host.
  • PowerQuery assumes: The command line carries the GUID as Processid:{GUID}, the format the CQL tab also assumes.
  • PowerQuery assumes: tgt.process.image.sha256 is populated on Process Creation events on this tenant.
  • KQL – Defender XDR: The command line format is inferred, not published, so a zero result does not clear a host.
  • KQL – Defender XDR assumes: The command line carries the GUID as Processid:{GUID}, the format the CQL tab also assumes.
// HUNT: dllhost.exe launched for the NetamLoader remote COM object or its DllSurrogate AppID
// MITRE: T1559.001, T1574.014
// CONF: medium  FP: low  COST: low
// REQUIRES: process creation with command line (ProcessRollup2)
// FALSE POSITIVES: none expected for these two GUIDs. A miss does not clear a host: the command line format is inferred, not published.
// SOURCE: Securelist 2026-10-29 (remote COM object CLSID used to start dllhost.exe; DllSurrogate AppID written by the .NET 2 Plugin)
// NOTE: dllhost.exe is a Windows binary. Detect only, never prevent.
// LOOKBACK: 30d
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^dllhost\.exe$/i
| CommandLine=/Processid:\{(ab8902b4-09ca-4bb6-b78d-a8f59079a8d5|ff259bcd-2f0a-44de-b34a-0092fec1d8a2)\}/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName, SHA256HashData])
Q5 · dllhost.exe making outbound connections to non-private addresses
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: The implant polls its C2 from inside dllhost.exe: 5 to 6 minutes between requests while the server answers, 22 to 28 hours when it does not. A COM surrogate with external HTTPS is rare, so low-count destinations are the rows to read. FP: Some COM surrogates reach Microsoft or CDN endpoints. Baseline 30 days and exclude known-good destinations; keep the rare ones.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: COM surrogate hosts for Microsoft services and security components also connect outward until baselined.
  • PowerQuery assumes: src.process.name is the connecting process on IP Connect events, per the field model.
  • PowerQuery assumes: IPv6 destinations are not excluded by the IPv4 range tests and may appear.
  • KQL – Defender XDR: COM surrogate hosts for Microsoft services and security components also connect outward until baselined.
  • KQL – Defender XDR assumes: RemoteIPType is populated, and Public excludes private, loopback and link-local addresses.
  • KQL – Defender XDR assumes: IPv6 public destinations are included, unlike the IPv4-only CQL.
// HUNT: dllhost.exe outbound connections to non-private addresses, rare destinations first
// MITRE: T1071.001, T1497.003, T1559.001
// CONF: medium  FP: medium  COST: medium
// REQUIRES: outbound network connection events with initiating process (NetworkConnectIP4)
// FALSE POSITIVES: COM surrogate hosts for Microsoft services, thumbnail or preview handlers that fetch content, endpoint security components
// TUNING: baseline 30 days, then exclude known-good destinations, for example add: | !cidr(RemoteAddressIP4, subnet=["203.0.113.0/24"]) with your own approved ranges
// SOURCE: Securelist 2026-10-29 (.NET 2 Plugin polls over HTTPS from dllhost.exe; initial 20 to 40 minute delay; 5 to 6 minute poll; 22 to 28 hour retry)
// NOTE: the C2 sits behind a shared CDN address, so do not treat the destination address alone as an indicator.
// LOOKBACK: 7d
#event_simpleName=NetworkConnectIP4
| ContextBaseFileName=/^dllhost\.exe$/i
| !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"])
| groupBy([aid, ComputerName, RemoteAddressIP4, RemotePort], function=[count(as=Conns), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
| sort(Conns, order=asc, limit=200)
Q6 · DNS lookups for the NetamLoader C2 domain
CONF HIGHFP LOWCOST LOW

Looks for: Any host resolving openaigpt4v.com or a subdomain, with the requesting process. Single-source infrastructure, so this ships as detect and hunt, not block.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Lookups by a resolver the agent does not observe are not captured.
  • PowerQuery assumes: event.dns.request is the populated name field; some schema versions carry dns.request instead, so run the readiness test first.
  • PowerQuery assumes: The contains test on the dotted subdomain form can also match a name that merely embeds it after a dot, which is the intended subdomain case.
  • KQL – Defender XDR: Advanced Hunting keeps 30 days, so the CQL tab's 90d window is not reproduced here.
  • KQL – Defender XDR assumes: DnsQueryResponse events carry the name under DnsQueryString or QueryName in AdditionalFields; the readiness test prints the field so you can see which key your tenant populates.
// HUNT: DNS lookups for openaigpt4v.com
// MITRE: T1071.001
// CONF: high  FP: low  COST: low
// REQUIRES: DNS request events with process context (DnsRequest)
// FALSE POSITIVES: researchers or analysts resolving the domain from a lab or from a threat-intel enrichment tool
// SOURCE: Securelist 2026-10-29 (C2 domain, first seen 2023-09-27 per Kaspersky); THN 2026-11-01 repeats it
// LOOKBACK: 90d
#event_simpleName=DnsRequest
| DomainName=/(^|\.)openaigpt4v\.com$/i
| groupBy([aid, ComputerName, ContextBaseFileName, DomainName], function=[count(as=Requests), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
| sort(FirstSeen, order=desc, limit=200)
Q7 · DllSurrogate value written under a COM AppID key
CONF LOWFP MEDIUMCOST LOW

Looks for: The .NET 2 Plugin creates an empty DllSurrogate value under HKLM\SOFTWARE\Classes\AppID\{GUID}. Sensor coverage of writes to this key is unconfirmed, so treat a zero result as untested, not clean. FP: Installers and COM-based products register DllSurrogate AppIDs. Baseline and exclude the writing images you recognise.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Coverage of the AppID key by the agent is unconfirmed, so this hunt can be silent while the write happens.
  • PowerQuery assumes: Sensor coverage of the Classes\AppID key is unconfirmed; a zero result may mean the write is not recorded.
  • PowerQuery assumes: Registry Value Modified covers value creation as well as change, and registry.keyPath holds the key path without the value name.
  • KQL – Defender XDR: Coverage of the AppID key by the sensor is unconfirmed, so this hunt can be silent while the write happens.
  • KQL – Defender XDR assumes: Sensor coverage of the Classes\AppID key is unconfirmed; a zero result may mean the write is not recorded.
  • KQL – Defender XDR assumes: RegistryKey holds the key path without the value name.
// HUNT: DllSurrogate value created under a COM AppID registry key
// MITRE: T1559.001, T1112
// CONF: low  FP: medium  COST: low
// REQUIRES: registry value write events (RegGenericValueUpdate). Sensor coverage of Classes\AppID is unconfirmed: validate in tenant that a test write to this key is recorded before trusting a zero result.
// FALSE POSITIVES: software installers and COM server registrations that legitimately use a surrogate host
// TUNING: baseline 30 days, then exclude known installer images, for example add: | ContextBaseFileName!=/^(msiexec|setup)\.exe$/i
// SOURCE: Securelist 2026-10-29 (DllSurrogate set to an empty string under LocalMachine\SOFTWARE\Classes\appid\{GUID}; the GUID derives from the C2 URL)
// LOOKBACK: 30d
#event_simpleName=/RegGenericValueUpdate|AsepValueUpdate/
| RegObjectName=/\\Classes\\AppID\\\{[0-9a-f-]{36}\}$/i
| RegValueName=/^DllSurrogate$/i
| table([@timestamp, ComputerName, aid, UserName, RegObjectName, RegValueName, RegStringValue, ContextBaseFileName, ImageFileName])
09

Detection Rules by Platform

CrowdStrike: Custom IOA

IOA namePatternExclusionsDeploymentPromotes
NetamLoader wrapper in update folderProcess Creation: image file name PatchMyPC-ScriptRunner.exe and image path matches .*\\SoftwareDistribution\\Download\\.*None expectedDetect only. Legitimate signed binary; the path is the signal. No prevent.Promote Q1
Executable from update download folderProcess Creation: image path matches .*\\SoftwareDistribution\\Download\\.*Verified, signed servicing installers per host groupDetect only, low severity until baselined.Promote Q3
dllhost.exe COM activation with report GUIDsProcess Creation: image dllhost.exe and command line contains ab8902b4-09ca-4bb6-b78d-a8f59079a8d5 or ff259bcd-2f0a-44de-b34a-0092fec1d8a2None expectedDetect, medium severity. Windows binary; never prevent.Promote Q4
C2 domain lookupDomain Name: openaigpt4v.com and subdomainsThreat-intel enrichment tools and analyst lab hostsDetect, high severity. Single-source infrastructure, so no prevent.Promote Q6

No IOA in this pack is set to prevent. The indicators are single-sourced, two of the filenames are legitimate signed files, and dllhost.exe is a core Windows binary. Revisit prevent for the domain after a second independent vendor reports on NetamLoader.

SentinelOne: custom detection rules

RuleSource queryRun asNotes
NetamLoader wrapper in update folderQ1, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
Executable from update download folderQ3, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
dllhost.exe COM activation with report GUIDsQ4, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
C2 domain lookupQ6, 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
NetamLoader wrapper in update folderQ1, 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
Executable from update download folderQ3, KQL – Defender XDR tabCustom detection rule, alert only, every hourMake the final result include Timestamp, ReportId and DeviceId: Microsoft recommends them 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
dllhost.exe COM activation with report GUIDsQ4, 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
C2 domain lookupQ6, KQL – Defender XDR tabCustom detection rule, alert only, every hourMake the final result include Timestamp, ReportId and DeviceId: Microsoft recommends them 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

Q2, Q5 and Q7 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

Grouped IOC Quick-Copy

One-click blocks. The CSV is short and every row is detect: one vendor report, inert on-disk files, and a single C2 domain. Behavior is the durable detection.

Falcon IOC Management CSVbulk import, detect only
type,value,action,severity,expiration,description,tags
domain,openaigpt4v.com,detect,high,2027-05-02,NetamLoader C2 per Securelist 2026-10-29 (single source; Cloudflare hosted),campaign:NetamLoader
md5,f2458ed133f8d0ca19e22c10f577e98e,detect,medium,2027-05-02,NetamLoader CAB archive per Securelist (single source),campaign:NetamLoader
md5,e1dc6f1ed193594f2284be22ae08dfa4,detect,medium,2027-05-02,NetamLoader Resources.resx payload carrier per Securelist (single source),campaign:NetamLoader
md5,67e3617f8709d34f806f8e02badba8a5,detect,medium,2027-05-02,NetamLoader fabric.config per Securelist (single source),campaign:NetamLoader
md5,514ac0d73b8a1b73488ef5e2e55a75c3,detect,medium,2027-05-02,NetamLoader planted PatchMyPC-ScriptRunner.exe.config per Securelist (single source),campaign:NetamLoader
# Not imported: 104.18.14.219 is a shared Cloudflare address. In-memory payload hashes are listed in the Memory-Resident block.
# Not imported: PatchMyPC-ScriptRunner.exe and Microsoft.HostIntegration.Connectors.dll are legitimate signed files. No hash or filename block.
Microsoft Defender indicators CSVSettings, Endpoints, Indicators, Import: audit only
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert
DomainName,openaigpt4v.com,Audit,NetamLoader C2 per Securelist 2026-10-29,NetamLoader C2 per Securelist 2026-10-29 (single source; Cloudflare hosted),2027-05-02T00: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 4. 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, not import
Behavioral signatures (hunt, not import)
1. PatchMyPC-ScriptRunner.exe image path under \SoftwareDistribution\Download\ (Q1)
2. Product DLLs and wrapper written into \SoftwareDistribution\Download\Install\ (Q2)
3. Any executable running from \SoftwareDistribution\Download\ (Q3)
4. dllhost.exe started with the report's CLSID or AppID GUID (Q4)
5. dllhost.exe making outbound HTTPS connections, 5 to 6 minute cadence (Q5)
6. DNS for openaigpt4v.com (Q6)
7. DllSurrogate value created under Classes\AppID (Q7)
8. Prefetch and CLR usage log for the wrapper missing after a recorded execution (N3)
9. 20 to 40 minute silence before first C2 contact; 22 to 28 hour retry when unanswered
Named Malware and Toolingdetections and abused files
Named tooling and detections
Kaspersky detection names: HEUR:Trojan.MSIL.NetamLoader.gen, Trojan.MSIL.Zapchast.*, Trojan.Script.Agent.*
Framework name used by Kaspersky: NetamLoader
Stages named in the report: XAML-1, XAML-2, XAML-2-1, XAML-2-2, XAML-3, .NET 1 Loader, .NET 2 Plugin
Legitimate files abused (do not block): PatchMyPC-ScriptRunner.exe, PatchMyPC-Enum.dll, PatchMyPC-Models.dll, Microsoft.HostIntegration.Connectors.dll
Planted or crafted files: PatchMyPC-ScriptRunner.exe.config, fabric.config, Resources.resx
Memory-Resident Payload Hashesreference, not for import
Memory-resident payload hashes (MD5, reference only, not for import)
514117d536e347560d049cfe0c609632  XAML-1
9000820d8153c24cd60d53001d3a97b6  XAML-2
b3c4a635179680edafb5d006cf0cffc6  XAML-2-1
4f28622c5e60c01319bf48cf742fe1a5  XAML-2-2
c86d53392a87dd3f3e62d62c9b92a9bd  XAML-3
ab81564bcb8ca7e710c66832de747556  .NET 1 Loader
cfb8b281bdf5d9390e64b10c312fb2c1  .NET 2 Plugin
Patch / ASR / Log Auditchecklist
Patch / ASR / log audit checklist
[ ] Which hosts have PatchMyPC-ScriptRunner.exe excluded from ASR or AV by process name? Scope exclusions to CCMCache / IMECache paths (playbook P3)
[ ] Any executable, .config or .resx under C:\Windows\SoftwareDistribution\Download? (playbook P1)
[ ] Are Windows Update, CLR usage and Prefetch-related logs forwarded off the endpoint?
[ ] Is there an alert on a gap in local update-agent logs?
[ ] Which AppID keys carry a DllSurrogate value, and are they all expected? (playbook P4)
[ ] Resolver and proxy logs searched for openaigpt4v.com and /model_v4/ for 90 days
11

Detection Validation Gates

GateCheck
Telemetry readyConfirm process creation with command line and path (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents), executable writes (CrowdStrike: PeFileWritten, NewExecutableWritten), outbound connections with process (CrowdStrike: NetworkConnectIP4) and DNS lookup with the requesting process (CrowdStrike: DnsRequest) are present fleet-wide, including on any customized Windows 10 build. Confirm a test write under Classes\AppID is recorded before trusting Q7.
Benign baselineRun Q3, Q5 and Q7 over 30 days and baseline. Q1, Q2, Q4 and Q6 should return zero rows in a normal estate. If Q1 returns rows, confirm whether your Patch My PC deployment is running from an unusual location before escalating.
Positive testsDo not run malware. Copy any harmless signed executable into a test folder named like the update download path (a lab path, not the live one) and rename it PatchMyPC-ScriptRunner.exe to exercise the rule logic, or resolve openaigpt4v.com from a lab host for Q6. Q4 needs a lab dllhost.exe launched with the GUID argument and is optional.
PromotionIOAs ship detect only (section 9). Promote the C2 domain to prevent only after a second independent source reports it. Never prevent on PatchMyPC-ScriptRunner.exe, Microsoft.HostIntegration.Connectors.dll or dllhost.exe.
12

Hardening, Tiered & Deployable

NetamLoader is delivered malware with no CVE, so there is no patch. Hardening targets what it needs: a place to run signed binaries with attacker-controlled config files, local logs it can edit, and a COM surrogate that can reach the internet. Controls are anchored to MITRE mitigations from the saved ATT&CK pages and to the Kaspersky and Patch My PC statements in the snapshots.

Immediate: this week, no user-visible change
  • Audit the update download folder. Run the read-only inventory in P1 on a sample of hosts. Kaspersky advises monitoring this folder for unexpected executables and configuration files. M1047 Audit (T1685). Vendor advisory: Kaspersky press release.
  • Forward logs off the endpoint and alert on gaps. Send Windows event logs, update-agent logs and EDR telemetry to a central store, and alert when a host's local log goes missing lines. NetamLoader deletes local copies. M1029 Remote Data Storage (T1070). Vendor advisory: Kaspersky. ⚠ best-practice, no formal benchmark for the update-agent log.
  • Scope any Patch My PC exclusion by path, not by name. The vendor article recommends excluding PatchMyPC-ScriptRunner.exe from ASR rules. That is safe only for its CCMCache and IMECache staging folders. A by-name exclusion would also cover the copy abused here. Check with P3. M1047 Audit (T1685). Judgment call: the vendor article does not say to scope by path.
  • Turn on Q1, Q2 and Q6 as scheduled detections at detect severity, after the 30-day look-back is clean.
Near term: 1 to 4 weeks, pilot on a ring first
  • Application control in audit mode for the update download folder. Audit execution from SoftwareDistribution\Download with AppLocker, then enforce on a ring once the audit log is clean (P2). M1038 Execution Prevention (T1685, T1574.014 context). Edition and policy-mode limits apply: ⚠ verify AppLocker support on a customized Windows 10 build.
  • Baseline COM surrogate registrations. List every AppID that carries a DllSurrogate value and review changes (P4). M1026 Privileged Account Management and M1048 Application Isolation and Sandboxing (T1559.001: AppID registry permissions under HKLM\SOFTWARE\Classes\AppID).
  • Egress control for dllhost.exe. Where policy allows, block or alert on outbound internet access from dllhost.exe at the host firewall or proxy. ⚠ best-practice, no formal benchmark.
Strategic: 1 to 3 months
  • Verify the origin of every update package. Inventory the tools allowed to place packages in the update folder, require signature verification of each, and run third-party management tools only from approved locations. Vendor advisory: Kaspersky press release. Supports T1195.
  • Write-protect .NET application directories. Install .NET software in locations that standard and service accounts cannot write to, including the folders applications run from. M1022 Restrict File and Directory Permissions (T1574.014). Note the loader needs administrator rights, so this reduces but does not remove risk.
  • Managed-code application control. Prefer WDAC policies that control which .NET assemblies and configs a signed host may load, over name-based allow lists. M1038. ⚠ best-practice, no formal benchmark.
  • Network anomaly monitoring for infrequent encrypted connections to unfamiliar hosts behind content delivery networks, including 22 to 28 hour retry cadences. Vendor advisory: Kaspersky press release (KATA is Kaspersky's product; any NDR with long-window analytics fits).
13

Deployable Playbooks

Every step lists Prerequisites, Reboot required and Rollback. Replace placeholders such as the evidence share before running.

P1 Inventory the update download folder (read-only)

Prerequisites: local administrator on a pilot host (read-only commands; file hashing may take a moment).
Reboot required: no.
Rollback: none required, nothing is changed.

# 1) Everything staged under the update download folder (report only)
$root = 'C:\Windows\SoftwareDistribution\Download'
Get-ChildItem -LiteralPath $root -Recurse -Force -ErrorAction SilentlyContinue -Include *.exe,*.dll,*.config,*.resx |
  Select-Object FullName, Length, LastWriteTime

# 2) MD5 of those files, to compare with the list in section 10
Get-ChildItem -LiteralPath $root -Recurse -Force -ErrorAction SilentlyContinue -Include *.exe,*.dll,*.config,*.resx |
  Get-FileHash -Algorithm MD5 | Select-Object Hash, Path

# 3) Config files that name an AppDomainManager (report only)
Get-ChildItem -LiteralPath $root -Recurse -Force -ErrorAction SilentlyContinue -Filter *.config |
  Select-String -Pattern 'appDomainManagerAssembly|appDomainManagerType' | Select-Object Path, Line

P2 AppLocker: audit execution from the update download folder

Prerequisites: an edition that supports AppLocker; Application Identity service set to automatic; a pilot ring only. This sets the Exe collection to AuditOnly, so nothing is blocked. If the host already has an AppLocker policy, do not run step 2: merge by hand after reviewing the backup.
Reboot required: no.
Rollback: Set-AppLockerPolicy -XmlPolicy "$env:ProgramData\applocker-backup.xml" (restores the policy saved in step 1). If there was no policy before, clear it with: Set-AppLockerPolicy -XmlPolicy "$env:ProgramData\applocker-empty.xml" after writing an empty <AppLockerPolicy Version="1" /> file there.

# 1) Back up the effective policy first
Get-AppLockerPolicy -Effective -Xml | Out-File -Encoding utf8 "$env:ProgramData\applocker-backup.xml"
Set-Service -Name AppIDSvc -StartupType Automatic; Start-Service AppIDSvc

# 2) Save the policy below as C:\Temp\netam-audit.xml, then apply it
Set-AppLockerPolicy -XmlPolicy 'C:\Temp\netam-audit.xml'

# 3) Review audit events (ID 8003 = would have been blocked) for 2 to 4 weeks
Get-WinEvent -LogName 'Microsoft-Windows-AppLocker/EXE and DLL' -FilterXPath "*[System[(EventID=8003)]]" -MaxEvents 200 |
  Select-Object TimeCreated, Message

# Only after the audit log is clean on the pilot ring, change EnforcementMode to Enabled.

# ---- netam-audit.xml ----
<AppLockerPolicy Version="1">
  <RuleCollection Type="Exe" EnforcementMode="AuditOnly">
    <FilePathRule Id="921cc481-6e17-4653-8f75-050b80acca20" Name="(Default) All files located in the Program Files folder" Description="" UserOrGroupSid="S-1-1-0" Action="Allow">
      <Conditions><FilePathCondition Path="%PROGRAMFILES%\*" /></Conditions>
    </FilePathRule>
    <FilePathRule Id="a61c8b2c-a319-4cd0-9690-d2177cad7b51" Name="(Default) All files located in the Windows folder" Description="" UserOrGroupSid="S-1-1-0" Action="Allow">
      <Conditions><FilePathCondition Path="%WINDIR%\*" /></Conditions>
    </FilePathRule>
    <FilePathRule Id="fd686d83-a829-4351-8ff4-27c7de5755d2" Name="(Default) All files" Description="" UserOrGroupSid="S-1-5-32-544" Action="Allow">
      <Conditions><FilePathCondition Path="*" /></Conditions>
    </FilePathRule>
    <FilePathRule Id="5b0b6d11-3c49-4d0c-9f43-6c2d1a7d0a11" Name="HuntPack: deny execution from the Windows Update download folder" Description="Audit only" UserOrGroupSid="S-1-1-0" Action="Deny">
      <Conditions><FilePathCondition Path="%WINDIR%\SoftwareDistribution\Download\*" /></Conditions>
    </FilePathRule>
  </RuleCollection>
</AppLockerPolicy>

P3 Review Patch My PC exclusions and scope them by path

Prerequisites: Microsoft Defender Antivirus is the active AV; local administrator; read-only commands.
Reboot required: no.
Rollback: none required for steps 1 and 2. For step 3, re-add the previous exclusion with the command printed by step 1.

# 1) Current exclusions that mention the Patch My PC wrapper (report only)
$p = Get-MpPreference
$p.ExclusionProcess | Where-Object { $_ -match 'PatchMyPC' }
$p.ExclusionPath | Where-Object { $_ -match 'PatchMyPC|CCMCache|IMECache' }
$p.AttackSurfaceReductionOnlyExclusions | Where-Object { $_ -match 'PatchMyPC|CCMCache|IMECache' }

# 2) Decide: a by-name exclusion for PatchMyPC-ScriptRunner.exe also covers a copy run from
#    C:\Windows\SoftwareDistribution\Download\Install. Prefer path-scoped exclusions.

# 3) Example replacement (edit paths to match your Configuration Manager or Intune cache)
# Remove-MpPreference -ExclusionProcess 'PatchMyPC-ScriptRunner.exe'
# Add-MpPreference -AttackSurfaceReductionOnlyExclusions 'C:\Windows\ccmcache\*'

P4 List COM AppIDs that use a DllSurrogate (read-only)

Prerequisites: local administrator; read-only.
Reboot required: no.
Rollback: none required, nothing is changed.

# AppID keys that carry a DllSurrogate value. Baseline the list, then review changes.
Get-ChildItem 'HKLM:\SOFTWARE\Classes\AppID' -ErrorAction SilentlyContinue |
  ForEach-Object {
    $v = Get-ItemProperty -LiteralPath $_.PSPath -Name DllSurrogate -ErrorAction SilentlyContinue
    if ($null -ne $v) { [pscustomobject]@{ AppID = $_.PSChildName; DllSurrogate = $v.DllSurrogate } }
  } | Sort-Object AppID

# Report only: compare the GUID in section 4 with the output.

P5 Preserve evidence before reimaging (copy-only)

Prerequisites: local administrator on the suspect host; a write-protected evidence share; do this BEFORE reimaging. The plugin stops about 28 hours after it starts, so act promptly.
Reboot required: no. Do not reboot before capture.
Rollback: none required, copy-only.

# 1) Identify dllhost.exe instances with the GUIDs from section 4 (report only)
Get-CimInstance Win32_Process -Filter "Name='dllhost.exe'" |
  Where-Object { $_.CommandLine -match 'ab8902b4-09ca-4bb6-b78d-a8f59079a8d5|ff259bcd-2f0a-44de-b34a-0092fec1d8a2' } |
  Select-Object ProcessId, CreationDate, CommandLine

# 2) Copy evidence to the share (replace the placeholder)
$dst = '\\EVIDENCE-SHARE\case-01\HOST-01'
Copy-Item 'C:\Windows\Logs\CmitUpdateAgent' "$dst\CmitUpdateAgent" -Recurse -ErrorAction SilentlyContinue
Copy-Item 'C:\Windows\SoftwareDistribution\Download' "$dst\Download" -Recurse -ErrorAction SilentlyContinue
Copy-Item "$env:WINDIR\Prefetch\*.pf" "$dst\Prefetch" -ErrorAction SilentlyContinue
# 3) Capture process memory of the dllhost.exe found in step 1 with your EDR or forensic tooling.
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateNetwork-contain the host in your EDR. Do not power off or reboot; the plugin lives in memory. Do not block the Cloudflare address at the network layer, it is shared.SOC L2Containment timestamp; Q1 to Q7 rows
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 L2Containment timestamp; Q1 to Q7 rows
PreserveCapture memory of the dllhost.exe from Q4 or Q5, then copy CmitUpdateAgent logs, the SoftwareDistribution\Download folder and Prefetch (P5). The loader deletes many of these, so absent files are findings too.IRHashes of copied evidence; memory image
ScopeRun Q1 to Q7 fleet-wide for 90 days. Pull resolver and proxy logs for openaigpt4v.com and /model_v4/. Check update-channel logs for how the CAB arrived (N5).IR + SOCHost list with first and last sighting
EradicateReimage after evidence is preserved. The loader required administrator rights, so treat credentials on the host as exposed: rotate local admin and service account secrets used there. Remove any unexpected package from the update download folder on peers.IR + identity teamReimage record; rotation log
RecoverRejoin only a rebuilt host. Re-verify update-channel tooling and its allowed locations. Restore path-scoped exclusions (P3).ITChange record
HuntKeep Q6 and Q5 scheduled at detect for 90 days. A missed sandbox is likely: the plugin pauses 20 to 40 minutes before its first request and 22 to 28 hours between failed polls.SOCHunt log
15

Detection Coverage Map

TechniqueBehaviorCQLIOACrowdStrike coverageSentinelOneDefender XDR
T1195Delivery into the update download folderQ2, Q3IOA 2Partial delivery path unknown; executable half onlyPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1574.014Wrapper run beside a planted .configQ1, Q2IOA 1Good process and PE halves; config and resx file writes are a gap (N1, N2)Good, with assumptionsGood, with assumptions
T1559.001dllhost.exe started for a remote COM objectQ4, Q7IOA 3Partial command line format inferred; AppID key coverage unconfirmedPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1071.001HTTPS polling from dllhost.exe; C2 domain lookupQ5, Q6IOA 4Good for the known domain; domain-agnostic view is Q5Good, with assumptionsGood, with assumptions
T1497.00320 to 40 minute pause; 22 to 28 hour retryQ5nonePartial 7-day Q5 window can miss the long retry; use 30 daysPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1070Deletion of files, CLR logs, Prefetch, log linesnonenoneGAP no deletion query; native N3 and off-endpoint log forwardingGAPGAP
T1620 / T1685In-memory XAML loading; AMSI and ETW tamperingnonenoneGAP in-process; outside endpoint telemetryGAPGAP

Validation gates: see section 11. Two known blind spots are declared above: file deletion and in-process tampering. A sandbox that stops after a few minutes will see no network activity.

16

Hunt Summary Ticket

TITLE:        Hunt - NetamLoader (.NET AppDomainManager loader in the Windows update folder)
SEVERITY:     High (stealthy in-memory loader on government-grade Windows builds; one victim reported)
SCOPE:        Windows 10 endpoints, especially customized builds and any host where Patch My PC runs
HYPOTHESIS:   A CAB placed in SoftwareDistribution\Download unpacks a signed Patch My PC wrapper beside a
              planted config. The runtime loads a Microsoft-signed assembly that reads fabric.config and
              Resources.resx, patches AMSI, deletes its traces and runs a plugin in dllhost.exe that
              polls openaigpt4v.com after a 20 to 40 minute pause.
QUERIES RUN:  Q1 wrapper in update folder | Q2 PE writes in Download\Install | Q3 any exe from Download |
              Q4 dllhost COM GUIDs | Q5 dllhost external connections | Q6 C2 DNS | Q7 DllSurrogate write + Native N1-N5
DO FIRST:     Q1, Q2, Q6 for 30 to 90 days. Any hit: capture dllhost.exe memory, preserve logs, then isolate.
FINDINGS:     <pending analyst execution>
GAPS:         Delivery path unknown; config and resx writes, file and log deletion and all in-process
              activity (AMSI, XAML loading) are outside endpoint telemetry; no persistence described.
ACTIONS:      Audit the update folder (P1); forward logs off-host; scope Patch My PC exclusions by path (P3);
              AppLocker audit (P2); never prevent on PatchMyPC-ScriptRunner.exe, Microsoft connector DLL or dllhost.exe.
OWNER:        HuntPack
VERSION:      v0.1 - 2026-11-02
17

Changelog

v0.12026-11-02Initial pack built to the review-v2 gold layout from the Kaspersky Securelist report and press release of 2026-10-29, with the Hacker News summary of 2026-11-01. 7 CQL hunts, 5 native hunts, 4 IOA candidates (all detect only), 5 deployable playbooks. One C2 domain and four on-disk MD5 values ship at detect; the Cloudflare address, in-memory hashes and legitimate files are context only. SentinelOne PowerQuery and Microsoft Defender KQL tabs added to Q1-Q7: SIEM Query Rosetta release 0.1.0 (public-beta; static-reviewed translations).
18

References

TierSourceUsed forAccessed
1Kaspersky Securelist: Unknown APT uses NetamLoader to attack a Win10 OS customized for governments in Asia (2026-10-29)All technical facts, indicators, victim and attribution statements2026-11-02
1Kaspersky press release (2026-10-29)Supply chain framing, Patch My PC statement, delays, recommendations2026-11-02
2The Hacker News (2026-11-01)Secondary summary; two extra companion DLL names2026-11-02
1Patch My PC: What Is Patch My PC ScriptRunner?Legitimate behavior, signing, staging folders, exclusion advice2026-11-02
2MITRE ATT&CK T1574.014 AppDomainManagerTechnique and M10222026-11-02
2MITRE ATT&CK T1685 Disable or Modify ToolsAMSI tampering context and mitigations2026-11-02
2MITRE ATT&CK T1070 Indicator RemovalAnti-forensics and M10292026-11-02
2MITRE ATT&CK T1559.001 Component Object ModelCOM abuse and AppID mitigations2026-11-02
2MITRE ATT&CK T1497.003 Time Based ChecksSandbox delay2026-11-02
2MITRE ATT&CK T1620 Reflective Code LoadingIn-memory loading2026-11-02

HuntPack v0.1 · NetamLoader · Generated 2026-11-02 · Defensive use only, no malware code. Single technical origin: validate field names in your tenant, confirm sensor support on any customized Windows 10 build and treat a zero from Q7 as untested.