Press "Enter" to skip to content

Building an EmPyre with Python


The “EmPyre Series”

5/12/16 – Building an EmPyre with Python

5/18/16 – Operating with EmPyre

5/24/16 – The Return Of the EmPyre

5/31/16 – OS X Office Macros with EmPyre

Our team has increasingly started to encounter well secured environments with a large number of Mac OS X machines. We realized that while we had a fairly expansive Windows toolkit, there were very few public options available for OS X agents, and none that satisfied our particular requirements. Our group is used to operating in heavy Windows environments (hence me not shutting up about offensive PowerShell on this blog) so we felt a bit out of our element, but we had to deliver on these engagements and needed something custom to do so.

We’re fans of using scripting languages offensively due to their flexibility and rapid development. We’re also big proponents ‘living off the land’ with existing OS functionality, which typically means PowerShell for Windows and Python/Bash/Applescript for OS X.

The PowerShell Empire code base is actually fairly language agnostic. The server essentially just handles key negotiation to stage a full script-based agent and provides a variety of language-specific post-exploitation modules. Over the course of two weeks we built an Empire-compatible Python agent and adapted the code base to handle it. The agent proved successful and over the past several months my awesome ATD workmates @424f424f, @xorrior, and @killswitch_gui helped to greatly improve the agent, backend, and a number of OS X-specific post-exploitation capabilities.

We’re calling the project EmPyre for now, and the code is now public on the AdaptiveThreat/EmPyre GitHub repository. This post will cover a quickstart for EmPyre, some architectural background, its relation to Empire, and future plans. @424f424f and @killswitch_gui will cover some of EmPyre in their HackMiami talk “External to DA the OSX Way: Operating In An OS X-Heavy Environment” on May 14th, and several of the team members will be publishing posts detailing various components and use cases for EmPyre over the coming weeks (which I’ll update here similar to the ‘Empire Series‘).


Clone EmPyre from and kick off the install (just like Empire) with ./setup/ Type a staging password when you get to that section or press enter for a randomly generated one:


Launch EmPyre with ./empyre, optionally specifying --debug if you want debug output written to ./empyre.debug. The main menu and UI should look familiar to Empire users:


The standard menu options (listeners, stagers, agents) should be familiar as well. Type listeners to jump to the listeners menu and info for currently configured options:


Modify the options you want with set OPTION VALUE and unset OPTION, then type execute to start the listener up:


The usestager STAGER LISTENER command lets you jump to a stager module for the specified listener and info shows you the options. And like with Empire’s UI, nearly everything is tab-completable. Let’s generate a one-liner launcher for the created listener, disabling the LittleSnitch check first (more on the later):


After executing this command on our OS X host, we’ll get an agent checkin, which we can interact with by jumping to the agents menu and then typing interact [tab] AGENT:


shell CMD will execute a shell command and download/upload operate as expected, etc. Use help to see all agent options:


To use a post-explotiation module type usemodule [tab] like in Empire. Option setting and execution is just like Empire:


This should be enough to get you started- posts in the coming weeks will cover operational usage and specific modules in much more depth. There’s also a quick demo of using EmPyre and a Mac 2011 Office Macro to Thunderstrike a victim hosted here on Vimeo.

EmPyre Architecture

As stated above, a large chunk of the EmPyre code base is shared with Empire. Part of this was out of time-contained necessity, part of it was to keep usage similar to Empire, and part of it is because we want to move towards an eventual common C2 architecture (described at the end of this post). As such, the EmPyre source code should be pretty familiar for anyone who’s played with Empire.

./setup/ will install all the necessary dependencies and kick off to build the SQLite backend database. Most of the options for the database setup are the same- the staging key can be specified or randomly generated, the negotiation URIs can be modified, default delays/jitters changed, etc. The ./setup/ script will reset your setup just like Empire as well.

The ./empyre script kicks off execution, with the same type of CLI options available with Empire. This includes its own RESTful API- we hope to have a controller that can handle both types of interfaces soon.

