Detecting the Most Popular MITRE Persistence Method – Registry Run Keys / Startup Folder

by Jul 29, 2025

Persistence is a cornerstone tactic for both threat actors and red‑teamers, allowing them to cling to a compromised system even after reboots, credential resets, or other disruptions that might otherwise cut them off. MITRE ATT&CK places these activities in tactic TA0003, listing 23 separate methods that translate into dozens of concrete techniques.

This post zeroes in on the subset that truly shows up in the wild. Our telemetry reveals that roughly fifteen techniques account for about 80 % of all persistence techniques used in the wild, and many are staples of well‑documented threat groups. By homing in on those, we avoid getting lost in exotic fringe cases and deliver guidance that directly improves day‑to‑day defenses. All examples target Windows environments so the recommendations remain practical.

Rather than skimming every persistence possibility, we will dissect the most common method, persistence via registry run keys, and their many variants, because each one offers attackers a wealth of footholds, and defenders an equally rich detection surface. Along the way, we’ll demonstrate hunting with Nextron’s THOR paired with Timesketch, giving you a workflow you can drop straight into your current standards.

By the end, you will understand the key persistence mechanisms that matter most, possess a solid framework for detecting them at scale, and know how to spot the tell‑tale traces manually during threat‑hunting or incident‑response engagements.

Most Popular Persistence Methods

As mentioned earlier, we aim to focus on the most used persistence techniques, rather than getting lost in rarely observed or exotic methods. While we could rely on personal experience, we’ve chosen to base this selection on actual data.

To achieve this, we load the MITRE ATT&CK Enterprise dataset and map each persistence technique to the threat groups that use it. This enables us to analyze which methods are most prevalent objectively.

First, we need to set up our working environment and install the required MITRE ATT&CK Python library:

python –m venv .venv
venv\Scripts\Activate.ps1
git clone https://github.com/mitre/cti
pip install mitreattack-python

The relevant dataset will be in cti/enterprise-attack/enterprise-attack.json. Once the environment is ready, we can write a quick proof-of-concept script to extract all persistence techniques and count how many threat groups use each one.

from mitreattack.stix20 import MitreAttackData

def main():
    mitre_attack_data = MitreAttackData("./cti/enterprise-attack/enterprise-attack.json")

    # Get all groups related to techniques
    groups_using_techniques = mitre_attack_data.get_all_groups_using_all_techniques()

    for stix_id, groups in groups_using_techniques.items():
        stix_data = mitre_attack_data.get_object_by_stix_id(stix_id)

        # Skip non-persistence techniques
        is_persistence_technique = "persistence" in [phase.get("phase_name", "") for phase in stix_data.get("kill_chain_phases", "")]
        if not is_persistence_technique:
            continue
        
        # Get ATT&CK ID
        external_references = stix_data.get("external_references", "")
        if external_references:
            attack_source = external_references[0]
            if attack_source.get("external_id") and attack_source.get("source_name") == "mitre-attack":
                external_id = attack_source["external_id"]
            else:
                continue
        else:
            continue

        technique_name = stix_data.get("name", "")
        group_count = len(groups)

        print(f"{external_id} - {technique_name} used by {group_count} groups")


if __name__ == "__main__":
    main()

The script returns results like this:

PS C:\nextron > python .\mitre.py
T1136.001 - Local Account used by 14 groups
T1133 - External Remote Services used by 27 groups
T1574.001 - DLL used by 32 groups
T1543.003 - Windows Service used by 26 groups
T1547.001 - Registry Run Keys / Startup Folder used by 54 groups
T1078 - Valid Accounts used by 43 groups
T1112 - Modify Registry used by 29 groups
T1137.001 - Office Template Macros used by 1 groups
T1505.003 - Web Shell used by 32 groups
T1556.009 - Conditional Access Policies used by 1 groups
T1078.001 - Default Accounts used by 4 groups
T1053.005 - Scheduled Task used by 53 groups
T1574.012 - COR_PROFILER used by 1 groups
T1098.004 - SSH Authorized Keys used by 3 groups
T1136 - Create Account used by 4 groups
T1574.006 - Dynamic Linker Hijacking used by 3 groups
T1053.002 - At used by 3 groups
T1078.003 - Local Accounts used by 13 groups
T1546.013 - PowerShell Profile used by 1 groups
T1546.010 - AppInit DLLs used by 1 groups
T1197 - BITS Jobs used by 5 groups
T1546.003 - Windows Management Instrumentation Event Subscription used by 10 groups
[…]

This provides a quick overview of the persistence techniques frequently used by known threat actors.

To make the results easier to understand, we upgraded our script to also generate a bar chart and a pie chart that visualize the distribution of these techniques. We focus on the top 15 techniques and categorize the remaining ones under an “Other” category to maintain clarity.

from mitreattack.stix20 import MitreAttackData
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

