402 lines
15 KiB
Python
402 lines
15 KiB
Python
|
|
"""
|
|
|
|
Listener handling functionality for Empire.
|
|
|
|
"""
|
|
import sys
|
|
import fnmatch
|
|
import imp
|
|
import helpers
|
|
import os
|
|
import pickle
|
|
import hashlib
|
|
import copy
|
|
|
|
class Listeners:
|
|
"""
|
|
Listener handling class.
|
|
"""
|
|
|
|
def __init__(self, MainMenu, args):
|
|
|
|
self.mainMenu = MainMenu
|
|
self.args = args
|
|
self.conn = MainMenu.conn
|
|
|
|
# loaded listener format:
|
|
# {"listenerModuleName": moduleInstance, ...}
|
|
self.loadedListeners = {}
|
|
|
|
# active listener format (these are listener modules that are actually instantiated)
|
|
# {"listenerName" : {moduleName: 'http', options: {setModuleOptions} }}
|
|
self.activeListeners = {}
|
|
|
|
self.load_listeners()
|
|
self.start_existing_listeners()
|
|
|
|
|
|
def load_listeners(self):
|
|
"""
|
|
Load listeners from the install + "/lib/listeners/*" path
|
|
"""
|
|
|
|
rootPath = "%s/lib/listeners/" % (self.mainMenu.installPath)
|
|
pattern = '*.py'
|
|
print helpers.color("[*] Loading listeners from: %s" % (rootPath))
|
|
|
|
for root, dirs, files in os.walk(rootPath):
|
|
for filename in fnmatch.filter(files, pattern):
|
|
filePath = os.path.join(root, filename)
|
|
|
|
# don't load up any of the templates
|
|
if fnmatch.fnmatch(filename, '*template.py'):
|
|
continue
|
|
|
|
# extract just the listener module name from the full path
|
|
listenerName = filePath.split("/lib/listeners/")[-1][0:-3]
|
|
|
|
# instantiate the listener module and save it to the internal cache
|
|
self.loadedListeners[listenerName] = imp.load_source(listenerName, filePath).Listener(self.mainMenu, [])
|
|
|
|
|
|
def set_listener_option(self, listenerName, option, value):
|
|
"""
|
|
Sets an option for the given listener module or all listener module.
|
|
"""
|
|
|
|
# for name, listener in self.listeners.iteritems():
|
|
# for listenerOption, optionValue in listener.options.iteritems():
|
|
# if listenerOption == option:
|
|
# listener.options[option]['Value'] = str(value)
|
|
|
|
for name, listenerObject in self.loadedListeners.iteritems():
|
|
|
|
if (listenerName.lower() == 'all' or listenerName == name) and (option in listenerObject.options):
|
|
|
|
# parse and auto-set some host parameters
|
|
if option == 'Host':
|
|
|
|
if not value.startswith('http'):
|
|
parts = value.split(':')
|
|
# if there's a current ssl cert path set, assume this is https
|
|
if ('CertPath' in listenerObject.options) and (listenerObject.options['CertPath']['Value'] != ''):
|
|
protocol = 'https'
|
|
defaultPort = 443
|
|
else:
|
|
protocol = 'http'
|
|
defaultPort = 80
|
|
|
|
elif value.startswith('https'):
|
|
value = value.split('//')[1]
|
|
parts = value.split(':')
|
|
protocol = 'https'
|
|
defaultPort = 443
|
|
|
|
elif value.startswith('http'):
|
|
value = value.split('//')[1]
|
|
parts = value.split(':')
|
|
protocol = 'http'
|
|
defaultPort = 80
|
|
|
|
if len(parts) != 1 and parts[-1].isdigit():
|
|
# if a port is specified with http://host:port
|
|
listenerObject.options['Host']['Value'] = "%s://%s" % (protocol, value)
|
|
if listenerObject.options['Port']['Value'] == '':
|
|
listenerObject.options['Port']['Value'] = parts[-1]
|
|
elif listenerObject.options['Port']['Value'] != '':
|
|
# otherwise, check if the port value was manually set
|
|
listenerObject.options['Host']['Value'] = "%s://%s:%s" % (protocol, value, listenerObject.options['Port']['Value'])
|
|
else:
|
|
# otherwise use default port
|
|
listenerObject.options['Host']['Value'] = "%s://%s" % (protocol, value)
|
|
if listenerObject.options['Port']['Value'] == '':
|
|
listenerObject.options['Port']['Value'] = defaultPort
|
|
|
|
return True
|
|
|
|
elif option == 'CertPath':
|
|
listenerObject.options[option]['Value'] = value
|
|
host = listenerObject.options['Host']['Value']
|
|
# if we're setting a SSL cert path, but the host is specific at http
|
|
if host.startswith('http:'):
|
|
listenerObject.options['Host']['Value'] = listenerObject.options['Host']['Value'].replace('http:', 'https:')
|
|
return True
|
|
|
|
if option == 'Port':
|
|
listenerObject.options[option]['Value'] = value
|
|
return True
|
|
|
|
elif option == 'StagingKey':
|
|
# if the staging key isn't 32 characters, assume we're md5 hashing it
|
|
value = str(value).strip()
|
|
if len(value) != 32:
|
|
stagingKeyHash = hashlib.md5(value).hexdigest()
|
|
print helpers.color('[!] Warning: staging key not 32 characters, using hash of staging key instead: %s' % (stagingKeyHash))
|
|
listenerObject.options[option]['Value'] = stagingKeyHash
|
|
else:
|
|
listenerObject.options[option]['Value'] = str(value)
|
|
return True
|
|
|
|
elif option in listenerObject.options:
|
|
|
|
listenerObject.options[option]['Value'] = value
|
|
|
|
# if option.lower() == 'type':
|
|
# if value.lower() == "hop":
|
|
# # 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
|
|
|
|
# if parts[0].lower() == 'defaultprofile' and os.path.exists(parts[1]):
|
|
# try:
|
|
# open_file = open(parts[1], 'r')
|
|
# profile_data_raw = open_file.readlines()
|
|
# open_file.close()
|
|
|
|
# profile_data = [l for l in profile_data_raw if not l.startswith('#' and l.strip() != '')]
|
|
# profile_data = profile_data[0].strip("\"")
|
|
|
|
# self.mainMenu.listeners.set_listener_option(parts[0], profile_data)
|
|
|
|
# except Exception:
|
|
# print helpers.color("[!] Error opening profile file %s" % (parts[1]))
|
|
|
|
else:
|
|
print helpers.color('[!] Error: invalid option name')
|
|
return False
|
|
|
|
|
|
def start_listener(self, moduleName, listenerObject):
|
|
"""
|
|
Takes a listener module object, starts the listener, adds the listener to the database, and
|
|
adds the listener to the current listener cache.
|
|
"""
|
|
|
|
category = listenerObject.info['Category']
|
|
name = listenerObject.options['Name']['Value']
|
|
nameBase = name
|
|
|
|
if not listenerObject.validate_options():
|
|
return
|
|
|
|
i = 1
|
|
while name in self.activeListeners.keys():
|
|
name = "%s%s" % (nameBase, i)
|
|
|
|
listenerObject.options['Name']['Value'] = name
|
|
|
|
try:
|
|
print helpers.color("[*] Starting listener '%s'" % (name))
|
|
success = listenerObject.start(name=name)
|
|
|
|
if success:
|
|
print helpers.color('[+] Listener successfully started!')
|
|
listenerOptions = copy.deepcopy(listenerObject.options)
|
|
self.activeListeners[name] = {'moduleName': moduleName, 'options':listenerOptions}
|
|
pickledOptions = pickle.dumps(listenerObject.options)
|
|
cur = self.conn.cursor()
|
|
cur.execute("INSERT INTO listeners (name, module, listener_category, options) VALUES (?,?,?,?)", [name, moduleName, category, pickledOptions])
|
|
cur.close()
|
|
else:
|
|
print helpers.color('[!] Listener failed to start!')
|
|
|
|
except Exception as e:
|
|
if name in self.activeListeners:
|
|
del self.activeListeners[name]
|
|
print helpers.color("[!] Error starting listener: %s" % (e))
|
|
|
|
|
|
def start_existing_listeners(self):
|
|
"""
|
|
Startup any listeners that are currently in the database.
|
|
"""
|
|
oldFactory = self.conn.row_factory
|
|
self.conn.row_factory = helpers.dict_factory
|
|
cur = self.conn.cursor()
|
|
cur.execute("SELECT id,name,module,listener_type,listener_category,options FROM listeners")
|
|
results = cur.fetchall()
|
|
cur.close()
|
|
|
|
for result in results:
|
|
listenerName = result['name']
|
|
moduleName = result['module']
|
|
nameBase = listenerName
|
|
|
|
|
|
i = 1
|
|
while listenerName in self.activeListeners.keys():
|
|
listenerName = "%s%s" % (nameBase, i)
|
|
|
|
# unpickle all the listener options
|
|
options = pickle.loads(result['options'])
|
|
|
|
try:
|
|
listenerModule = self.loadedListeners[moduleName]
|
|
|
|
for option, value in options.iteritems():
|
|
listenerModule.options[option] = value
|
|
|
|
print helpers.color("[*] Starting listener '%s'" % (listenerName))
|
|
if moduleName == 'redirector':
|
|
success = True
|
|
else:
|
|
success = listenerModule.start(name=listenerName)
|
|
|
|
if success:
|
|
print helpers.color('[+] Listener successfully started!')
|
|
listenerOptions = copy.deepcopy(listenerModule.options)
|
|
self.activeListeners[listenerName] = {'moduleName': moduleName, 'options':listenerOptions}
|
|
else:
|
|
print helpers.color('[!] Listener failed to start!')
|
|
|
|
except Exception as e:
|
|
if listenerName in self.activeListeners:
|
|
del self.activeListeners[listenerName]
|
|
print helpers.color("[!] Error starting listener: %s" % (e))
|
|
|
|
self.conn.row_factory = oldFactory
|
|
|
|
|
|
def kill_listener(self, listenerName):
|
|
"""
|
|
Shut down the server associated with a listenerName and delete the
|
|
listener from the database.
|
|
|
|
To kill all listeners, use listenerName == 'all'
|
|
"""
|
|
|
|
if listenerName.lower() == 'all':
|
|
listenerNames = self.activeListeners.keys()
|
|
else:
|
|
listenerNames = [listenerName]
|
|
|
|
for listenerName in listenerNames:
|
|
if listenerName not in self.activeListeners:
|
|
print helpers.color("[!] Listener '%s' not active!" % (listenerName))
|
|
return False
|
|
|
|
# shut down the listener and remove it from the cache
|
|
if self.mainMenu.listeners.get_listener_module(listenerName) == 'redirector':
|
|
# remove the listener object from the internal cache
|
|
del self.activeListeners[listenerName]
|
|
self.conn.row_factory = None
|
|
cur = self.conn.cursor()
|
|
cur.execute("DELETE FROM listeners WHERE name=?", [listenerName])
|
|
cur.close()
|
|
continue
|
|
|
|
self.shutdown_listener(listenerName)
|
|
|
|
# remove the listener from the database
|
|
self.conn.row_factory = None
|
|
cur = self.conn.cursor()
|
|
cur.execute("DELETE FROM listeners WHERE name=?", [listenerName])
|
|
cur.close()
|
|
|
|
|
|
def shutdown_listener(self, listenerName):
|
|
"""
|
|
Shut down the server associated with a listenerName, but DON'T
|
|
delete it from the database.
|
|
"""
|
|
|
|
if listenerName.lower() == 'all':
|
|
listenerNames = self.activeListeners.keys()
|
|
else:
|
|
listenerNames = [listenerName]
|
|
|
|
for listenerName in listenerNames:
|
|
if listenerName not in self.activeListeners:
|
|
print helpers.color("[!] Listener '%s' doesn't exist!" % (listenerName))
|
|
return False
|
|
|
|
# retrieve the listener module for this listener name
|
|
activeListenerModuleName = self.activeListeners[listenerName]['moduleName']
|
|
activeListenerModule = self.loadedListeners[activeListenerModuleName]
|
|
|
|
if activeListenerModuleName == 'redirector':
|
|
print helpers.color("[!] skipping redirector listener %s. Start/Stop actions can only initiated by the user." % (listenerName))
|
|
continue
|
|
|
|
# signal the listener module to shut down the thread for this particular listener instance
|
|
activeListenerModule.shutdown(name=listenerName)
|
|
|
|
# remove the listener object from the internal cache
|
|
del self.activeListeners[listenerName]
|
|
|
|
|
|
def is_listener_valid(self, name):
|
|
return name in self.activeListeners
|
|
|
|
|
|
def get_listener_id(self, name):
|
|
"""
|
|
Resolve a name to listener ID.
|
|
"""
|
|
oldFactory = self.conn.row_factory
|
|
self.conn.row_factory = None
|
|
cur = self.conn.cursor()
|
|
cur.execute('SELECT id FROM listeners WHERE name=? or id=?', [name, name])
|
|
results = cur.fetchone()
|
|
cur.close()
|
|
self.conn.row_factory = oldFactory
|
|
|
|
if results:
|
|
return results[0]
|
|
else:
|
|
return None
|
|
|
|
|
|
def get_listener_name(self, listenerId):
|
|
"""
|
|
Resolve a listener ID to a name.
|
|
"""
|
|
cur = self.conn.cursor()
|
|
cur.execute('SELECT name FROM listeners WHERE name=? or id=?', [listenerId, listenerId])
|
|
results = cur.fetchone()
|
|
cur.close()
|
|
|
|
if results:
|
|
return results[0]
|
|
else:
|
|
return None
|
|
|
|
|
|
def get_listener_module(self, listenerName):
|
|
"""
|
|
Resolve a listener name to the module used to instantiate it.
|
|
"""
|
|
cur = self.conn.cursor()
|
|
cur.execute('SELECT module FROM listeners WHERE name=?', [listenerName])
|
|
results = cur.fetchone()
|
|
cur.close()
|
|
|
|
if results:
|
|
return results[0]
|
|
else:
|
|
return None
|
|
|
|
def get_listener_options(self):
|
|
"""
|
|
Return the options for a listener type
|
|
"""
|
|
cur = self.conn.cursor()
|
|
cur.execute('SELECT options FROM listeners')
|
|
results = cur.fetchall()
|
|
cur.close()
|
|
|
|
if results:
|
|
return results[0][0]
|
|
else:
|
|
return None
|
|
|
|
|
|
def get_listener_names(self):
|
|
"""
|
|
Return all current listener names.
|
|
"""
|
|
return self.activeListeners.keys()
|