MATCHBOIL Downloader Activity and Persistence
An adversary has deployed a MATCHBOIL downloader that establishes persistence through Registry Run keys or scheduled tasks after performing WMI-based system discovery to uniquely identify the victim host.
Based on research by ESET Research 2026-10-10 13 steps · 6 queries T1047 T1053.005 T1059.001 T1071.001 T1547.001 T1566
Brief
Why Now
ESET Research recently detailed a campaign by the UAC-0099 group using a C# downloader known as MATCHBOIL (see MATCHBOIL: New tricks, same old evil intentions). The adversary targets Ukrainian governmental and energy organizations by deploying loaders that fingerprint victim systems and maintain persistence using user-profile directories. This hunt focuses on identifying the specific lifecycle of this downloader across the endpoint and network.
How the Hunt Flows
The hunt begins with a scoping phase on the hb_file_activity surface. The adversary typically creates a directory named DeviceMonitor within the Local AppData folder and drops a config.ini file to manage state. We search for these specific folder patterns and filenames to find hosts that have already transitioned to the payload delivery stage.
Once we identify suspicious hosts, we pivot to initial execution leads. The hunt examines the hb_script_activity and hb_process_activity surfaces in parallel. We look for VBScript or PowerShell loaders that use XMLHTTP objects to fetch payloads, alongside wmic.exe commands querying CPU and BIOS serial numbers. Finding a script-based arrival coupled with hardware reconnaissance provides a high-confidence lead for an active MATCHBOIL infection.
In the validation phase, we look for established persistence and C2 metadata. We query hb_registry_activity for Run keys pointing to the DeviceMonitor path and hb_scheduled_job for tasks named CheckTask. We then correlate these endpoint triggers with hb_http_activity. Recent variants use a hardcoded 25-character User-Agent string. By baselining User-Agent lengths and filtering for rare values that meet this length, we isolate the C2 channel from legitimate background traffic.
This is a hunt rather than a simple detection because Registry Run keys and scheduled tasks in user directories are frequently used by benign software updaters. A standalone detection rule would produce high volumes of noise. This hunt requires the correlation of file creation, WMI-based discovery, and specific HTTP metadata to confirm the full attack lifecycle before an analyst acts.
Blind Spots
This hunt has two primary blind spots. First, MATCHBOIL can perform WMI reconnaissance directly through .NET APIs like ManagementObjectSearcher. If the malware calls these APIs internally rather than spawning wmic.exe, the discovery phase will be invisible to process-line monitoring. Second, the use of HTTPS for C2 communication prevents the inspection of the hex-encoded payload within the network traffic. Analysts must rely on User-Agent length and endpoint file artifacts to confirm the infection.
Steps
-
Scope for installation artifacts
Query · scopingIdentify hosts where the downloader's directory structure or configuration files have been created.
reads hb_file_activitysqlSELECT device_hostname, file_path, file_name, process_name, time FROM hb_file_activity WHERE (LOWER(file_path) LIKE LOWER('{{target_folder_pattern}}') OR LOWER(file_name) = 'config.ini') AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Hosts showing the creation of the DeviceMonitor folder or config.ini files in AppData.
-
Script-based loaders
Query · triageIdentify VBScript or PowerShell activity used to download and execute the primary MATCHBOIL binary.
reads hb_script_activitysqlSELECT device_hostname, script_content, script_type, time FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%xmlhttp%' OR LOWER(script_content) LIKE '%adodb.stream%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Script blocks containing web requests or stream-to-file logic. Silence indicates no script-based delivery was captured.
-
WMI hardware discovery
Query · triageFind hardware fingerprinting commands used for victim identification during C2 check-in.
reads hb_process_activitysqlSELECT device_hostname, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%wmic%' AND (LOWER(process_cmd_line) LIKE '%cpu%' OR LOWER(process_cmd_line) LIKE '%bios%')) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. WMIC commands querying CPUID or Serial Numbers. Silence may mean discovery was handled via internal .NET APIs.
-
Triage early indicators
Agent triageDetermine if the scoping files and initial execution patterns represent a suspicious MATCHBOIL lead.
-
Registry Run-key persistence
Query · detection candidateIdentify registry keys pointing to the downloader binary for persistence across reboots.
reads hb_registry_activitysqlSELECT device_hostname, reg_target, reg_value_name, reg_value_data, time FROM hb_registry_activity WHERE LOWER(reg_target) LIKE '%\currentversion\run%' AND (LOWER(reg_value_name) = 'devicemonitor' OR LOWER(reg_value_data) LIKE '%devicemonitor%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Registry values in HKCU Run keys pointing to user-profile paths. Silence means persistence may be task-based.
-
Scheduled task persistence
Query · triageFind scheduled tasks used to maintain execution or provide periodic payload updates.
reads hb_scheduled_jobsqlSELECT device_hostname, job_name, job_cmd_line, job_definition_path, time FROM hb_scheduled_job WHERE (LOWER(job_name) LIKE '%checktask%' OR LOWER(job_definition_path) LIKE '%checktask%' OR LOWER(job_cmd_line) LIKE '%devicemonitor%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Scheduled tasks named CheckTask or pointing to the DeviceMonitor folder. Silence indicates no such task persistence.
-
C2 HTTP metadata patterns
Query · baselineBaseline User-Agent lengths to find the anomalous 25-character strings documented in MATCHBOIL C2 traffic.
reads hb_http_activitysqlSELECT url_hostname, user_agent, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_http_activity WHERE length(user_agent) = {{ua_length}} AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY url_hostname, user_agent HAVING hosts <= 3What a hit looks like. Rare HTTP requests with 25-character User-Agents. Silence means C2 metadata has likely rotated.
-
Analyze full infection state
Agent triageCorrelate early stage leads with persistence and C2 activity to produce a final verdict per host.
-
Route on verdict
DecisionDirect the results to containment or manual analysis based on the agent's confidence.
-
Isolate infected host
Response actionPrevent further payload delivery and lateral movement while preserving forensic state.
-
Manual analyst review
Analyst taskReview findings and confirm UAC-0099 attribution.
-
Hunt close out
Analyst taskDocument result and update detection logic.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Spearphishing and VBScript Loader T1566 · T1059.001 |
Yes | script-loaders |
| System Discovery via WMI T1047 |
Yes | wmi-recon |
| C2 Communication and Payload Retrieval T1071.001 |
Yes | scoping-installation, c2-http-metadata |
| Registry and Task Persistence T1547.001 · T1053.005 |
Yes | registry-run-persistence, scheduled-task-persistence |
Blind spots
- Needs EDR introspection into .NET ManagementObjectSearcher calls. C# processes can call WMI directly via APIs, which bypasses command-line monitoring for wmic.exe, making the discovery phase invisible. It would answer whether the downloader performed WMI recon without spawning wmic.exe.
- Needs TLS inspection or endpoint memory analysis. HTTPS encryption prevents the identification of the payload inside the HTML script tags during transit. It would answer whether the hex-encoded payload was delivered in the HTTP response body.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Days of history to examine. |
scope_hosts | list[host] | — | Optional list of hostnames to narrow the search. |
target_folder_pattern | string | %\\appdata\\local\\devicemonitor\\% | Path pattern for the MATCHBOIL installation directory. |
ua_length | number | 25 | User-Agent string length observed in 2024 variants. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Web server / proxy logs | siem | network |
Source
---
analysis: A simple Run-key rule might fire on many benign updaters; this hunt correlates
folder creation, script-based delivery, WMI discovery, and rare HTTP metadata to
reduce false positives and identify the full attack lifecycle.
blind_spots:
- id: internal-wmi-recon
question: whether the downloader performed WMI recon without spawning wmic.exe
requires: EDR introspection into .NET ManagementObjectSearcher calls
risk: C# processes can call WMI directly via APIs, which bypasses command-line monitoring
for wmic.exe, making the discovery phase invisible.
stage: discovery-wmi-recon
- id: encrypted-c2-payload
question: whether the hex-encoded payload was delivered in the HTTP response body
requires: TLS inspection or endpoint memory analysis
risk: HTTPS encryption prevents the identification of the payload inside the HTML
script tags during transit.
stage: c2-payload-delivery
coverage:
- stage: initial-access-phishing-script
status: covered
steps:
- script-loaders
- stage: discovery-wmi-recon
status: covered
steps:
- wmi-recon
- stage: c2-payload-delivery
status: covered
steps:
- scoping-installation
- c2-http-metadata
- stage: persistence-registry-task
status: covered
steps:
- registry-run-persistence
- scheduled-task-persistence
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: promote-to-detection
justification: UAC-0099 is a known threat to critical infrastructure and governmental
sectors in Ukraine. The MATCHBOIL downloader is a persistent entry point that
requires cross-surface correlation to identify definitively.
methodology: model-assisted
trigger: intel-report
hypothesis: An adversary has deployed a MATCHBOIL downloader that establishes persistence
through Registry Run keys or scheduled tasks after performing WMI-based system discovery
to uniquely identify the victim host.
labels:
- hunt
- attack.t1566
- attack.t1059.001
- attack.t1047
- attack.t1547.001
- attack.t1053.005
- attack.t1071.001
- command and control
- discovery
- initial access
- persistence
name: MATCHBOIL Downloader Activity and Persistence
parameters:
lookback_days:
default: '14'
description: Days of history to examine.
type: number
scope_hosts:
default: []
description: Optional list of hostnames to narrow the search.
type: list[host]
target_folder_pattern:
default: '%\\appdata\\local\\devicemonitor\\%'
description: Path pattern for the MATCHBOIL installation directory.
from:
kind: article
observed: '2026-10-08'
ref: https://www.welivesecurity.com/en/eset-research/matchboil-new-tricks-same-old-evil-intentions/
type: string
ua_length:
default: '25'
description: User-Agent string length observed in 2024 variants.
from:
kind: article
observed: '2026-10-08'
ref: https://www.welivesecurity.com/en/eset-research/matchboil-new-tricks-same-old-evil-intentions/
type: number
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.welivesecurity.com/en/eset-research/matchboil-new-tricks-same-old-evil-intentions/
gates:
- dry-run
- lint
model: hb_google/gemini-3-flash-preview
rationale: The hunt should prioritize workstations and servers in the transportation
and energy sectors. Start with a broad lookback window as MATCHBOIL has been active
for several years.
references:
- name: "ESET Research \u2014 MATCHBOIL: New tricks, same old evil intentions"
url: https://www.welivesecurity.com/en/eset-research/matchboil-new-tricks-same-old-evil-intentions/
related:
- hunt: lonepage-powershell-downloader
reason: LONEPAGE is another UAC-0099 downloader that focuses on PowerShell and specific
C&C URL patterns.
relation: out-of-scope-alternative
scenario:
stages:
- name: Spearphishing and VBScript Loader
observables:
- VBScript file manual execution
- Archive file download from spearphishing link
- Execution of MATCHBOIL binary
slug: initial-access-phishing-script
tactic: initial-access
techniques:
- T1566
- T1059.001
- name: System Discovery via WMI
observables:
- ManagementObjectSearcher C# class usage
- WMI queries for CPUID
- WMI queries for BIOS serial number
- WMI queries for username and MAC address
slug: discovery-wmi-recon
tactic: discovery
techniques:
- T1047
- name: C2 Communication and Payload Retrieval
observables:
- HTTPS requests with custom HTTP header 'SN'
- HTTPS requests with custom HTTP header 'Count'
- 25-character User-Agent string
- Hex-encoded payload extracted from HTML <script> tags
- Creation of config.ini in payload directory
- Payload installation in %LOCALAPPDATA%\DeviceMonitor
slug: c2-payload-delivery
tactic: command-and-control
techniques:
- T1071.001
- name: Registry and Task Persistence
observables:
- Registry value 'DeviceMonitor' in HKCU\Software\Microsoft\Windows\CurrentVersion\Run
- Scheduled task named 'Updates\CheckTask'
- Two-minute execution timer (later variants)
slug: persistence-registry-task
tactic: persistence
techniques:
- T1547.001
- T1053.005
summary: The Russia-aligned UAC-0099 group uses spearphishing links to deliver an
archive containing a VBScript loader, which subsequently installs the MATCHBOIL
C# downloader. MATCHBOIL performs system discovery via WMI and establishes persistence
through both registry Run keys and scheduled tasks before communicating with a
C2 server to deploy the MATCHWOK backdoor.
severity: high
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
web:
category: siem
name: Web server / proxy logs
telemetry:
- network
tlp: clear
type: investigation
---
# MATCHBOIL Downloader Activity and Persistence
This hunt targets the MATCHBOIL C# downloader, a tool used by the UAC-0099 group. The malware follows a distinct lifecycle: it is typically introduced via VBScript loaders, performs hardware-based fingerprinting using WMI, and establishes persistence in user-writable directories. The hunt uses a phased flow to first identify initial execution and discovery leads, then validates them against established persistence mechanisms and anomalous C2 HTTP metadata such as specific User-Agent lengths and configuration file drops.
## scoping-installation
<!-- Scope for installation artifacts -->
Identify hosts where the downloader's directory structure or configuration files have been created.
```sqlite target=endpoint role=scoping params=(lookback_days=lookback_days, target_folder_pattern=target_folder_pattern)
~~~yaml
expected: Hosts showing the creation of the DeviceMonitor folder or config.ini files
in AppData.
reads:
- device_hostname
- file_name
- file_path
- process_name
- time
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-10-10'
~~~
SELECT device_hostname, file_path, file_name, process_name, time FROM hb_file_activity WHERE (LOWER(file_path) LIKE LOWER('{{target_folder_pattern}}') OR LOWER(file_name) = 'config.ini') AND time >= datetime('now', '-{{lookback_days}} days')
```
## parallel-early
<!-- Examine early-stage leads -->
parallel:
- → script-loaders
- → wmi-recon
join: → triage-early
## script-loaders
<!-- Script-based loaders -->
Identify VBScript or PowerShell activity used to download and execute the primary MATCHBOIL binary.
```sqlite target=endpoint role=triage params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Script blocks containing web requests or stream-to-file logic. Silence indicates
no script-based delivery was captured.
reads:
- device_hostname
- script_content
- script_type
- time
silence: not_evidence_of_absence
source: hb_script_activity
verified: dry-run
verified_at: '2026-10-10'
~~~
SELECT device_hostname, script_content, script_type, time FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%xmlhttp%' OR LOWER(script_content) LIKE '%adodb.stream%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## wmi-recon
<!-- WMI hardware discovery -->
Find hardware fingerprinting commands used for victim identification during C2 check-in.
```sqlite target=endpoint role=triage params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: WMIC commands querying CPUID or Serial Numbers. Silence may mean discovery
was handled via internal .NET APIs.
reads:
- device_hostname
- process_cmd_line
- time
- user_name
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-10-10'
~~~
SELECT device_hostname, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%wmic%' AND (LOWER(process_cmd_line) LIKE '%cpu%' OR LOWER(process_cmd_line) LIKE '%bios%')) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## triage-early
<!-- Triage early indicators -->
```agent target=hunter
cite: required
context:
- scoping-installation
- script-loaders
- wmi-recon
max_iterations: 3
objective: Identify hosts where file installation aligns with suspicious script activity
or hardware discovery.
success_criteria: A list of hosts showing evidence of multiple early-stage indicators.
tools:
- endpoint
- web
```
## parallel-follow
<!-- Validate established infection -->
parallel:
- → registry-run-persistence
- → scheduled-task-persistence
- → c2-http-metadata
join: → triage-full
## registry-run-persistence
<!-- Registry Run-key persistence -->
Identify registry keys pointing to the downloader binary for persistence across reboots.
```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Registry values in HKCU Run keys pointing to user-profile paths. Silence
means persistence may be task-based.
reads:
- device_hostname
- reg_target
- reg_value_data
- reg_value_name
- time
silence: not_evidence_of_absence
source: hb_registry_activity
verified: dry-run
verified_at: '2026-10-10'
~~~
SELECT device_hostname, reg_target, reg_value_name, reg_value_data, time FROM hb_registry_activity WHERE LOWER(reg_target) LIKE '%\currentversion\run%' AND (LOWER(reg_value_name) = 'devicemonitor' OR LOWER(reg_value_data) LIKE '%devicemonitor%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## scheduled-task-persistence
<!-- Scheduled task persistence -->
Find scheduled tasks used to maintain execution or provide periodic payload updates.
```sqlite target=endpoint role=triage params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Scheduled tasks named CheckTask or pointing to the DeviceMonitor folder.
Silence indicates no such task persistence.
reads:
- device_hostname
- job_cmd_line
- job_definition_path
- job_name
- time
silence: not_evidence_of_absence
source: hb_scheduled_job
verified: dry-run
verified_at: '2026-10-10'
~~~
SELECT device_hostname, job_name, job_cmd_line, job_definition_path, time FROM hb_scheduled_job WHERE (LOWER(job_name) LIKE '%checktask%' OR LOWER(job_definition_path) LIKE '%checktask%' OR LOWER(job_cmd_line) LIKE '%devicemonitor%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## c2-http-metadata
<!-- C2 HTTP metadata patterns -->
Baseline User-Agent lengths to find the anomalous 25-character strings documented in MATCHBOIL C2 traffic.
```sqlite target=web role=baseline params=(lookback_days=lookback_days, ua_length=ua_length, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: Rare HTTP requests with 25-character User-Agents. Silence means C2 metadata
has likely rotated.
prevalence:
by: device_hostname
key:
- url_hostname
- user_agent
rare_below: 3
reads:
- device_hostname
- time
- url_hostname
- user_agent
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-10-10'
~~~
SELECT url_hostname, user_agent, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_http_activity WHERE length(user_agent) = {{ua_length}} AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY url_hostname, user_agent HAVING hosts <= 3
```
## triage-full
<!-- Analyze full infection state -->
```agent target=hunter
cite: required
context:
- triage-early
- registry-run-persistence
- scheduled-task-persistence
- c2-http-metadata
max_iterations: 4
objective: Determine if any host exhibits a complete MATCHBOIL lifecycle from initial
script execution to established persistence.
success_criteria: A final verdict citing specific rows for script, discovery, persistence,
and network metadata.
tools:
- endpoint
- web
```
## route-on-verdict
<!-- Route on verdict -->
if~: "the triage-full verdict is malicious for at least one host" (confidence: high, judge=hunter)
then: → isolate-host
indeterminate: → manual-review
unavailable: → manual-review (blind_spot: internal-wmi-recon)
else: → close-out
## isolate-host
<!-- Isolate infected host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host from the network. Collect the DeviceMonitor directory contents and the config.ini file for forensic analysis before removing the Registry Run keys or scheduled tasks.
```
→ manual-review
## manual-review
<!-- Manual analyst review -->
```manual target=analyst
Review the script content fragments for VBS downloader logic. Verify if the 25-character User-Agent matches the identified hosts. Document the BIOS serial numbers or CPUIDs retrieved via WMI for further threat intelligence mapping.
```
→ close-out
## close-out
<!-- Hunt close out -->
```manual target=analyst
Record the findings. If the Registry Run-key query yielded high-confidence results with low noise, promote it to a standing detection rule.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.