Most initial access vectors are temporary. A phish yields a web shell that gets recycled, or a stolen session token expires after a few hours. To turn a fleeting foothold into operational stability, an attacker must drop anchor. They rarely need custom rootkits or obscure zero-days to do this; Windows provides all the infrastructure required out of the box.
If you are waiting for your EDR to throw a high-severity alert on persistence, you are already losing. Attackers disguise their entry points inside legitimate administrative mechanisms, betting that security analysts won’t distinguish between a legitimate updater and a malicious launcher. To find them, you need to know exactly where these artifacts live on disk, how they modify the registry, and how to write hunt queries that surface anomalies across your fleet.
Registry Run Keys: The Lowest-Hanging Fruit
Registry Run keys remain the most common persistence vector because they require minimal effort and survive system reboots. When a user logs in, the Windows Interactive Logon Process (winlogon.exe) checks specific registry keys and executes any binary paths defined within their value data.
Attackers typically target two main locations:
- Current User (HKCU):
HKCU\Software\Microsoft\Windows\CurrentVersion\Run(does not require local administrative privileges). - Local Machine (HKLM):
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run(requires elevated privileges, executes for every user).
To simulate this mechanism, an attacker drops an executable into a readable location and writes a string value to the HKCU Run key:
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "SecurityUpdate" /t REG_SZ /d "C:\Users\Public\Libraries\update.exe" /f
On disk, the executable sits in C:\Users\Public\Libraries\. In the registry, a new value SecurityUpdate is created under the targeted hive.
To hunt for new or modified Run keys across Microsoft Defender for Endpoint (KQL), run this query targeting suspicious executable paths and unquoted binary strings:
DeviceRegistryEvents
| where RegistryKey has_any (
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
@"SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
)
| where ActionType in ("RegistryValueSet", "RegistryKeyCreated")
| where RegistryValueData matches regex @"(?i)(c:\\users\\public|c:\\programdata|c:\\windows\\temp|powershell|cmd\.exe|wscript|cscript)"
| project Timestamp, DeviceName, AccountName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFolderPath
Scheduled Tasks: Hiding in System32\Tasks
Scheduled tasks allow execution triggered by time intervals, system events, or user actions (such as workstation unlock). Because legitimate software routinely installs scheduled tasks, malicious tasks blend in easily.
When a scheduled task is created, Windows writes two distinct artifacts:
- Disk: An XML definition file located in
C:\Windows\System32\Tasks\(or subfolders likeC:\Windows\System32\Tasks\Microsoft\Windows\). - Registry: Metadata written to
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\andHKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\.
An attacker can create a persistence task via schtasks.exe:
schtasks /create /tn "Updater\Maintenance" /tr "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -Enc aT...==" /sc daily /st 09:00 /ru SYSTEM
Notice the task path: subfolder creation under TaskCache\Tree is a common tactic to imitate valid Microsoft paths.
To catch scheduled task creation in KQL, monitor both command-line invocations of schtasks.exe and Security Event ID 4698 (A scheduled task was created):
DeviceProcessEvents
| where ProcessCommandLine has "schtasks" and ProcessCommandLine has "/create"
| where ProcessCommandLine matches regex @"(?i)(powershell|cmd|cscript|wscript|mshta|rundll32|regsvr32|c:\\users\\public|c:\\windows\\temp)"
| project Timestamp, DeviceName, AccountName, FolderPath, ProcessCommandLine, InitiatingProcessParentFileName
If you collect Windows Security Logs via Microsoft Sentinel, query Event ID 4698 directly to extract the embedded XML payload:
SecurityEvent
| where EventID == 4698
| extend EventDataXML = parse_xml(EventData)
| extend TaskName = tostring(EventDataXML.EventData.Data[0].["#text"])
| extend Command = tostring(EventDataXML.EventData.Data[5].["#text"])
| where Command matches regex @"(?i)(powershell|cmd|cscript|wscript|c:\\users\\public|c:\\windows\\temp)"
| project TimeGenerated, Computer, SubjectUserName, TaskName, Command
Service Creation: Escalate and Persist in One Move
Windows Services are managed by the Service Control Manager (services.exe) and typically run with NT AUTHORITY\SYSTEM privileges. Creating a malicious service provides both persistence and privilege escalation.
When a service is registered:
- Registry: A dedicated subkey is created at
HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>. - Key Values: The path to the executable is stored in the
ImagePathregistry value.
An attacker creates a service pointing to a staged payload:
sc.exe create "WinAppSvc" binPath= "C:\Windows\Temp\svc_payload.exe" start= auto
If the binary is not a valid Windows Service binary (i.e., it lacks the ServiceMain callback function), services.exe will terminate the process after a timeout period, but the executable code will have already run during startup.
To detect service installation, rely on System Event ID 7045 (“A service was installed in the system”) or monitor telemetry from service creation registry keys:
DeviceEvents
| where ActionType == "ServiceInstalled"
| extend ServiceName = tostring(AdditionalFields.ServiceName)
| extend ServiceFolderPath = tostring(AdditionalFields.ImagePath)
| extend ServiceStartType = tostring(AdditionalFields.StartType)
| where ServiceFolderPath matches regex @"(?i)(c:\\users\\public|c:\\windows\\temp|c:\\programdata|powershell|cmd\.exe)"
| project Timestamp, DeviceName, AccountName, ServiceName, ServiceFolderPath, ServiceStartType
WMI Event Subscriptions: Living Off the Repository
WMI (Windows Management Instrumentation) event subscriptions are among the stealthiest persistence mechanisms on Windows. They run entirely in memory inside the WMI repository, leaving no standalone binary on disk if configured to execute inline scripts (e.g., VBScript or PowerShell via a ActiveScriptEventConsumer).
A WMI persistence mechanism requires three distinct objects within the root\subscription namespace:
__EventFilter: Defines the trigger (e.g., system uptime reaching 300 seconds, or a user logging in).__EventConsumer: Defines the action (e.g.,CommandLineEventConsumerrunning a command).__FilterToConsumerBinding: Binds the filter to the consumer.
All three objects are written directly to the WMI repository database file on disk: C:\Windows\System32\wbem\Repository\OBJECTS.DATA.
Because attackers rarely use native wmic.exe commands anymore, they assemble WMI subscriptions directly via PowerShell:
$Filter = Set-CimInstance -Namespace "root\subscription" -ClassName __EventFilter -Property @{
Name = "SystemCheckFilter"
EventNamespace = "root\cimv2"
QueryLanguage = "WQL"
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
}
$Consumer = Set-CimInstance -Namespace "root\subscription" -ClassName CommandLineEventConsumer -Property @{
Name = "SystemCheckConsumer"
CommandLineTemplate = "powershell.exe -NoP -NonI -W Hidden -Enc aT...=="
}
Set-CimInstance -Namespace "root\subscription" -ClassName __FilterToConsumerBinding -Property @{
Filter = [ref]$Filter
Consumer = [ref]$Consumer
}
To audit WMI persistence locally using PowerShell, enumerate all bindings in the root\subscription namespace:
Get-CimInstance -Namespace root\subscription -ClassName __FilterToConsumerBinding | Select-Object Filter, Consumer
For fleet-wide hunting with Defender for Endpoint telemetry (Sysmon Events 19, 20, and 21 map directly to WMI events):
DeviceEvents
| where ActionType in ("WmiEventFilterCreated", "WmiEventConsumerCreated", "WmiEventConsumerToFilterCreated")
| extend EventDetail = parse_json(AdditionalFields)
| project Timestamp, DeviceName, ActionType, AccountName, EventDetail
Building a Unified Persistence Baseline
Individual queries catch individual artifacts, but real-world hunting requires baselining. Legitimate applications install tasks, run keys, and services constantly. Your goal is not to eliminate every result from these queries, but to establish an operational baseline.
Focus your hunting workflows on these three core indicators across all four persistence vectors:
- Suspicious File Paths: Executables or scripts executing out of writeable non-standard directories like
C:\Users\Public\,C:\ProgramData\, orC:\Windows\Temp\. - Encoded Command Lines: Powershell invocations utilizing
-EncodedCommand(-e,-enc) or-WindowStyle Hidden. - Parent-Process Anomaly: Legitimate services spawning interactive shells, or
wmiprvse.exelaunching unexpected child processes.
Incorporate these queries into scheduled threat hunting sweeps rather than relying solely on automated detection rules. By searching raw telemetry for these persistence artifacts, you force attackers to burn their footholds long before they reach their final objectives.
Related content
Demystifying SeImpersonatePrivilege: Token Mechanics and Service Hardening
ResearchBeyond URL Fetching: How IMDSv1 Turns SSRF into Full Cloud Compromise
ResearchBreaking Active Directory Attack Paths: From SharpHound Data to Choke-Point Remediation
ResearchBuilding a High-Signal External Recon Pipeline That Actually Scales
Want a second set of eyes on your security posture?
Let's talk about where your real exposure is.
Book an advisory call