def main():
    mitre_attack_data = MitreAttackData("./cti/enterprise-attack/enterprise-attack.json")

    # Get all groups related to techniques
    groups_using_techniques = mitre_attack_data.get_all_groups_using_all_techniques()
    results = []

    for stix_id, groups in groups_using_techniques.items():
        stix_data = mitre_attack_data.get_object_by_stix_id(stix_id)

        # Skip non-persistence techniques
        is_persistence_technique = "persistence" in [phase.get("phase_name", "") for phase in stix_data.get("kill_chain_phases", "")]
        if not is_persistence_technique:
            continue
        
        # Get ATT&CK ID
        external_references = stix_data.get("external_references", "")
        if external_references:
            attack_source = external_references[0]
            if attack_source.get("external_id") and attack_source.get("source_name") == "mitre-attack":
                external_id = attack_source["external_id"]
            else:
                continue
        else:
            continue

        technique_name = stix_data.get("name", "")
        group_count = len(groups)

        results.append({
            'technique': technique_name,
            'attack_id': external_id,
            'group_count': group_count
        })

    results = sorted(results, key=lambda x: x['group_count'], reverse=True)

    top_n = 15
    top_results = results[:top_n]
    other_results = results[top_n:]

    technique_labels = [f"{item['attack_id']} {item['technique']}" for item in top_results]
    group_counts = [item['group_count'] for item in top_results]

    if other_results:
        other_count = sum(item['group_count'] for item in other_results)
        technique_labels.append("Other")
        group_counts.append(other_count)

    # Gradient colors
    colors = plt.cm.magma(np.linspace(0.2, 0.8, len(technique_labels)))

    # Set dark mode
    background_color = '#232136'
    plt.rcParams['figure.facecolor'] = background_color
    plt.rcParams['axes.facecolor'] = background_color

    # Horizontal bar chart
    fig, ax = plt.subplots(figsize=(14, 0.6 * len(technique_labels) + 2))
    bars = ax.barh(technique_labels, group_counts, color=colors, edgecolor='white')

    ax.set_xlabel('Number of Groups Using Technique', fontsize=14, color='white')
    ax.set_title('Top Persistence Techniques Used by Threat Actors (MITRE ATT&CK)', fontsize=16, color='white', pad=20)
    ax.invert_yaxis()
    ax.tick_params(colors='white', labelsize=12)

    # Add labels
    for bar in bars:
        ax.text(bar.get_width() + 0.5, bar.get_y() + bar.get_height() / 2,
                f'{int(bar.get_width())}', va='center', fontsize=11, color='white')

    plt.grid(axis='x', linestyle='--', alpha=0.3)
    plt.tight_layout()
    plt.show()

    # Pie chart
    fig, ax = plt.subplots(figsize=(12, 12))
    wedges, texts, autotexts = ax.pie(
        group_counts,
        colors=plt.cm.plasma(np.linspace(0.2, 0.8, len(technique_labels))),
        autopct='%1.1f%%',
        startangle=140,
        pctdistance=0.85,
        wedgeprops={'linewidth': 1, 'edgecolor': 'white'}
    )

    # Style text
    for text in texts:
        text.set_color('white')
        text.set_fontsize(10)
    for autotext in autotexts:
        autotext.set_color('white')
        autotext.set_fontsize(10)

    # Add leader lines and labels
    for i, wedge in enumerate(wedges):
        ang = (wedge.theta2 + wedge.theta1) / 2
        x = np.cos(np.deg2rad(ang))
        y = np.sin(np.deg2rad(ang))
        label_x = 1.35 * x
        label_y = 1.35 * y
        ax.text(label_x, label_y, technique_labels[i], ha='center', va='center', fontsize=9, color='white', wrap=True)
        line_x = 0.92 * x
        line_y = 0.92 * y
        ax.plot([line_x, label_x], [line_y, label_y], color='white', linewidth=0.8)

    ax.set_title('Persistence Technique Usage Distribution (MITRE ATT&CK)', fontsize=16, color='white', pad=20)
    plt.axis('equal')
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    main()

The bar chart illustrates the most popular persistence techniques by displaying the number of threat groups employing each one.

Top persistence techniques used by threat actors

The pie chart visualizes the relative distribution of these techniques, with clear leader lines connecting each slice to its label for easy readability.

Persistence technique usage distribution

Based on our analysis of the MITRE ATT&CK Enterprise dataset, we found that a small number of persistence techniques dominate the landscape. When we mapped each technique to the threat groups that use it, something stood out: the 15 most common persistence techniques account for almost 80% of all group associations MITRE has recorded so far.

To put that into perspective, Registry Run Keys / Startup Folder (T1547.001) is linked to 54 threat actor groups, Scheduled Task / Job (T1053.005) appears in campaigns attributed to 53 groups and Valid Accounts (T1078) can be attributes to 43 groups – And these three techniques on their own already cover almost one third of all tracked persistence method occurrences. These techniques are not just theoretically common; they repeatedly show up in real-world intrusions.