Most of ./lib/common/* will look pretty similar too- EmPyre uses the same underlying packet structure, http handlers, Cmd message interface, etc. EmPyre also uses the same general staging scheme and asynchronous HTTP[s] communication style. The main UI and most commands should be mostly the same, and modules retain things like admin and opsec-safe checks. The main differences are in the agent, stagers, and post modules, described in more detail in the next section.

Empire versus EmPyre

So I’ve covered some of the similarities, but what are the differences between the two projects?

Obviously, EmPyre’s agent is written in pure Python instead of PowerShell. The key negotiation stager is located in ./data/agent/ while the agent itself is located in ./data/agent/ The agent is Python 2.7 compatible and only depends on code from the Python Standard Library. We wanted to minimize the assumptions of target environment (similarly to coding our offensive PowerShell to version 2.0) and didn’t want to have to install any third party packages on a host. This resulted in things like us bringing along an AES implementation in the stager (from and a Diffie-Hellman implementation from

EmPyre also has a different set of stagers. We’ll have a post in the series that covers stagers in more detail, but we currently have AppleScript, dyLib generation, Mach-O generation, a HTML Safari launcher, .WAR generation, Office macro generation, and the traditional one-liner launcher. For the underlying launcher commands, we actually pipe echoed Python code to the python binary, which prevents the executed command from showing up with ps:


For launchers, there’s also an default check for Little Snitch which prevents agent execution if Little Snitch is detected. To disable this, set the LittleSnitch option to False in the launcher module before generation. For reference, here’s what that initial one-liner looks like decoded:

Most importantly, EmPyre has its own set of OS X-specific post-exploitation modules. Note: since the agent’s only requirement is Python 2.7, EmPyre will run on several Linux variants. We don’t have many specific Linux post-exploitation modules in the project yet but we hope this will change shortly.

These modules include expected things like keyloggers, clipboard stealers, and screenshots, as well as every CCDC operator’s favorite Trollsploit (don’t worry, we have Thunderstruck). Lateral movement is a bit more limited on OS X but includes SSH options for launching agents. Privesc includes a “sudo spawn” module to launch a high integrity agent if you have the user’s password, as well as a Python version of Get-GPPPassword.

There’s also a modified version of @fuzzynop‘s FiveOnceInYourLife code he released two years ago at DerbyCon. The collection/osx/prompt module with the ListApps option set will list programs suitable for prompting which can then be specified with AppName. This will launch the specified program and prompt for user credentials. Captured credentials can then be used with sudo_spawn to launch a high-integrity agent to execute things like hash dumping.



Collection options also include things like hash and iMessage dumping, email searching, webcam snapping, and more. Network situational awareness includes a basic port scanner, low-hanging fruit finder, and a load of Active Directory integrations to enumerate things like computers/users/groups through ldapsearch. There are also a number of persistence options that will be covered in an upcoming blog post in the series.

EmPyre Key Negotiation

EmPyre’s key negotiation functions a bit differently than Empire’s. To cut down on size and external dependencies, Diffie-Hellman is used in the Encrypted Key Exchange (DH-EKE) setup instead of RSA and RC4 is used to obscure the stage0 request instead of XOR. The scheme is as follows:

  • KEYs = staging key, set per server (used for RC4 and initial AES comms)
  • KEYn = the DH-EKE negotiated key
  • PUBc = the client-generated DH public key
  • PUBs = the server-generated DH public key
  1. client runs launcher code that GETs from /stage0 – the launcher implements a minimized RC4 decoding stub and negotiation key
  2. server returns RC4(KEYs, – (the key negotiation stager) contains minimized DH and AES implementations
  3. client generates DH key PUBc, and POSTs HMAC(AES(KEYs, PUBc)) to /stage1, server generates a new DH key on each initial check in
  4. server returns HMAC(AES(KEYs, nonce+PUBs)) client calculates shared DH key KEYn
  5. client POSTs HMAC(AES(KEYn, [nonce+1]+sysinfo) to /stage2
  6. server returns HMAC(AES(KEYn, patched
  7. client sleeps on interval, and then GETs /tasking.uri
  8. if no tasking, return standard looking page
  9. if tasking, server returns HMAC(AES(KEYn, tasking))
  10. client posts HMAC(AES(KEYn, tasking)) to /response.uri

We’re obviously not cryptographers, and we’ve had issues with Empire’s crypto in the past, so if anyone finds any issue we’ll owe you a round at the next conference we end up at!

Future Plans

EmPyre Python agents will likely be combined into the Empire code base at some point but we’re still sorting out how exactly to handle the integration. For now both projects will remain separate, likely until after the BlackHat timeframe, but we’re open to suggestions and feedback.

We hope the community embraces this as much as they have Empire, with module contributions, bug reports, and more. We’re also aiming to expand out the Linux capabilities of the toolset- the more help we get the better the solution will be for everyone.

One Comment

Leave a Reply

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