254 lines
9.1 KiB
Python
254 lines
9.1 KiB
Python
import base64
|
|
import random
|
|
|
|
# Empire imports
|
|
from lib.common import helpers
|
|
from lib.common import agents
|
|
from lib.common import encryption
|
|
from lib.common import packets
|
|
from lib.common import messages
|
|
|
|
|
|
class Listener:
|
|
|
|
def __init__(self, mainMenu, params=[]):
|
|
|
|
self.info = {
|
|
'Name': 'Template',
|
|
|
|
'Author': ['@harmj0y'],
|
|
|
|
'Description': ("Listener template"),
|
|
|
|
# categories - client_server, peer_to_peer, broadcast, third_party
|
|
'Category' : ('client_server'),
|
|
|
|
'Comments': []
|
|
}
|
|
|
|
# any options needed by the stager, settable during runtime
|
|
self.options = {
|
|
# format:
|
|
# value_name : {description, required, default_value}
|
|
|
|
'Name' : {
|
|
'Description' : 'Listener name.',
|
|
'Required' : True,
|
|
'Value' : 'http_foreign'
|
|
},
|
|
'Host' : {
|
|
'Description' : 'Hostname/IP for staging.',
|
|
'Required' : True,
|
|
'Value' : "http://%s:%s" % (helpers.lhost(), 80)
|
|
},
|
|
'Port' : {
|
|
'Description' : 'Port for the listener.',
|
|
'Required' : True,
|
|
'Value' : 80
|
|
},
|
|
'StagingKey' : {
|
|
'Description' : 'Staging key for initial agent negotiation.',
|
|
'Required' : True,
|
|
'Value' : '2c103f2c4ed1e59c0b4e2e01821770fa'
|
|
},
|
|
'DefaultDelay' : {
|
|
'Description' : 'Agent delay/reach back interval (in seconds).',
|
|
'Required' : True,
|
|
'Value' : 5
|
|
},
|
|
'DefaultJitter' : {
|
|
'Description' : 'Jitter in agent reachback interval (0.0-1.0).',
|
|
'Required' : True,
|
|
'Value' : 0.0
|
|
},
|
|
'DefaultLostLimit' : {
|
|
'Description' : 'Number of missed checkins before exiting',
|
|
'Required' : True,
|
|
'Value' : 60
|
|
},
|
|
'DefaultProfile' : {
|
|
'Description' : 'Default communication profile for the agent.',
|
|
'Required' : True,
|
|
'Value' : "/admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
|
|
},
|
|
'KillDate' : {
|
|
'Description' : 'Date for the listener to exit (MM/dd/yyyy).',
|
|
'Required' : False,
|
|
'Value' : ''
|
|
},
|
|
'WorkingHours' : {
|
|
'Description' : 'Hours for the agent to operate (09:00-17:00).',
|
|
'Required' : False,
|
|
'Value' : ''
|
|
}
|
|
}
|
|
|
|
# required:
|
|
self.mainMenu = mainMenu
|
|
self.threads = {} # used to keep track of any threaded instances of this server
|
|
|
|
# optional/specific for this module
|
|
|
|
|
|
# set the default staging key to the controller db default
|
|
self.options['StagingKey']['Value'] = str(helpers.get_config('staging_key')[0])
|
|
|
|
|
|
def default_response(self):
|
|
"""
|
|
If there's a default response expected from the server that the client needs to ignore,
|
|
(i.e. a default HTTP page), put the generation here.
|
|
"""
|
|
print helpers.color("[!] default_response() not implemented for listeners/template")
|
|
return ''
|
|
|
|
|
|
def validate_options(self):
|
|
"""
|
|
Validate all options for this listener.
|
|
"""
|
|
|
|
for key in self.options:
|
|
if self.options[key]['Required'] and (str(self.options[key]['Value']).strip() == ''):
|
|
print helpers.color("[!] Option \"%s\" is required." % (key))
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def generate_launcher(self, encode=True, userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None):
|
|
"""
|
|
Generate a basic launcher for the specified listener.
|
|
"""
|
|
|
|
if not language:
|
|
print helpers.color('[!] listeners/template generate_launcher(): no language specified!')
|
|
return None
|
|
|
|
if listenerName and (listenerName in self.mainMenu.listeners.activeListeners):
|
|
|
|
# extract the set options for this instantiated listener
|
|
listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options']
|
|
host = listenerOptions['Host']['Value']
|
|
stagingKey = listenerOptions['StagingKey']['Value']
|
|
profile = listenerOptions['DefaultProfile']['Value']
|
|
uris = [a.strip('/') for a in profile.split('|')[0].split(',')]
|
|
stage0 = random.choice(uris)
|
|
launchURI = "%s/%s" % (host, stage0)
|
|
|
|
if language.startswith('po'):
|
|
# PowerShell
|
|
return ''
|
|
|
|
if language.startswith('py'):
|
|
# Python
|
|
return ''
|
|
|
|
else:
|
|
print helpers.color("[!] listeners/template generate_launcher(): invalid language specification: only 'powershell' and 'python' are current supported for this module.")
|
|
|
|
else:
|
|
print helpers.color("[!] listeners/template generate_launcher(): invalid listener name specification!")
|
|
|
|
|
|
def generate_stager(self, listenerOptions, encode=False, encrypt=True, language=None):
|
|
"""
|
|
If you want to support staging for the listener module, generate_stager must be
|
|
implemented to return the stage1 key-negotiation stager code.
|
|
"""
|
|
print helpers.color("[!] generate_stager() not implemented for listeners/template")
|
|
return ''
|
|
|
|
|
|
def generate_agent(self, listenerOptions, language=None):
|
|
"""
|
|
If you want to support staging for the listener module, generate_agent must be
|
|
implemented to return the actual staged agent code.
|
|
"""
|
|
print helpers.color("[!] generate_agent() not implemented for listeners/template")
|
|
return ''
|
|
|
|
|
|
def generate_comms(self, listenerOptions, language=None):
|
|
"""
|
|
Generate just the agent communication code block needed for communications with this listener.
|
|
This is so agents can easily be dynamically updated for the new listener.
|
|
|
|
This should be implemented for the module.
|
|
"""
|
|
|
|
if language:
|
|
if language.lower() == 'powershell':
|
|
|
|
updateServers = """
|
|
$Script:ControlServers = @("%s");
|
|
$Script:ServerIndex = 0;
|
|
""" % (listenerOptions['Host']['Value'])
|
|
|
|
getTask = """
|
|
function script:Get-Task {
|
|
|
|
|
|
}
|
|
"""
|
|
|
|
sendMessage = """
|
|
function script:Send-Message {
|
|
param($Packets)
|
|
|
|
if($Packets) {
|
|
|
|
}
|
|
}
|
|
"""
|
|
|
|
return updateServers + getTask + sendMessage + "\n'New agent comms registered!'"
|
|
|
|
elif language.lower() == 'python':
|
|
# send_message()
|
|
pass
|
|
else:
|
|
print helpers.color("[!] listeners/template generate_comms(): invalid language specification, only 'powershell' and 'python' are current supported for this module.")
|
|
else:
|
|
print helpers.color('[!] listeners/template generate_comms(): no language specified!')
|
|
|
|
|
|
def start(self, name=''):
|
|
"""
|
|
If a server component needs to be started, implement the kick off logic
|
|
here and the actual server code in another function to facilitate threading
|
|
(i.e. start_server() in the http listener).
|
|
"""
|
|
|
|
# listenerOptions = self.options
|
|
# if name and name != '':
|
|
# self.threads[name] = helpers.KThread(target=self.start_server, args=(listenerOptions,))
|
|
# self.threads[name].start()
|
|
# time.sleep(1)
|
|
# # returns True if the listener successfully started, false otherwise
|
|
# return self.threads[name].is_alive()
|
|
# else:
|
|
# name = listenerOptions['Name']['Value']
|
|
# self.threads[name] = helpers.KThread(target=self.start_server, args=(listenerOptions,))
|
|
# self.threads[name].start()
|
|
# time.sleep(1)
|
|
# # returns True if the listener successfully started, false otherwise
|
|
# return self.threads[name].is_alive()
|
|
|
|
return True
|
|
|
|
|
|
def shutdown(self, name=''):
|
|
"""
|
|
If a server component was started, implement the logic that kills the particular
|
|
named listener here.
|
|
"""
|
|
|
|
# if name and name != '':
|
|
# print helpers.color("[!] Killing listener '%s'" % (name))
|
|
# self.threads[name].kill()
|
|
# else:
|
|
# print helpers.color("[!] Killing listener '%s'" % (self.options['Name']['Value']))
|
|
# self.threads[self.options['Name']['Value']].kill()
|
|
|
|
pass |