Certainly, ATT&CK coverage is not exhaustive. Some techniques used in the wild may never be publicly reported, and some operations will always remain undocumented. Still, these numbers highlight a fundamental yet critical priority. Suppose your detection capabilities can reliably catch high-frequency techniques, such as Run key modifications or rogue scheduled tasks. In that case, you are already covering a significant share of known attacker behavior and forcing threat actors to invest time in finding alternative attack paths.

With this in mind, we’ll now introduce the most widely used persistence technique: Registry Run Keys / Startup Folders.

T1547.001 – Registry Run Keys / Startup Folder

The Registry Run Keys / Startup Folder technique, part of the broader MITRE ATT&CK technique “Boot or Logon Autostart Execution”, is one of the most widely used and reliable persistence methods on Windows systems. By adding specific entries to the Windows Registry or placing executables in startup folders, threat actors can ensure that their payloads will automatically run when the system boots or when a user logs in.

This technique is especially popular because it does not require privilege escalation when the attacker targets user-level keys. It is compatible across all modern Windows versions and is often overlooked in environments with limited registry monitoring or incomplete autorun coverage. Establishing persistence through this method is also straightforward; it can be done manually without specialized tools, though a variety of tools exist that make the process even easier for attackers.

It is worth noting, however, that there is some ambiguity about what exactly qualifies as a “Run Key” within this context. MITRE’s documentation lists a broad set of registry keys and locations that can be abused to execute code at startup or logon.

However, not all of them are “run keys” in the narrow sense. For example, the HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute value triggers filesystem checks on boot but is not typically classified as a run key. Similarly, other keys referenced by security experts, including Mark Russinovich, extend beyond the run key definition. Microsoft’s documentation currently defines just four primary run keys that directly cause programs to execute when a user logs in. At the same time, MITRE includes additional keys that run programs during system boot or logon; not all of these fall under the strict definition of run keys. Beyond that, dozens of other registry locations support event‑based persistence (the screensaver hook is a classic example), broadening the landscape even further.

For clarity and focus, this chapter examines only the registry locations that trigger execution at logon or boot. Event‑driven keys and similar outliers are left aside, even though the detection logic is largely the same. We will refer to the selected paths collectively as autostart registry locations; some are bona‑fide run keys, others are not, but every one of them delivers the same outcome, a guaranteed foothold the next time Windows starts or a user signs including so called ASEP (Auto-Start Extensibility Points) registry locations:

Per-user ASEPs

  • HKCU\Software\Microsoft\Windows\CurrentVersion\Run
  • HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
  • HKCU\Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\Run
  • HKCU\Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\RunOnce
  • HKCU\Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\RunonceEx
  • HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Load
  • HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Run
  • HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
  • HKCU\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Run

Service startup during boot

  • HKCU\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce
  • HKLM\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce
  • HKCU\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce
  • HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices
  • HKCU\Software\Microsoft\Windows\CurrentVersion\RunServices

Systemwide ASEPs

  • HKLM\Software\Microsoft\Windows\CurrentVersion\Run
  • HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce
  • HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnceEx
  • HKLM\Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\Run
  • HKLM\Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\RunOnce
  • HKLM\Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\RunonceEx
  • HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
  • HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce
  • HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnceEx
  • Group Policy Controlled
  • HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
  • HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run

Boot execute

  • HKLM\System\CurrentControlSet\Control\ServiceControlManagerExtension
  • HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute
  • HKLM\System\CurrentControlSet\Control\Session Manager\Execute
  • HKLM\System\CurrentControlSet\Control\Session Manager\S0InitialCommand
  • HKLM\System\CurrentControlSet\Control\Session Manager\SetupExecute

Startup folder location

  • HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
  • HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
  • HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
  • HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders

In addition to registry-based persistence, executables and shortcuts placed in the Windows startup folders are automatically executed when a user logs on. These folders include:

  • %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup (Current User)
  • C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup (All Users)

ASEP Registry Run Key Usage Detection

Every persistence technique leaves traces, and the classic Run key method is no exception. Although an infection chain begins elsewhere, malspam, Living-off-the-land execution, credential theft, good defenders still want to spot the moment a payload becomes autostart-capable. Below is a tiered approach that combines registry auditing, process telemetry, and native Windows channels, making it difficult for an attacker to remain invisible.

Registry Modification

Windows records no registry edits by default, so defenders must enable either native object auditing or Sysmon to track these changes. Object auditing is a native Windows capability: Enable Audit Registry under secpol.msc > Advanced Audit Policy > Object Access.

Enable registry auditing

Auditing alone is not enough; the key also needs a SACL (System Access Control List). For demonstration purposes, a broad SACL on HKLM\Software is acceptable; however, in production, it should be scoped narrowly, as each additional subkey multiplies the log volume. Right-click the key, choose Permissions > Advanced > Auditing, add the principal (for example, Everyone), and tick at least Set Value and Create Subkey.

Registry SACL creation for all users

With auditing active, write a test value and perform a logoff/logon to force evaluation:

