Added /empire/api/listeners/kill to kill a listener specified by POST data

Added /empire/api/listeners/options to enumerate currently set listener options
Added start to docstrings in functions -> still need to describe complete request/response JSON formats
removed /empire/api/agents/ID/X
/empire/api/agents/name/Y -> /empire/api/agents/Y
removed /empire/api/listeners/id/X
/empire/api/listeners/name/Y -> /empire/api/listeners/Y
"X listeners currently active" now pulls from the backend DB
1.6
Harmj0y 2016-03-21 21:50:19 -04:00
parent 334f1f4b5c
commit eaaea57253
3 changed files with 144 additions and 49 deletions

View File

@ -72,12 +72,13 @@ def start_restful_api(port=1337):
GET http://localhost:1337/empire/api/modules return all current modules
GET http://localhost:1337/empire/api/listeners return all current listeners
GET http://localhost:1337/empire/api/listeners/id/X return the listener with id X
GET http://localhost:1337/empire/api/listeners/name/Y return the listener with id Y
GET http://localhost:1337/empire/api/listeners/Y return the listener with id Y
GET http://localhost:1337/empire/api/listeners/options return all listener options
POST http://localhost:1337/empire/api/listeners starts a new listener with the specified options
POST http://localhost:1337/empire/api/listeners/kill kills the listener specified by options
GET http://localhost:1337/empire/api/agents return all current agents
GET http://localhost:1337/empire/api/agents/id/X return the agent with id X
GET http://localhost:1337/empire/api/agents/name/Y return the agent with name Y
GET http://localhost:1337/empire/api/agents/Y return the agent with name Y
GET http://localhost:1337/empire/api/reporting return all logged events
GET http://localhost:1337/empire/api/reporting/agent/X return all logged events for the given agent name X
@ -106,12 +107,19 @@ def start_restful_api(port=1337):
@app.route('/empire/api/version', methods=['GET'])
def get_version():
"""
Returns the current Empire version.
"""
return jsonify({'version': empire.VERSION})
@app.route('/empire/api/config', methods=['GET'])
def get_config():
"""
Returns JSON of the current Empire config.
TODO: describe returned JSON format
"""
configRaw = execute_db_query(conn, 'SELECT * FROM config')
[staging_key, stage0_uri, stage1_uri, stage2_uri, default_delay, default_jitter, default_profile, default_cert_path, default_port, install_path, server_version, ip_whitelist, ip_blacklist, default_lost_limit, autorun_command, autorun_data] = configRaw[0]
@ -121,7 +129,11 @@ def start_restful_api(port=1337):
@app.route('/empire/api/stagers', methods=['GET'])
def get_stagers():
"""
Returns JSON describing all stagers.
TODO: describe returned JSON format
"""
stagerInfo = {}
for stagerName,stager in main.stagers.stagers.iteritems():
info = stager.info
@ -133,7 +145,11 @@ def start_restful_api(port=1337):
@app.route('/empire/api/stagers/<string:stager_name>', methods=['GET'])
def get_stagers_name(stager_name):
"""
Returns JSON describing the specified stager_name passed.
TODO: describe returned JSON format
"""
stagerInfo = {}
for stagerName,stager in main.stagers.stagers.iteritems():
if(stagerName == stager_name):
@ -146,7 +162,16 @@ def start_restful_api(port=1337):
@app.route('/empire/api/stagers', methods=['POST'])
def generate_stager():
"""
Generates a stager with the supplied config and returns JSON information
describing the generated stager, with 'Output' being the stager output.
Required JSON args:
StagerName - the stager name to generate
Listener - the Listener name to use for the stager
TODO: describe returned JSON format
"""
if not request.json or not 'StagerName' in request.json or not 'Listener' in request.json:
abort(400)
@ -161,8 +186,6 @@ def start_restful_api(port=1337):
stager = main.stagers.stagers[stagerName]
# self.stager.options[option]['Value']
# set all passed options
for option,values in request.json.iteritems():
if option != 'StagerName':
@ -175,8 +198,6 @@ def start_restful_api(port=1337):
if values['Required'] and ((not values['Value']) or (values['Value'] == '')):
return jsonify({'error': 'required stager options missing'})
# self.mainMenu.stagers.stagers[stagerName]
# print request.json['listener']
stagerOut = stager.options
stagerOut['Output'] = stager.generate()
@ -185,7 +206,11 @@ def start_restful_api(port=1337):
@app.route('/empire/api/modules', methods=['GET'])
def get_modules():
"""
Returns JSON describing all currently loaded modules.
TODO: describe returned JSON format
"""
moduleInfo = {}
for moduleName,module in main.modules.modules.iteritems():
info = module.info
@ -197,7 +222,11 @@ def start_restful_api(port=1337):
@app.route('/empire/api/listeners', methods=['GET'])
def get_listeners():
"""
Returns JSON describing all currently registered listeners.
TODO: describe returned JSON format
"""
activeListenersRaw = execute_db_query(conn, 'SELECT * FROM listeners')
activeListeners = {}
@ -208,32 +237,13 @@ def start_restful_api(port=1337):
return jsonify({'listeners' : activeListeners})
# @app.route('/empire/api/listeners', methods=['POST'])
# def creat_listener():
# if not request.json:
# abort(400)
# print request.json['title']
@app.route('/empire/api/listeners/id/<int:listener_id>', methods=['GET'])
def get_listener_id(listener_id):
activeListenersRaw = execute_db_query(conn, 'SELECT * FROM listeners')
activeListeners = {}
for activeListener in activeListenersRaw:
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit] = activeListener
if ID == listener_id:
activeListeners[name] = {'ID':ID, 'name':name, 'host':host, 'port':port, 'cert_path':cert_path, 'staging_key':staging_key, 'default_delay':default_delay, 'default_jitter':default_jitter, 'default_profile':default_profile, 'kill_date':kill_date, 'working_hours':working_hours, 'listener_type':listener_type, 'redirect_target':redirect_target, 'default_lost_limit':default_lost_limit}
return jsonify({'listeners' : activeListeners})
@app.route('/empire/api/listeners/name/<string:listener_name>', methods=['GET'])
@app.route('/empire/api/listeners/<string:listener_name>', methods=['GET'])
def get_listener_name(listener_name):
"""
Returns JSON describing the listener specified by listener_name.
TODO: describe returned JSON format
"""
activeListenersRaw = execute_db_query(conn, 'SELECT * FROM listeners')
activeListeners = {}
@ -245,9 +255,66 @@ def start_restful_api(port=1337):
return jsonify({'listeners' : activeListeners})
@app.route('/empire/api/listeners/options', methods=['GET'])
def get_listener_options():
"""
Returns JSON describing the current listener options.
TODO: describe returned JSON format
"""
return jsonify({'ListenerOptions' : main.listeners.options})
@app.route('/empire/api/listeners', methods=['POST'])
def start_listener():
"""
Starts a listener with options supplied in the POST.
TODO: describe returned JSON format
"""
# set all passed options
for option,values in request.json.iteritems():
returnVal = main.listeners.set_listener_option(option, values)
if not returnVal:
return jsonify({'error': 'Error setting listener value %s with option %s' %(option, values)})
valid = main.listeners.validate_listener_options()
if not valid:
return jsonify({'error': 'Error validating listener options'})
main.listeners.add_listener_from_config()
return jsonify({'ListenerOptions' : main.listeners.options})
@app.route('/empire/api/listeners/kill', methods=['POST'])
def kill_listener():
"""
Kills a listener with options supplied in the POST.
TODO: describe returned JSON format
"""
listenerName = request.json['Name']
if listenerName.lower() == "all":
main.listeners.killall()
else:
if listenerName != "" and main.listeners.is_listener_valid(listenerName):
main.listeners.shutdown_listener(listenerName)
main.listeners.delete_listener(listenerName)
return jsonify({'success': 'listener %s killed' %(listenerName)})
else:
return jsonify({'error': 'invalid listener name: %s' %(listenerName)})
@app.route('/empire/api/agents', methods=['GET'])
def get_agents():
"""
Returns JSON describing all currently registered agents.
TODO: describe returned JSON format
"""
activeAgentsRaw = execute_db_query(conn, 'SELECT * FROM agents')
activeAgents = {}
@ -258,22 +325,13 @@ def start_restful_api(port=1337):
return jsonify({'agents' : activeAgents})
@app.route('/empire/api/agents/id/<int:agent_id>', methods=['GET'])
def get_agents_id(agent_id):
activeAgentsRaw = execute_db_query(conn, 'SELECT * FROM agents WHERE id=?', [agent_id])
activeAgents = {}
for activeAgent in activeAgentsRaw:
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, checkin_time, lastseen_time, parent, children, servers, uris, old_uris, user_agent, headers, functions, kill_date, working_hours, ps_version, lost_limit, taskings, results] = activeAgent
activeAgents[name] = {"ID":ID, "sessionID":sessionID, "listener":listener, "name":name, "delay":delay, "jitter":jitter, "external_ip":external_ip, "internal_ip":internal_ip, "username":username, "high_integrity":high_integrity, "process_name":process_name, "process_id":process_id, "hostname":hostname, "os_details":os_details, "session_key":session_key, "checkin_time":checkin_time, "lastseen_time":lastseen_time, "parent":parent, "children":children, "servers":servers, "uris":uris, "old_uris":old_uris, "user_agent":user_agent, "headers":headers, "functions":functions, "kill_date":kill_date, "working_hours":working_hours, "ps_version":ps_version, "lost_limit":lost_limit, "taskings":taskings, "results":results}
return jsonify({'agents' : activeAgents})
@app.route('/empire/api/agents/name/<string:agent_name>', methods=['GET'])
@app.route('/empire/api/agents/<string:agent_name>', methods=['GET'])
def get_agents_name(agent_name):
"""
Returns JSON describing the agent specified by agent_name.
TODO: describe returned JSON format
"""
activeAgentsRaw = execute_db_query(conn, 'SELECT * FROM agents WHERE name=? OR session_id=?', [agent_name, agent_name])
activeAgents = {}
@ -284,9 +342,23 @@ def start_restful_api(port=1337):
return jsonify({'agents' : activeAgents})
@app.route('/empire/api/agents/<string:agent_name>', methods=['POST'])
def task_agent(agent_name):
"""
Tasks an agent with name agent_name.
TODO: describe returned JSON format
"""
pass
@app.route('/empire/api/reporting', methods=['GET'])
def get_reporting():
"""
Returns JSON describing the reporting events from the backend database.
TODO: describe returned JSON format
"""
reportingRaw = execute_db_query(conn, 'SELECT * FROM reporting')
reportingEvents = {}
@ -299,6 +371,12 @@ def start_restful_api(port=1337):
@app.route('/empire/api/reporting/agent/<string:reporting_agent>', methods=['GET'])
def get_reporting_agent(reporting_agent):
"""
Returns JSON describing the reporting events from the backend database for
the agent specified by reporting_agent.
TODO: describe returned JSON format
"""
# first resolve the supplied name to a sessionID
results = execute_db_query(conn, 'SELECT session_id FROM agents WHERE name=?', [reporting_agent])
@ -319,7 +397,12 @@ def start_restful_api(port=1337):
@app.route('/empire/api/reporting/type/<string:event_type>', methods=['GET'])
def get_reporting_type(event_type):
"""
Returns JSON describing the reporting events from the backend database for
the event type specified by event_type.
TODO: describe returned JSON format
"""
reportingRaw = execute_db_query(conn, 'SELECT * FROM reporting WHERE event_type=?', [event_type])
reportingEvents = {}
@ -332,7 +415,12 @@ def start_restful_api(port=1337):
@app.route('/empire/api/reporting/msg/<string:msg>', methods=['GET'])
def get_reporting_msg(msg):
"""
Returns JSON describing the reporting events from the backend database for
the any messages with *msg* specified by msg.
TODO: describe returned JSON format
"""
reportingRaw = execute_db_query(conn, "SELECT * FROM reporting WHERE message like ?", ['%'+msg+'%'])
reportingEvents = {}

