TerminalFix ClickFix Delivery and Automated Reconnaissance
An intruder has used a fake Cloudflare verification lure to trick a user into pasting a PowerShell command, facilitating local directory staging and automated domain discovery.
Based on research by Microsoft 2026-09-20 9 steps · 3 queries T1018 T1053.005 T1059.001 T1087.002 T1115 T1482 T1547.001
Brief
Recent reporting from Microsoft, "TerminalFix campaign deploys a reverse tunnel through multistage intrusion", details a threat that starts with a social engineering lure and ends with a persistent reverse tunnel. The campaign uses ClickFix tactics where a user is tricked into pasting a command to solve a fake Cloudflare verification issue. This hunt focuses on the early and middle stages of this intrusion.
How the Hunt Flows
The first step scopes the environment for specific directory activity. The adversary stages files in a unique ProgramData path. The query identifies hosts where processes run from this folder or establish the path. This provides a focused list of suspicious hosts for deeper investigation.
Next, the hunt splits into two parallel investigative paths. The first path looks for the delivery interaction. It searches script activity for contents matching the fake Cloudflare "not a robot" or "verification" lures. This confirms that a user actually executed the malicious paste.
The second parallel path monitors for automated Active Directory discovery. The intruder often runs a burst of commands like nltest /domain_trusts or net group "domain admins". The hunt baselines this activity to identify clusters of discovery that are rare across the fleet.
Finally, the hunt weighs the evidence. An analyst or automated logic correlates the staging directory, the script lure, and the discovery burst. If a host matches the full chain, the hunt directs the user to isolate the host immediately to prevent further movement.
What the Hunt Cannot See
This hunt relies heavily on PowerShell Script Block Logging (Event ID 4104). If logging is disabled, the pasted command will not appear in telemetry. Additionally, the hunt assumes endpoint visibility into registry and process activity. Unmanaged hosts or those not reporting these events remain hidden from the scoping steps. While the hunt identifies discovery activity, it does not see the encrypted traffic of the reverse tunnel itself if the initial staging is missed.
How to Run the Hunt
This is a hunt.md playbook. You can import it into Huntbase or any hunt.md-aware runtime. Because it uses a multi-stage approach, you should run the scoping query first to identify the primary targets before fanning out into the deeper script and discovery analysis.
In this series
Steps
-
Identify campaign-specific directory activity
Query · scopingFind hosts where files are being executed from or paths are established in the unique ProgramData folder mentioned in the campaign.
reads hb_process_activitysqlSELECT device_hostname, process_path, process_cmd_line, time FROM hb_process_activity WHERE (LOWER(process_path) LIKE '%\programdata\{{programdata_target}}\%' OR LOWER(process_cmd_line) LIKE '%\programdata\{{programdata_target}}\%') AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. A list of hosts running processes from the campaign's staging directory. Silence means this specific path has not been used.
-
Detect ClickFix PowerShell interaction
Query · detection candidateIdentify PowerShell script blocks that match the fake Cloudflare verification lure pasted into the terminal.
reads hb_script_activitysqlSELECT device_hostname, script_content, time FROM hb_script_activity WHERE (instr(LOWER(script_content), 'cloudflare') > 0 AND (instr(LOWER(script_content), 'verification') > 0 OR instr(LOWER(script_content), 'not a robot') > 0)) 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 fake verification messages. Silence proves the lure was not executed via a script-block logging interface.
-
Monitor for automated AD discovery
Query · baselineIdentify hosts performing a burst of domain and infrastructure discovery commands often seen in TerminalFix campaigns.
reads hb_process_activitysqlSELECT device_hostname, process_name, process_cmd_line, COUNT(*) as cmd_count, MIN(time) as first_seen FROM hb_process_activity WHERE (instr(LOWER(process_cmd_line), '/domain_trusts') > 0 OR instr(LOWER(process_cmd_line), 'domain admins') > 0 OR instr(LOWER(process_cmd_line), 'get-aduser') > 0 OR (LOWER(process_name) LIKE '%ping.exe%' AND (LOWER(process_cmd_line) LIKE '%dc%' OR LOWER(process_cmd_line) LIKE '%db%' OR LOWER(process_cmd_line) LIKE '%backup%'))) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY device_hostname, process_name, process_cmd_lineWhat a hit looks like. A cluster of discovery commands on one host that are rare across the fleet. Multiple matches in a short window indicate automated mapping.
-
Weigh campaign evidence
Agent triageCorrelate the directory staging with the social engineering lure and the resulting reconnaissance burst to confirm a TerminalFix infection.
-
Route on verdict
DecisionDirect high-confidence findings to containment and others to manual review.
-
Isolate compromised host
Response actionSever the network connection of a confirmed TerminalFix beachhead to prevent lateral movement.
-
Analyst review
Analyst taskReview findings, tune discovery baselines, and look for signs of the follow-on reverse tunnel.
-
Close out
Analyst taskDocument findings and verify remediation.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Steganographic Payload Extraction T1027.003 |
Not visible | Detection shifted to initial delivery interaction (ClickFix T1115/T1059.001) per design review to prioritize high-fidelity social engineering lures. |
| Redundant Persistence T1547.001 · T1053.005 |
Yes | scoping-programdata-activity |
| Extensive Domain Discovery T1018 · T1087.002 · T1482 |
Yes | reconnaissance-baseline |
| Social Engineering via Fake CAPTCHA T1204.001 |
Out of scope | Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series. |
| Malicious PowerShell Launcher T1059.001 · T1105 |
Out of scope | Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series. |
| DLL Sideloading via LockScreenContentServer T1574.001 |
Out of scope | Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series. |
| Asynchronous File-Watch Command Loop T1059.001 |
Out of scope | Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series. |
| Reverse WebSocket Tunneling T1572 · T1090.003 |
Out of scope | Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series. |
Blind spots
- Needs hb_script_activity with Script Block Logging (ID 4104). Without script block logging, the pasted multi-line PowerShell command will not be visible in telemetry. It would answer whether the lure was executed in a terminal.
- Needs hb_registry_activity (Sysmon) or osquery hive snapshot. If the host is not reporting registry events, the persistence mechanism remains hidden. It would answer whether the Run key exists on unmanaged hosts.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Days of telemetry to examine. |
programdata_target | string | f47f2a8c21c9df4e | Randomized folder name used in the campaign summary. |
scope_hosts | list[host] | — | Hosts identified in the scoping step. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
Source
---
analysis: A single rule for the Run key can be evaded with randomized naming; this
hunt pivots from delivery lures (script content) to local staging (ProgramData)
to discovery bursts, requiring an analyst to weigh the full chain of evidence.
blind_spots:
- id: no-script-logging
question: whether the lure was executed in a terminal
requires: hb_script_activity with Script Block Logging (ID 4104)
risk: Without script block logging, the pasted multi-line PowerShell command will
not be visible in telemetry.
stage: defense-evasion-steganography
- id: endpoint-visibility
question: whether the Run key exists on unmanaged hosts
requires: hb_registry_activity (Sysmon) or osquery hive snapshot
risk: If the host is not reporting registry events, the persistence mechanism remains
hidden.
stage: persistence-mechanisms
coverage:
- blind_spot: no-script-logging
reason: Detection shifted to initial delivery interaction (ClickFix T1115/T1059.001)
per design review to prioritize high-fidelity social engineering lures.
stage: defense-evasion-steganography
status: not_visible
- stage: persistence-mechanisms
status: covered
steps:
- scoping-programdata-activity
- stage: discovery-domain-reconnaissance
status: covered
steps:
- reconnaissance-baseline
- reason: Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel
through multistage intrusion' series.
stage: initial-access-social-engineering
status: out_of_scope
- reason: Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel
through multistage intrusion' series.
stage: execution-powershell-launcher
status: out_of_scope
- reason: Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel
through multistage intrusion' series.
stage: defense-evasion-dll-sideloading
status: out_of_scope
- reason: Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel
through multistage intrusion' series.
stage: command-and-control-asynchronous-shell
status: out_of_scope
- reason: Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel
through multistage intrusion' series.
stage: command-and-control-reverse-tunnel
status: out_of_scope
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: keep-as-periodic-hunt
justification: TerminalFix infections lead to persistent network-level proxy access;
identifying the transition from social engineering to reconnaissance is critical
to stop the intruder before they reach internal databases or domain controllers.
methodology: model-assisted
trigger: intel-report
hypothesis: An intruder has used a fake Cloudflare verification lure to trick a user
into pasting a PowerShell command, facilitating local directory staging and automated
domain discovery.
labels:
- hunt
- attack.t1115
- attack.t1059.001
- attack.t1547.001
- attack.t1053.005
- attack.t1018
- attack.t1087.002
- attack.t1482
name: TerminalFix ClickFix Delivery and Automated Reconnaissance
parameters:
lookback_days:
default: '14'
description: Days of telemetry to examine.
from:
kind: manual
observed: '2026-08-29'
ref: standard-retention
type: number
programdata_target:
default: f47f2a8c21c9df4e
description: Randomized folder name used in the campaign summary.
from:
kind: article
observed: '2026-08-29'
ref: msrc-blog
type: string
scope_hosts:
default: []
description: Hosts identified in the scoping step.
from:
kind: manual
observed: '2026-08-29'
ref: analyst-input
type: list[host]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.microsoft.com/en-us/security/blog/2026/08/28/terminalfix-campaign-deploys-reverse-tunnel-through-multistage-intrusion/
gates:
- dry-run
- lint
model: hb_google/gemini-3-flash-preview
rationale: Start with user workstations; focus on hosts with activity in the randomized
ProgramData directory or those running nltest and net group commands.
references:
- name: 'MSRC Blog: TerminalFix campaign deploys a reverse tunnel through multistage
intrusion'
url: https://www.microsoft.com/en-us/security/blog/2026/08/28/terminalfix-campaign-deploys-reverse-tunnel-through-multistage-intrusion/
related:
- hunt: terminalfix-tunnel-detection
reason: This hunt targets the delivery and discovery phases; a sibling hunt targets
the Python reverse tunnel deployment.
relation: follows
scenario:
stages:
- name: Social Engineering via Fake CAPTCHA
observables:
- Cloudflare Turnstile verification overlay
- Verification command copied to clipboard
- Instructions to open Windows Terminal or PowerShell
- Fake Cloudflare-themed terminal output messages
slug: initial-access-social-engineering
tactic: initial-access
techniques:
- T1204.001
- name: Malicious PowerShell Launcher
observables:
- C:\ProgramData\f47f2a8c21c9df4e
- 1.bat
- ZIP archive download with custom User-Agent
- 'I am not a robot - Cloudflare ID: f47f2a8c21c9df4e'
slug: execution-powershell-launcher
tactic: execution
techniques:
- T1059.001
- T1105
- name: DLL Sideloading via LockScreenContentServer
observables:
- LockScreenContentServer.exe
- dui70.dll (unsigned, forged timestamp 2104)
- LockScreenContentServer.exe loading dui70.dll from ProgramData
slug: defense-evasion-dll-sideloading
tactic: defense-evasion
techniques:
- T1574.001
- name: Steganographic Payload Extraction
observables:
- p1.png
- p2.png
- p3.png
- gitnow.dev
- Extract-RawFileFromImage PowerShell function
- Reassembling DLL fragments from PNG pixel data
slug: defense-evasion-steganography
tactic: defense-evasion
techniques:
- T1027.003
- name: Redundant Persistence
observables:
- LockScreenContentServer_MuODG5yBM
- 'Registry Run Key: HKCU\Software\Microsoft\Windows\CurrentVersion\Run'
- Scheduled Task running every 60 minutes
- attrib +h +s folder hiding on C:\ProgramData subfolders
slug: persistence-mechanisms
tactic: persistence
techniques:
- T1547.001
- T1053.005
- name: Extensive Domain Discovery
observables:
- nltest /domain_trusts
- net group "domain admins" /domain
- get-aduser
- get-adcomputer
- Ping sweeps of dc, db, backup, gateway, mail servers
slug: discovery-domain-reconnaissance
tactic: discovery
techniques:
- T1018
- T1087.002
- T1482
- name: Asynchronous File-Watch Command Loop
observables:
- PowerShell file-watch loop monitoring text files
- Invoke-Expression (IEX) on watched file content
- Command output written to disk files
slug: command-and-control-asynchronous-shell
tactic: command-and-control
techniques:
- T1059.001
- name: Reverse WebSocket Tunneling
observables:
- pythonw.exe
- client.py
- gitnow.dev:443
- Reverse WebSocket tunnel providing SOCKS proxy access
slug: command-and-control-reverse-tunnel
tactic: command-and-control
techniques:
- T1572
- T1090.003
summary: The TerminalFix campaign employs fake Cloudflare CAPTCHA prompts to trick
users into executing malicious PowerShell commands that initiate a multi-stage
infection. The attack leverages DLL sideloading and steganography to deploy a
persistent Python-based reverse tunnel, enabling attackers to conduct extensive
Active Directory reconnaissance and maintain encrypted SOCKS-style proxy access
to the victim's internal network.
series:
index: 2
slug: terminalfix-campaign-deploys-a-reverse-tunnel-through-multistage-intrusion
title: TerminalFix campaign deploys a reverse tunnel through multistage intrusion
total: 3
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
tlp: clear
type: investigation
---
# TerminalFix ClickFix Delivery and Automated Reconnaissance
This hunt targets the early-to-mid stages of a TerminalFix intrusion, focusing on the transition from social engineering (ClickFix) to environment mapping. It identifies the interaction where clipboard-pasted PowerShell lures are executed, staged in unique ProgramData paths, and followed by a rapid burst of Active Directory and infrastructure discovery commands. By correlating these distinct behavioural surfaces, the hunt identifies compromised hosts acting as network pivot points before the deployment of reverse tunnels.
## scoping-programdata-activity
<!-- Identify campaign-specific directory activity -->
Find hosts where files are being executed from or paths are established in the unique ProgramData folder mentioned in the campaign.
```sqlite target=endpoint role=scoping params=(lookback_days=lookback_days, programdata_target=programdata_target)
~~~yaml
expected: A list of hosts running processes from the campaign's staging directory.
Silence means this specific path has not been used.
reads:
- device_hostname
- process_path
- process_cmd_line
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, process_path, process_cmd_line, time FROM hb_process_activity WHERE (LOWER(process_path) LIKE '%\programdata\{{programdata_target}}\%' OR LOWER(process_cmd_line) LIKE '%\programdata\{{programdata_target}}\%') AND time >= datetime('now', '-{{lookback_days}} days')
```
## investigation-fan-out
<!-- Parallel investigation of delivery and discovery -->
parallel:
- → clickfix-delivery-interaction
- → reconnaissance-baseline
join: → triage-agent
## clickfix-delivery-interaction
<!-- Detect ClickFix PowerShell interaction -->
Identify PowerShell script blocks that match the fake Cloudflare verification lure pasted into the terminal.
```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Script blocks containing fake verification messages. Silence proves the
lure was not executed via a script-block logging interface.
reads:
- device_hostname
- script_content
- time
silence: not_evidence_of_absence
source: hb_script_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, script_content, time FROM hb_script_activity WHERE (instr(LOWER(script_content), 'cloudflare') > 0 AND (instr(LOWER(script_content), 'verification') > 0 OR instr(LOWER(script_content), 'not a robot') > 0)) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## reconnaissance-baseline
<!-- Monitor for automated AD discovery -->
Identify hosts performing a burst of domain and infrastructure discovery commands often seen in TerminalFix campaigns.
```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: A cluster of discovery commands on one host that are rare across the fleet.
Multiple matches in a short window indicate automated mapping.
prevalence:
by: device_hostname
key:
- process_cmd_line
rare_below: 5
reads:
- device_hostname
- process_name
- process_cmd_line
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, process_name, process_cmd_line, COUNT(*) as cmd_count, MIN(time) as first_seen FROM hb_process_activity WHERE (instr(LOWER(process_cmd_line), '/domain_trusts') > 0 OR instr(LOWER(process_cmd_line), 'domain admins') > 0 OR instr(LOWER(process_cmd_line), 'get-aduser') > 0 OR (LOWER(process_name) LIKE '%ping.exe%' AND (LOWER(process_cmd_line) LIKE '%dc%' OR LOWER(process_cmd_line) LIKE '%db%' OR LOWER(process_cmd_line) LIKE '%backup%'))) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY device_hostname, process_name, process_cmd_line
```
## triage-agent
<!-- Weigh campaign evidence -->
```agent target=hunter
cite: required
context:
- scoping-programdata-activity
- clickfix-delivery-interaction
- reconnaissance-baseline
max_iterations: 4
objective: Determine if the host shows a complete chain from ClickFix interaction
to campaign-specific staging and automated environment discovery.
success_criteria: A verdict of malicious | suspicious | benign per host with citations
of script content matching the Cloudflare lure.
tools:
- endpoint
```
## route-verdict
<!-- Route on verdict -->
if~: "the triage verdict is malicious for at least one host" (confidence: high, judge=hunter)
then: → isolate-host
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: no-script-logging)
else: → analyst-review
## isolate-host
<!-- Isolate compromised host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host and collect the contents of the ProgramData campaign directory for forensic analysis.
```
→ analyst-review
## analyst-review
<!-- Analyst review -->
```manual target=analyst
Verify the cited script blocks. Pivot to network traffic for connections to gitnow.dev or unexpected pythonw.exe activity on port 443.
```
→ close-out
## close-out
<!-- Close out -->
```manual target=analyst
Ensure the registry persistence keys and scheduled tasks are removed. Record any new infrastructure pings identified in the triage process.
```
→ 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.