C:\Users\azureuser> reg add HKLM\Software\Microsoft\Windows\CurrentVersion\Run /v TestRunKey /d "calc.exe" /f
C:\Users\azureuser> # Reauthenticate to trigger the execution
C:\Users\azureuser> shutdown /l

The logon triggers Security 4663, followed by 4657, which records the value name, old and new data, calling process, and user SID.

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" />
		<EventID>4657</EventID>
		<Version>0</Version>
		<Level>0</Level>
		<Task>12801</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8020000000000000</Keywords>
		<TimeCreated SystemTime="2025-06-27T12:34:08.9493097Z" />
		<EventRecordID>2029333</EventRecordID>
		<Correlation />
		<Execution ProcessID="4" ThreadID="16740" />
		<Channel>Security</Channel>
		<Computer>redacted</Computer>
		<Security />
	</System>
	<EventData>
		<Data Name="SubjectUserSid">S-1-5-21-...-500</Data>
		<Data Name="SubjectUserName">azureuser</Data>
		<Data Name="SubjectDomainName">redacted</Data>
		<Data Name="SubjectLogonId">0x9ab8ad1</Data>
		<Data Name="ObjectName">\REGISTRY\USER\S-1-5-21-...-500\Software\Microsoft\Windows\CurrentVersion\Run</Data>
		<Data Name="ObjectValueName">TestRunKey</Data>
		<Data Name="HandleId">0xb8</Data>
		<Data Name="OperationType">%%1904</Data>
		<Data Name="OldValueType">-</Data>
		<Data Name="OldValue">-</Data>
		<Data Name="NewValueType">%%1873</Data>
		<Data Name="NewValue">calc.exe</Data>
		<Data Name="ProcessId">0x278c</Data>
		<Data Name="ProcessName">C:\Windows\System32\reg.exe</Data>
	</EventData>
</Event>

Sysmon achieves the same result with less GPO work. When its configuration enables registry logging, each write generates an Event ID 13 Registry value set, including the process ID, registry value, and registry key.

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385f-c22a-43e0-bf4c-06f5698ffbd9}" />
		<EventID>13</EventID>
		<Version>2</Version>
		<Level>4</Level>
		<Task>13</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8000000000000000</Keywords>
		<TimeCreated SystemTime="2025-06-27T12:29:32.6a601717Z" />
		<EventRecordID>1163</EventRecordID>
		<Correlation />
		<Execution ProcessID="2900" ThreadID="16148" />
		<Channel>Microsoft-Windows-Sysmon/Operational</Channel>
		<Computer>redacted</Computer>
		<Security UserID="S-1-5-18" />
	</System>
	<EventData>
		<Data Name="RuleName">T1060,RunKey</Data>
		<Data Name="EventType">SetValue</Data>
		<Data Name="UtcTime">2025-06-27 12:29:32.658</Data>
		<Data Name="ProcessGuid">{51e2ac6f-8eac-685e-202b-000000001300}</Data>
		<Data Name="ProcessId">19976</Data>
		<Data Name="Image">C:\Windows\system32\reg.exe</Data>
		<Data Name="TargetObject">HKU\S-1-5-21-...-500\Software\Microsoft\Windows\CurrentVersion\Run\TestRunKey</Data>
		<Data Name="Details">calc.exe</Data>
		<Data Name="User">redacted\azureuser</Data>
	</EventData>
</Event>

Process Execution

