Press "Enter" to skip to content

Nothing Lasts Forever: Persistence with Empire

This post is part of the ‘Empire Series’ with some background and an ongoing list of series posts [kept here].

Code execution is great and remote control is awesome, but if you don’t have a persistence strategy planned nothing can throw a wrench in your engagement like an unplanned reboot or user logout. This post covers 17 current Empire persistence modules that can help you with retaining hard-fought access, broken into userland/elevated options, PowerBreach, and miscellaneous approaches.

We like to break reboot persistence down into a three different questions. First, are you installing the persistence from userland or an elevated context? Second, where are you storing the payload you want to execute? And finally, how do you want the system to trigger your malicious logic?


The persistence/userland/* and elevated persistence modules in Empire are organized by trigger method (schtasks, registry, etc.) with the storage options contained within each module. Most of these methods were adapted from PowerSploit’s Persistence.psm1 module. All of these modules also have a Cleanup option which will set the machine back to its original state, as well as an ExtFile option that takes a path to a local file on the control server and persists that logic (instead of an Empire stager).

Persisting from a non-elevated context can get tricky as your options are a bit constrained. However, if you aren’t able to privilege escalate on a system, Empire has three options available to you.

The registry module uses one of the oldest methods in the book- setting HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Run to trigger your payload when the current user logs back into the system. For storing the launcher text, you also have three options: RegPath stores the payload in the specified registry location, ADSPath will store the payload in an alternate data stream (of the form C:\Users\John\AppData:blah.txt), and EventLogID will stuff the launcher text into the event log under the specified event ID. If either ADSPath or EventLogID are specified those methods will be used, otherwise the default is RegPath. One note: this method will cause a cmd/PowerShell prompt to briefly appear on user login, and this is the most likely method to be caught by defenders.



The userland schtasks module is a bit sneakier. While you can’t trigger a script on system boot, you can set the logic to trigger on a specified DailyTime (HH:mm format) or after the current user has been idle for IdleTime minutes. Note that this also briefly pops up a cmd/PowerShell prompt on payload execution, so the IdleTime approach may be a better option in some situations.



Rounding out the userland options is something that’s a bit different, backdoor_lnk. Inspired by @nikhil_mitt‘s post, this module takes a full LNKPath to an existing .LNK shortcut as well as a specified RegPath to store the launcher logic. The script will store the base64-encoded launcher in the registry location, preserve the shortcut icon, and then set the launch target to be powershell.exe that launches the original binary and then the Empire stager. The end result is a trojanized shortcut that will spawn a new Empire payload whenever the user clicks on the shortcut. As with other approaches, Cleanup will restore the original .lnk paramaters.




Like userland, the persistence/elevated/* approaches are organized by trigger method with storage options contained within each module. With elevated privileges, we have a few more options.

The elevated version of registry is very similar to the userland version, but uses the HKLM version  of the Run key to trigger. This means that it will trigger for any user that logs into the system (instead of just the current user), but a prompt still displays.

The elevated version of schtasks is definitely more flexible than its userland cousin. You have the same DailyTime and IdleTime trigger options, but also now have an OnLogon option as well. This will run your payload when a user logs in but won’t display a prompt, and also runs as SYSTEM. If you’re using elevated schtasks for persistence, this is almost always the best option.


The wmi module (again adopted from PowerSploit) is definitely one of our go to persistence methods when we have elevated execution. It adds a permanent WMI subscription either at a DailyTime or within 5 minutes of system boot with AtStartup. This will run as SYSTEM, regardless of whether a user has logged in or not.



persistence/powerbreach/* focuses on memory only methods that do not persist across a reboot. It consists of three interesting triggers that can execute an Empire launcher. All of these modules have a Sleep option (which specifies the number of seconds to sleep between checks) and a Timeout option (also in seconds, which tells the backdoor how long to run before exiting, 0 being forever). Each of these are also spawned in a separate powershell.exe process, meaning that they’ll keep running as long as the system doesn’t reboot/the user doesn’t log out, even if the Empire agent exits. By default they will all also only run until a successful launcher trigger.

The eventlog backdoor will monitor the system for failed RDP login attempts from a certain username specified by the Trigger value. Because of this, it needs elevated privileges. To trigger an Empire stager, just RDP to the machine and execute a failed login with the Trigger username. The username also needs to exist.

resolver will continuously resolve a specified Hostname and trigger the stager when the name resolves to the specified Trigger IP. When you want to retrigger your Empire stager, just change the A record for the given Hostname.

The deaduser trigger will periodically poll Active Directory for a given user and trigger the stager if the user no longer exists. This one is great to set on any backdoor users added to the domain, and can serve as a kind of warning system if admins are onto you.

There’s lots more information on these methods in @sixdub‘s post “Varying Heuristics in a Network with PowerBreach”, and draw their inspiration from Jake Williams and Mark Baggett’s 2013 Shmoocon “Wipe the drive!” talk.


The persistence/misc/* modules contain a variety of methods that didn’t exactly fit into other categories.

add_netuser is pretty straightforward and allows you to easily add a user (privileges permitting) to the current domain, the local computer, or a local user to a remote computer. By default it will add to the local machine (modifiable with ComputerName). Domain adds can be specified with the Domain switch.

We also have a few options for the enumeration and abuse of SSP Authentication Packagesget_ssps and install_ssp (again from PowerSploit) allow you to enumerate and install malicious SSPs on the target system. @pyrotek3 has some more information on how to abuse these methods on his blog. There’s also memssp from @gentilkiwi, weaponized through PowerSploit’s Invoke-Mimikatz function. This executes Mimikatz’ misc::memssp function to log all user authentication events to C:\Windows\System32\mimisla.log. Note that this particular method is memory-resident only and won’t survive a reboot, and requires administrative privileges.

skeleton_key (also from @gentilkiwi/Invoke-Mimikatz) runs Mimikatz’ misc::skeleton function to patch LSASS such that the password ‘mimikatz’ allows successful authentication. @pyrotek3 has a nice writeup on this as well. This module obviously also requires administrative privileges and is only applicable on domain controllers.


disable_machine_acct_change is an interesting one and can help enable “Persistence With Computer Accounts” as again described in one of @pyrotek3‘s posts. If you can recover a machine’s account hash (ends with $), whether through SAM dumping, Mimikatz’s sekurlsa::logonpasswords, or other means, you can create silver tickets that grant you administrative access to the machine as long as that hash does not change. Unfortunately, by default machines will rotate their machine account passwords every 30 days and report the change in Active Directory. But because it’s up to the machine to rotate its own password, we can disable this behavior and retain indefinite access to a machine! This is where disable_machine_acct_change comes in- it sets HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters\DisablePasswordChange to 1 which prevents the system from changes its account password, granting you continued access with the machine hash. For more information, definitely check out @pyrotek3‘s post on the subject.

And finally, we have debugger (almost identical to the lateral_movement/invoke_wmi_debugger module described before). This module lets you set the Image File Execution Options for a particular program through the registry so that an Empire stager is launched instead of the original program. This would be especially nice if we can somehow execute the binary remotely and without any privileges. Luckily for us, there are five separate accessibility/”Ease of Access” binaries that can be triggered from an RDP login window before user authentication, making them prime targets for this module. They also all nicely run as SYSTEM, need administrative to set the debugger for, and can be set through the TargetBinary option.

  • sethc.exe – the “sticky keys” backdoor that many are familiar with. You can trigger this by pressing shift five times, or by clicking the utilman button on the login screen, selecting the stickykeys option, and pressing OK/Apply.
  • Utilman.exe – the Windows Utility Manager, launched with the button on the login screen or with Win + U.
  • osk.exe – the on screen keyboard, triggerable through the utilman interface like sethc.exe.
  • Narrator.exe – the Windows text narrator, triggerable through the utilman interface like sethc.exe.
  • Magnify.exe – the on screen magnifier, triggerable through the utilman interface like sethc.exe.



Like the userland and elevated modules the payload text is stored in a specified RegPath and CleanUp will restore the system. You can also specify a TriggerBinary to execute (like C:\Windows\System32\cmd.exe) instead of an Empire stager. One big note: an RDP session will time out after 30 seconds of no user keyboard/mouse activity, and your triggered agents will die with the window close. Your best bet is to inject a new agent into another running process on the system before ending the RDP pre-login session.


Persistence is an important part of most engagements. If you plan on keeping control of a machine beyond a day or so, a preplanned persistence strategy can keep a rogue reboot from causing you heartburn. Luckily, Empire abstracts away some of the complexity of establishing persistence, allowing you to easily deploy a persistence method and remove it when your engagement is completed.


  1. g1tch0 g1tch0 March 31, 2016

    Does empire have any support for stagers/persistence/payloads that work through NAT?

    • harmj0y harmj0y March 31, 2016

      what do you mean exactly by ‘work through NAT’?

      • g1tch0 g1tch0 March 31, 2016

        sorry – should have clarified: if i want to accept connections on my local subnet AND from machines another network (connecting via NAT’d address), do I need to setup 2 listeners on different ports (one with a local IP and the other with the NAT IP)? Thanks for your contributions to the PS community btw – been following and using for years.

        • g1tch0 g1tch0 March 31, 2016

          (note – I’m seeing a video embedded (vimeo) in my last post – was not from me)

Leave a Reply

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