View File

@ -218,7 +218,7 @@ class MainMenu(cmd.Cmd):
else:
num_modules = 0
num_listeners = self.listeners.listeners
num_listeners = self.listeners.get_listeners()
if(num_listeners):
num_listeners = len(num_listeners)
else:
@ -2647,7 +2647,7 @@ class StagerMenu(cmd.Cmd):
listenerName = self.stager.options['Listener']['Value']
if not main.listeners.is_listener_valid(listenerName):
if not self.mainMenu.listeners.is_listener_valid(listenerName):
print helpers.color("[!] Invalid listener ID or name.")
return False

View File

@ -200,12 +200,15 @@ class Listeners:
else:
self.options['Port']['Value'] = "80"
return True
elif option == "CertPath":
self.options[option]['Value'] = value
host = self.options["Host"]['Value']
# if we're setting a SSL cert path, but the host is specific at http
if host.startswith("http:"):
self.options["Host"]['Value'] = self.options["Host"]['Value'].replace("http:", "https:")
return True
elif option == "Port":
self.options[option]['Value'] = value
@ -214,11 +217,13 @@ class Listeners:
parts = host.split(":")
if len(parts) == 2 or len(parts) == 3:
self.options["Host"]['Value'] = parts[0] + ":" + parts[1] + ":" + str(value)
return True
elif option == "StagingKey":
# if the staging key isn't 32 characters, assume we're md5 hashing it
if len(value) != 32:
self.options[option]['Value'] = hashlib.md5(value).hexdigest()
return True
elif option in self.options:
@ -228,9 +233,11 @@ class Listeners:
# set the profile for hop.php for hop
parts = self.options['DefaultProfile']['Value'].split("|")
self.options['DefaultProfile']['Value'] = "/hop.php|" + "|".join(parts[1:])
return True
else:
print helpers.color("[!] Error: invalid option name")
return False
def get_listener_options(self):
"""