Run key payloads leave traces in the usual execution artefacts such as Prefetch entries or SRUM ((System Resource Usage Monitor) records. Yet those sources rarely reveal how the binary started; they blur together auto‑starts (ASEPs) and all other program executions. A more precise strategy is two‑fold: first, monitor for registry‑editing utilities like reg.exe (or any process that writes to the relevant hives); second, watch for the moment the Run‑key value is invoked and the payload spawns. Together, these signatures capture the full lifecycle—both the registry change that implants persistence and the subsequent execution of the malicious code.

For example, when the Microsoft-Windows-Shell-Core/Operational channel is enabled, Windows logs Event 9707 whenever payload is launched from an ASEP Run key (system-wide or per-user) and Event 9708 when that launch completes successfully.

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Shell-Core" Guid="{30336ed4-e327-447c-9de0-51b652c86108}" />
		<EventID>9707</EventID>
		<Version>0</Version>
		<Level>4</Level>
		<Task>9707</Task>
		<Opcode>1</Opcode>
		<Keywords>0x2000000004010000</Keywords>
		<TimeCreated SystemTime="2025-06-27T09:08:14.5701197Z" />
		<EventRecordID>7866</EventRecordID>
		<Correlation />
		<Execution ProcessID="10548" ThreadID="18824" />
		<Channel>Microsoft-Windows-Shell-Core/Operational</Channel>
		<Computer>redacted</Computer>
		<Security UserID="S-1-5-21-...-500" />
	</System>
	<EventData>
		<Data Name="Command">calc.exe</Data>
	</EventData>
</Event>

But that is only part of the picture. Instead of waiting for the persistence payload to execute, we can catch the registry‑modifying process itself. When process‑creation auditing is enabled, Windows Security Event ID 4688 logs every launch of reg.exe along with its full command line. By alerting on invocations whose arguments point to ASEP run‑key locations or Startup‑folder paths, we detect the tool that implants the beacon—rather than the beacon’s C2 activity after it is already in place.

Process creation auditing enabled

The reg.exe call that writes to the system-wide Run key is recorded as Security event ID 4688 (process creation) followed by ID 4689 (process termination); with Sysmon enabled, the same action appears as Event ID 1.

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" />
		<EventID>4688</EventID>
		<Version>2</Version>
		<Level>0</Level>
		<Task>13312</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8020000000000000</Keywords>
		<TimeCreated SystemTime="2025-06-27T12:34:08.9426932Z" />
		<EventRecordID>2029331</EventRecordID>
		<Correlation />
		<Execution ProcessID="4" ThreadID="16740" />
		<Channel>Security</Channel>
		<Computer>redacted</Computer>
		<Security />
	</System>
	<EventData>
		<Data Name="SubjectUserSid">S-1-5-21-...-500</Data>
		<Data Name="SubjectUserName">azureuser</Data>
		<Data Name="SubjectDomainName">redacted</Data>
		<Data Name="SubjectLogonId">0x9ab8ad1</Data>
		<Data Name="NewProcessId">0x278c</Data>
		<Data Name="NewProcessName">C:\Windows\System32\reg.exe</Data>
		<Data Name="TokenElevationType">%%1936</Data>
		<Data Name="ProcessId">0x4d00</Data>
		<Data Name="CommandLine">"C:\Windows\system32\reg.exe" add HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v TestRunKey /d calc.exe /f</Data>
		<Data Name="TargetUserSid">S-1-0-0</Data>
		<Data Name="TargetUserName">-</Data>
		<Data Name="TargetDomainName">-</Data>
		<Data Name="TargetLogonId">0x0</Data>
		<Data Name="ParentProcessName">C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Data>
		<Data Name="MandatoryLabel">S-1-16-12288</Data>
	</EventData>
</Event>

Exotic Registry Modification Techniques Detection

Up to this point, we can already trace changes to Run key locations, capture payload launches from those keys and watch the processes that perform the edits. If your visibility ends at process-creation telemetry, however, an attacker can slip past by choosing tools other than reg.exe or regedit.exe. The following sections walk through three such alternatives: PowerShell, regini.exe, and VBScript, showing the commands that perform the write and the log sources you must enable to see them.

PowerShell

PowerShell offers a fully featured registry provider. A single line such as New-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" -Name Evil -Value "C:\evil.exe" -PropertyType String –Force creates the value Evil = C:\evil.exe without using regedit.exe. The same applies to its WMI/CIM counterpart Invoke-WmiMethod -Namespace root\default -Class StdRegProv -Name SetStringValue -ArgumentList @(2147483650, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", "C:\\evil.exe", "Evil").

It is important to note that, unless these commands are not executed within an existing PowerShell process, no Security Event 4688 or Sysmon Event 1 is generated. Instead, PowerShell Script-Block Logging captures both methods when Script-Block Logging (or, at the very least, Module Logging) is enabled. With Script-Block Logging active, the Microsoft-Windows-PowerShell/Operational channel records Event 4104, which contains the complete command line:

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-PowerShell" Guid="{a0c1853b-5c40-4b15-8766-3cf1c58f985a}" />
		<EventID>4104</EventID>
		<Version>1</Version>
		<Level>5</Level>
		<Task>2</Task>
		<Opcode>15</Opcode>
		<Keywords>0x0</Keywords>
		<TimeCreated SystemTime="2025-06-27T13:54:47.4318017Z" />
		<EventRecordID>13660</EventRecordID>
		<Correlation ActivityID="{c3cf0db0-d2d5-0007-178e-c837bee5db01}" />
		<Execution ProcessID="19712" ThreadID="16332" />
		<Channel>Microsoft-Windows-PowerShell/Operational</Channel>
		<Computer>redacted</Computer>
		<Security UserID="S-1-5-21-...-500" />
	</System>
	<EventData>
		<Data Name="MessageNumber">1</Data>
		<Data Name="MessageTotal">1</Data>
		<Data Name="ScriptBlockText">New-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" -Name Evil -Value "C:\evil.exe" -PropertyType String -Force</Data>
		<Data Name="ScriptBlockId">bcf07a2b-7b09-426e-b767-82e854cb8708</Data>
		<Data Name="Path" />
	</EventData>
</Event>

regini.exe

A second Living-off-the-Land option is regini.exe. Because this application consumes an ASCII “registry script”. You first have to construct a small text block and then feed it to the binary.

C:\Users\azureuser> (echo \Registry\Machine\Software\Microsoft\Windows\CurrentVersion\Run & echo     Evil = REG_SZ C:\evil.exe) > addRun.ini
C:\Users\azureuser> type addRun.ini
\Registry\Machine\Software\Microsoft\Windows\CurrentVersion\Run
    Evil = REG_SZ C:\evil.exe
C:\Users\azureuser> regini addRun.ini

The spaces before the value name Evil are essential; regini expects the value line to be indented by four characters by default.

VBScript

You can also drop a tiny VBS file and execute it with wscript.exe:

C:\Users\azureuser> echo Set s=CreateObject("WScript.Shell"): s.RegWrite "HKLM\Software\Microsoft\Windows\CurrentVersion\Run\Evil", "C:\evil.exe", "REG_SZ" > addRun.vbs & wscript.exe addRun.vbs

Alternatively, cscript.exe or mshta.exe (which can embed VBScript), or direct COM use of WScript.Shell, for example, achieve the same results. Using mshta.exe, you don’t even need to write the VBScript to disk:

C:\Users\azureuser> mshta vbscript:Execute("Set s=CreateObject(""WScript.Shell""):s.RegWrite ""HKLM\Software\Microsoft\Windows\CurrentVersion\Run\Evil"", ""C:\evil.exe"", ""REG_SZ"": window.close")

The same applies to rundll32.exe:

C:\Users\azureuser> rundll32 vbscript:"\..\mshtml,RunHTMLApplication "+CreateObject("Wscript.Shell").RegWrite("HKLM\Software\Microsoft\Windows\CurrentVersion\Run\Evil","C:\evil.exe","REG_SZ")(window.close)

AppLocker can be configured to block VBS execution, for example, with the following FilePathRule:

<?xml version="1.0" encoding="utf-8"?>
<AppLockerPolicy Version="1">
    <RuleCollection Type="Script" EnforcementMode="Enabled">
        <FilePathRule Id="0c4a61d6-3a1e-4242-a643-146ef829e816"
                    Name="Block all VBS scripts"
                    Description="Prevent execution of any .vbs files"
                    UserOrGroupSid="S-1-1-0" 
                    Action="Deny">
            <Conditions>
                <FilePathCondition Path="*.vbs" />
            </Conditions>
        </FilePathRule>
    </RuleCollection>
</AppLockerPolicy>

This ensures that every file ending with .vbs cannot be executed. Alternatively, the rule can be set to Audit mode. Activate it with:

C:\Users\azureuser> Set-AppLockerPolicy -XmlPolicy BlockVBS.xml –Merge
C:\Users\azureuser> gpupdate /force
C:\Users\azureuser> Restart-Service AppIDSvc

Validate that the rule is active:

C:\Users\azureuser> Get-AppLockerPolicy -Effective -Xml

The output should contain the <FilePathRule> fragment shown above. Execution of a VBS file then triggers a Microsoft-Windows-AppLocker/MSI and Script Event 8007 (in Enforcement mode) or 8004 (in Audit mode):

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-AppLocker" Guid="{cbda4dbf-8d5d-4f69-9578-be14aa540d22}" />
		<EventID>8007</EventID>
		<Version>0</Version>
		<Level>2</Level>
		<Task>0</Task>
		<Opcode>0</Opcode>
		<Keywords>0x4000000000000000</Keywords>
		<TimeCreated SystemTime="2025-06-28T18:04:22.1259421Z" />
		<EventRecordID>1</EventRecordID>
		<Correlation />
		<Execution ProcessID="3232" ThreadID="4764" />
		<Channel>Microsoft-Windows-AppLocker/MSI and Script</Channel>
		<Computer>DESKTOP-299P60T</Computer>
		<Security UserID="S-1-5-21-…-1001" />
	</System>
	<UserData>
		<RuleAndFileData
			xmlns="http://schemas.microsoft.com/schemas/event/Microsoft.Windows/1.0.0.0">
			<PolicyNameLength>6</PolicyNameLength>
			<PolicyName>SCRIPT</PolicyName>
			<RuleId>{0c4a61d6-3a1e-4242-a643-146ef829e816}</RuleId>
			<RuleNameLength>21</RuleNameLength>
			<RuleName>Block all VBS scripts</RuleName>
			<RuleSddlLength>52</RuleSddlLength>
			<RuleSddl>D:(XD;;FX;;;S-1-1-0;(APPID://PATH Contains "*.VBS"))</RuleSddl>
			<TargetUser>S-1-5-21-…-1001</TargetUser>
			<TargetProcessId>3232</TargetProcessId>
			<FilePathLength>18</FilePathLength>
			<FilePath>%OSDRIVE%\EVIL.VBS</FilePath>
			<FileHashLength>0</FileHashLength>
			<FileHash />
			<FqbnLength>1</FqbnLength>
			<Fqbn>-</Fqbn>
			<TargetLogonId>0x35fb2</TargetLogonId>
			<FullFilePathLength>11</FullFilePathLength>
			<FullFilePath>C:\evil.vbs</FullFilePath>
		</RuleAndFileData>
	</UserData>
</Event>

Interim Summary

We have explored multiple methods for modifying persistence-related registry keys using built-in Windows tools and the telemetry generated by each technique. The key takeaway is defense in depth: rely on both process-level signals (4688/Sysmon 1, AppLocker 8007, AMSI 4104) and registry-level auditing (Security 4657, Sysmon 13). If one log source is suppressed, the other still exposes the activity.

Focusing solely on “known” Run key locations is risky; new autostart paths appear, and attackers often repurpose non-standard keys. A healthier approach is to baseline normal registry and process behaviour, and then alert on deviations, especially when signed but rarely used binaries (e.g., mshta.exe, cscript.exe, regini.exe) interact with persistence branches of the hive.

Finally, remember that live registry inspection is valuable during incident response, but it should never replace proper logging. Transaction files, USN records, and memory-resident handles can help corroborate timeline gaps; however, they are slower and more error-prone than having the correct events in the first place. Layered detections, thorough baselining, and resilient log forwarding remain the best insurance against stealthy registry abuse.

File Write Detection

Now, let’s look at file-write detections. The basics include NTFS artefacts you already know—$MFT, $UsnJrnl, $J, $I30, and $LogFile. Additionally, we can use Windows event logs to detect file-write activity in specific locations. Remember, this post focuses on T1547.001 – Registry Run Keys/Startup Folder, which involves not only registry changes but also the Startup folders located at %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup (for the Current User) and C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup (for All Users). Attackers can also add new startup folders by editing keys such as HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders.

Common Startup registry value name and location

As with registry object auditing, we can enable directory auditing. First, enable Audit File System in secpol.msc when in a workgroup, or via a GPO in a domain.

File System Auditing enabled

Next, create a SACL on the directory to monitor—in this case, C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup. Open the folder’s Security > Advanced > Auditing tab, choose a principal (e.g., Everyone or Users), and select at least write activities.

File System Startup Folder SACL creation

When any principal writes to this directory, Windows normally generates three Security 4663 events:

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" />
		<EventID>4663</EventID>
		<Version>1</Version>
		<Level>0</Level>
		<Task>12800</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8020000000000000</Keywords>
		<TimeCreated SystemTime="2025-06-28T20:22:25.0406075Z" />
		<EventRecordID>2037495</EventRecordID>
		<Correlation />
		<Execution ProcessID="4" ThreadID="4144" />
		<Channel>Security</Channel>
		<Computer>redacted</Computer>
		<Security />
	</System>
	<EventData>
		<Data Name="SubjectUserSid">S-1-5-21-...-500</Data>
		<Data Name="SubjectUserName">azureuser</Data>
		<Data Name="SubjectDomainName">redacted</Data>
		<Data Name="SubjectLogonId">0x9ab8ad1</Data>
		<Data Name="ObjectServer">Security</Data>
		<Data Name="ObjectType">File</Data>
		<Data Name="ObjectName">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\calc.exe</Data>
		<Data Name="HandleId">0x50d0</Data>
		<Data Name="AccessList">%%4417</Data>
		<Data Name="AccessMask">0x2</Data>
		<Data Name="ProcessId">0x2934</Data>
		<Data Name="ProcessName">C:\Windows\explorer.exe</Data>
		<Data Name="ResourceAttributes">S:AI</Data>
	</EventData>
</Event>

Look for access types WriteData (or AddFile), AppendData (or AddSubdirectory or CreatePipeInstance), and WriteAttributes. A corresponding Sysmon Event 11 also records the file write:

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385f-c22a-43e0-bf4c-06f5698ffbd9}" />
		<EventID>11</EventID>
		<Version>2</Version>
		<Level>4</Level>
		<Task>11</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8000000000000000</Keywords>
		<TimeCreated SystemTime="2025-06-28T20:20:58.5343488Z" />
		<EventRecordID>4113</EventRecordID>
		<Correlation />
		<Execution ProcessID="2900" ThreadID="16148" />
		<Channel>Microsoft-Windows-Sysmon/Operational</Channel>
		<Computer>redacted</Computer>
		<Security UserID="S-1-5-18" />
	</System>
	<EventData>
		<Data Name="RuleName">T1023</Data>
		<Data Name="UtcTime">2025-06-28 20:20:58.532</Data>
		<Data Name="ProcessGuid">{51e2ac6f-5f7a-685e-6229-000000001300}</Data>
		<Data Name="ProcessId">10548</Data>
		<Data Name="Image">C:\Windows\Explorer.EXE</Data>
		<Data Name="TargetFilename">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\calc.exe</Data>
		<Data Name="CreationUtcTime">2025-06-28 20:20:33.239</Data>
		<Data Name="User">redacted\azureuser</Data>
	</EventData>
</Event>

For completeness, inspect the $MFT and $UsnJrnl:

C:\Users\azureuser> MFTECmd.exe -f ".\$Extend\$UsnJrnl%3A$J" -m $MFT --csv . --csvf usnjrnl.csv
C:\Users\azureuser> MFTECmd.exe -f $MFT --csv . --csvf mft.csv
C:\Users\azureuser> LogFileParser64.exe /LogFileFile:.\$LogFile /SkipSqlite3:1 /OutputPath:.

Sorting the Master File Table by the Created0x10 timestamp makes the new calc.exe in the Startup folder easy to spot.

Suspicious startup executables identified using the MFT

If the file were later deleted, the $MFT entry would disappear and a RenameOldName record would appear in the journal.

Startup application creation identified using the UsnJrnl

Remember that the NTFS $LogFile preserves the most complete history of every change. Additional NTFS artefacts—combined with file-system auditing—will often expose any rogue .reg or .ini file an attacker drops to alter registry data. Because such scripts are typically tiny, they usually reside entirely within their MFT record, making them easy to carve directly from the MFT itself.

THOR Timelining using Timesketch

The newest THOR helper, thor2timesketch, went public only a couple of months ago and closes the gap between THOR reports and a Timesketch workflow. Instead of paging through the classic HTML summary or shipping raw Syslog to another host, you can now feed THOR findings straight into your forensic timeline.

The setup is trivial. Run THOR ≥ 10.7 with JSON v2 output enabled and point the output at a file you’ll later import:

C:\Tools\THOR\thor64.exe --lab -p .\triage\ --jsonv2 --jsonfile results.json

In this example we scan a Velociraptor SANS‑triage directory to mimic the “grab‑and‑scan” pattern of a routine host‑forensics engagement. Before launching THOR we wiped the event logs, removed existing Run‑key values, replayed each persistence variant discussed in this post, and dropped several binaries into the Startup folders.

THOR completes its sweep within minutes and leaves results.json, a self‑contained collection of every THOR finding, in the working directory. Ordinarily you might parse that file further, convert it to HTML, or forward the data elsewhere. Here, however, we rely on the Timesketch conversion utility to produce a timeline‑ready artifact:

thor2ts results.json -o results_ts.jsonl

Timesketch demands a very specific JSONL structure, so the intermediate conversion step is mandatory; piping the raw THOR output directly will fail. Run the helper, and any other Python script, in a dedicated virtual environment to avoid dependency clashes.

thor2timesketch utility usage

Spin up Timesketch, import that JSON with thor2timesketch, and the timeline instantly flags every persistence trick we used, Run keys, Startup-folder droppers, the mshta.exe and VBScript, alongside the usual THOR signature hits. No extra parsing, no manual tagging, just a clean, searchable view that chains our artefacts together and gives investigators a head start the moment an incident begins.

Timesketch sample event generated from a mshta.exe and VBS combination

Wrapping Up

In this walkthrough, we explored the full persistence chain behind T1547.001—beginning with registry Run key edits, extending to files planted in Startup folders, and ending with the forensic artefacts and log records each step leaves behind. On the registry side, we showed how attackers could write Run key values with native binaries such as reg.exe, regini.exe, mshta.exe, cscript.exe, and even inline VBScript delivered through rundll32. Each technique produces its own telemetry: Security 4688 or Sysmon 1 for the launching process, Script-Block Event 4104 for PowerShell, Security 4657 or Sysmon 13 for the key change, and AppLocker Events 8007 or 8004 when script hosts are denied or audited. For file-based autostarts we enabled Audit File System and attached a SACL to the Startup directory, confirming that every payload write generates the expected trio of Security 4663 events while Sysmon 11 records the same operation with full path, process, and hash.

Throughout the post, we emphasised defence in depth. Relying on a single signal—only process starts, only registry auditing, only file writes—creates blind spots a determined adversary can exploit. Combining multiple, independent controls not only raises the chance of detection but also yields higher-quality timelines for responders. THOR detects the shown patterns out of the box, and a Timesketch notebook ties registry, process, and NTFS evidence together for rapid pivoting.

We also looked forward: memory-resident handles reveal keys that remain open long after values are changed, hive timestamps and transaction logs expose stealthier edits, and ETW consumers or direct API hooks can supplement Windows logging for high-sensitivity environments. Finally, because sophisticated actors increasingly tamper with the logging pipeline itself, we noted the need to monitor for ETW patching and to forward Security and Sysmon streams in real-time so local log clearing cannot erase the record.

Taken together, these layers give SOC analysts and incident responders a robust, repeatable playbook for spotting—and proving—Run key and Startup-folder persistence, even when attackers mix LOLBins, script hosts, and file-less techniques in an attempt to hide.

About the author:

Maurice Fielenbach

Maurice Fielenbach trains cybersecurity professionals in reverse engineering and malware analysis — his main area of focus — and digital forensics through his company, Hexastrike Cybersecurity. The company also develops tools for red and blue teams and publishes technical blog posts covering both offensive and defensive topics. He also serves as Head of CERT at r-tec, leading a team of forensic specialists, managing and investigating a wide range of security incidents.

Subscribe to our Newsletter

Monthly news, tips and insights.

Follow Us

Upgrade Your Cyber Defense with THOR

Detect hacker activity with the advanced APT scanner THOR. Utilize signature-based detection, YARA rules, anomaly detection, and fileless attack analysis to identify and respond to sophisticated intrusions.