mirror of
https://github.com/EmpireProject/EmPyre
synced 2026-06-08 10:57:53 +00:00
411 lines
15 KiB
Python
411 lines
15 KiB
Python
"""
|
|
|
|
Common terminal messages used across EmPyre.
|
|
|
|
Titles, agent displays, listener displays, etc.
|
|
|
|
"""
|
|
|
|
import os, textwrap
|
|
|
|
# EmPyre imports
|
|
import helpers
|
|
|
|
|
|
###############################################################
|
|
#
|
|
# Messages
|
|
#
|
|
###############################################################
|
|
|
|
def title(version):
|
|
"""
|
|
Print the tool title, with version.
|
|
"""
|
|
os.system('clear')
|
|
print "================================================================="
|
|
print " EmPyre: Python post-exploitation agent | [Version]: %s" % (version)
|
|
print '================================================================='
|
|
print """
|
|
______ ____
|
|
/ ____/___ ___ / __ \__ __________
|
|
/ __/ / __ `__ \/ /_/ / / / / ___/ _ \\
|
|
/ /___/ / / / / / ____/ /_/ / / / __/
|
|
/_____/_/ /_/ /_/_/ \__, /_/ \___/
|
|
/____/
|
|
|
|
"""
|
|
|
|
def wrap_string(data, width=40, indent=32, indentAll=False, followingHeader=None):
|
|
"""
|
|
Print a option description message in a nicely
|
|
wrapped and formatted paragraph.
|
|
|
|
followingHeader -> text that also goes on the first line
|
|
"""
|
|
|
|
data = str(data)
|
|
|
|
if len(data) > width:
|
|
lines = textwrap.wrap(textwrap.dedent(data).strip(), width=width)
|
|
|
|
if indentAll:
|
|
returnString = ' '*indent+lines[0]
|
|
if followingHeader:
|
|
returnString += " " + followingHeader
|
|
else:
|
|
returnString = lines[0]
|
|
if followingHeader:
|
|
returnString += " " + followingHeader
|
|
i = 1
|
|
while i < len(lines):
|
|
returnString += "\n"+' '*indent+(lines[i]).strip()
|
|
i += 1
|
|
return returnString
|
|
else:
|
|
return data.strip()
|
|
|
|
def wrap_columns(col1, col2, width1=24, width2=40, indent=31):
|
|
"""
|
|
Takes two strings of text and turns them into nicely formatted column output.
|
|
|
|
Used by display_module()
|
|
"""
|
|
|
|
lines1 = textwrap.wrap(textwrap.dedent(col1).strip(), width=width1)
|
|
lines2 = textwrap.wrap(textwrap.dedent(col2).strip(), width=width2)
|
|
|
|
result = ''
|
|
|
|
limit = max(len(lines1), len(lines2))
|
|
|
|
for x in xrange(limit):
|
|
|
|
if x < len(lines1):
|
|
if x != 0:
|
|
result += ' '*indent
|
|
result += '{line: <0{width}s}'.format(width=width1, line=lines1[x])
|
|
else:
|
|
if x == 0:
|
|
result += ' '*width1
|
|
else:
|
|
result += ' '*(indent + width1)
|
|
|
|
if x < len(lines2):
|
|
result += ' ' + '{line: <0{width}s}'.format(width=width2, line=lines2[x])
|
|
|
|
if x != limit-1:
|
|
result += "\n"
|
|
|
|
return result
|
|
|
|
def display_options(options, color=True):
|
|
"""
|
|
Take a dictionary and display it nicely.
|
|
"""
|
|
for key in options:
|
|
if color:
|
|
print "\t%s\t%s" % (helpers.color('{0: <16}'.format(key), "green"), wrap_string(options[key]))
|
|
else:
|
|
print "\t%s\t%s" % ('{0: <16}'.format(key), wrap_string(options[key]))
|
|
|
|
def agent_print(agents):
|
|
"""
|
|
Take an agent dictionary and display everything nicely.
|
|
"""
|
|
print ""
|
|
print helpers.color("[*] Active agents:\n")
|
|
print " Name Internal IP Machine Name Username Delay Last Seen"
|
|
print " --------- ----------- ------------ --------- ----- --------------------"
|
|
|
|
for agent in agents:
|
|
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_id, hostname, os_details, session_key, nonce, checkin_time, lastseen_time, servers, uris, old_uris, user_agent, headers, kill_date, working_hours, py_version, lost_limit, taskings, results] = agent
|
|
if str(high_integrity) == "1":
|
|
# add a * to the username if it's high integrity (root)
|
|
username = "*" + username
|
|
print " %.19s%.16s%.30s%.20s%.9s%.20s" % ('{0: <19}'.format(name), '{0: <16}'.format(internal_ip), '{0: <30}'.format(hostname), '{0: <20}'.format(username), '{0: <9}'.format(str(delay)+"/"+str(jitter)), lastseen_time)
|
|
|
|
print ""
|
|
|
|
def display_agents(agents):
|
|
"""
|
|
Take an agent dictionary and display everything nicely.
|
|
"""
|
|
if len(agents) > 0:
|
|
agent_print(agents)
|
|
else:
|
|
print helpers.color("[!] No agents currently registered ")
|
|
|
|
def display_staleagents(agents):
|
|
"""
|
|
Take a stale agent dictionary and display everything nicely.
|
|
"""
|
|
if len(agents) > 0:
|
|
agent_print(agents)
|
|
else:
|
|
print helpers.color("[!] No stale agents currently registered ")
|
|
|
|
def display_agent(agent):
|
|
"""
|
|
Display an agent all nice-like.
|
|
|
|
Takes in the tuple of the raw agent database results.
|
|
"""
|
|
|
|
# extract out database fields.
|
|
keys = ["ID", "sessionID", "listener", "name", "delay", "jitter", "external_ip", "internal_ip", "username", "high_integrity", "process_id", "hostname", "os_details", "session_key", "nonce", "checkin_time", "lastseen_time", "servers", "uris", "old_uris", "user_agent", "headers", "kill_date", "working_hours", "py_version", "lost_limit", "takings", "results"]
|
|
|
|
print helpers.color("\n[*] Agent info:\n")
|
|
|
|
# turn the agent into a keyed dictionary
|
|
agentInfo = dict(zip(keys, agent))
|
|
|
|
for key in agentInfo:
|
|
if key != "functions" and key != "takings" and key != "results":
|
|
print "\t%s\t%s" % (helpers.color('{0: <16}'.format(key), "blue"), wrap_string(agentInfo[key], width=70))
|
|
print ""
|
|
|
|
def display_listeners(listeners):
|
|
"""
|
|
Take a listeners list and display everything nicely.
|
|
"""
|
|
|
|
if len(listeners) > 0:
|
|
print ""
|
|
print helpers.color("[*] Active listeners:\n")
|
|
|
|
print " ID Name Host Type Delay/Jitter KillDate Redirect Target"
|
|
print " -- ---- ---- ------- ------------ -------- ---------------"
|
|
|
|
for listener in listeners:
|
|
|
|
[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] = listener
|
|
|
|
if not host.startswith("http"):
|
|
if cert_path and cert_path != "":
|
|
host = "https://" + host
|
|
else:
|
|
host = "http://" + host
|
|
host += ":" + str(port)
|
|
|
|
print " %s%s%s%s%s%s%s" % ('{0: <6}'.format(ID), '{0: <18}'.format(name), '{0: <37}'.format(host), '{0: <10}'.format(listener_type), '{0: <15}'.format(str(default_delay)+"/"+str(default_jitter)), '{0: <12}'.format(kill_date), redirect_target)
|
|
|
|
print ""
|
|
|
|
else:
|
|
print helpers.color("[!] No listeners currently active ")
|
|
|
|
def display_listener(options):
|
|
"""
|
|
Displays a listener's information structure.
|
|
"""
|
|
|
|
print "\nListener Options:\n"
|
|
print " Name Required Value Description"
|
|
print " ---- -------- ------- -----------"
|
|
|
|
for option, values in options.iteritems():
|
|
# if there's a long value length, wrap it
|
|
if len(str(values['Value'])) > 33:
|
|
print " %s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(wrap_string(values['Value'], width=32, indent=32, followingHeader=values['Description'])))
|
|
else:
|
|
print " %s%s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(values['Value']), values['Description'])
|
|
|
|
print "\n"
|
|
|
|
def display_listener_database(listener):
|
|
"""
|
|
Displays a listener's information from the database.
|
|
|
|
Transforms the tuple set to an options dictionary and calls display_listener().
|
|
"""
|
|
|
|
[ID, name, host, port, certPath, stagingKey, defaultDelay, defaultJitter, defaultProfile, killDate, workingHours, listenerType, redirectTarget, defaultLostLimit] = listener
|
|
|
|
options = {
|
|
'ID' : {
|
|
'Description' : 'Listener ID.',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'Name' : {
|
|
'Description' : 'Listener name.',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'Host' : {
|
|
'Description' : 'Hostname/IP for staging.',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'Type' : {
|
|
'Description' : 'Listener type (native, pivot, hop).',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'RedirectTarget' : {
|
|
'Description' : 'Listener target to redirect to for pivot/hop.',
|
|
'Required' : False,
|
|
'Value' : ''
|
|
},
|
|
'StagingKey' : {
|
|
'Description' : 'Staging key for initial agent negotiation.',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'DefaultDelay' : {
|
|
'Description' : 'Agent delay/reach back interval (in seconds).',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'DefaultJitter' : {
|
|
'Description' : 'Jitter in agent reachback interval (0.0-1.0).',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'DefaultLostLimit' : {
|
|
'Description' : 'Number of missed checkins before exiting',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'DefaultProfile' : {
|
|
'Description' : 'Default communication profile for the agent.',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'CertPath' : {
|
|
'Description' : 'Certificate path for https listeners.',
|
|
'Required' : False,
|
|
'Value' : ''
|
|
},
|
|
'Port' : {
|
|
'Description' : 'Port for the listener.',
|
|
'Required' : True,
|
|
'Value' : ''
|
|
},
|
|
'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' : ''
|
|
}
|
|
}
|
|
|
|
options['ID']['Value'] = ID
|
|
options['Name']['Value'] = name
|
|
options['Host']['Value'] = host
|
|
options['Port']['Value'] = port
|
|
options['CertPath']['Value'] = certPath
|
|
options['StagingKey']['Value'] = stagingKey
|
|
options['DefaultDelay']['Value'] = defaultDelay
|
|
options['DefaultJitter']['Value'] = defaultJitter
|
|
options['DefaultProfile']['Value'] = defaultProfile
|
|
options['KillDate']['Value'] = killDate
|
|
options['WorkingHours']['Value'] = workingHours
|
|
options['Type']['Value'] = listenerType
|
|
options['RedirectTarget']['Value'] = redirectTarget
|
|
options['DefaultLostLimit']['Value'] = defaultLostLimit
|
|
|
|
display_listener(options)
|
|
|
|
def display_stager(stagerName, stager):
|
|
"""
|
|
Displays a stager's information structure.
|
|
"""
|
|
|
|
print "\nName: " + stager.info['Name']
|
|
|
|
print "\nDescription:"
|
|
desc = wrap_string(stager.info['Description'], width=50, indent=2, indentAll=True)
|
|
if len(desc.splitlines()) == 1:
|
|
print " " + str(desc)
|
|
else:
|
|
print desc
|
|
|
|
# print out any options, if present
|
|
if stager.options:
|
|
print "\nOptions:\n"
|
|
print " Name Required Value Description"
|
|
print " ---- -------- ------- -----------"
|
|
|
|
for option, values in stager.options.iteritems():
|
|
print " %s%s%s%s" % ('{0: <17}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <18}'.format(values['Value']), wrap_string(values['Description'], indent=49))
|
|
|
|
print "\n"
|
|
|
|
def display_module(moduleName, module):
|
|
"""
|
|
Displays a module's information structure.
|
|
"""
|
|
|
|
print '\n{0: >17}'.format("Name: ") + str(module.info['Name'])
|
|
print '{0: >17}'.format("Module: ") + str(moduleName)
|
|
print '{0: >17}'.format("NeedsAdmin: ") + ("True" if module.info['NeedsAdmin'] else "False")
|
|
print '{0: >17}'.format("OpsecSafe: ") + ("True" if module.info['OpsecSafe'] else "False")
|
|
print '{0: >17}'.format("Background: ") + ("True" if module.info['Background'] else "False")
|
|
print '{0: >17}'.format("RunOnDisk: ") + ("True" if module.info['Background'] else "False")
|
|
print '{0: >17}'.format("OutputExtension: ") + (str(module.info['OutputExtension']) if module.info['OutputExtension'] else "None")
|
|
|
|
print "\nAuthors:"
|
|
for author in module.info['Author']:
|
|
print " " + author
|
|
|
|
print "\nDescription:"
|
|
desc = wrap_string(''.join(module.info['Description']), width=60, indent=2, indentAll=True)
|
|
if len(desc.splitlines()) == 1:
|
|
print " " + str(desc)
|
|
else:
|
|
print desc
|
|
|
|
# print out any options, if present
|
|
if module.options:
|
|
|
|
# get the size for the first column
|
|
maxNameLen = len(max(module.options.keys(), key=len))
|
|
|
|
print "\nOptions:\n"
|
|
print " %sRequired Value Description" %('{:<{}s}'.format("Name", maxNameLen+1))
|
|
print " %s-------- ------- -----------" %('{:<{}s}'.format("----", maxNameLen+1))
|
|
|
|
for option,values in module.options.iteritems():
|
|
print " %s%s%s" % ('{:<{}s}'.format(str(option), maxNameLen+1), '{0: <12}'.format(("True" if values['Required'] else "False")), wrap_columns(str(values['Value']), str(values['Description']), indent=(31 + (maxNameLen-16))))
|
|
|
|
print ""
|
|
|
|
def display_module_search(moduleName, module):
|
|
"""
|
|
Displays the name/description of a module for search results.
|
|
"""
|
|
|
|
print " " + helpers.color(moduleName, "blue") + "\n"
|
|
# width=40, indent=32, indentAll=False,
|
|
|
|
lines = textwrap.wrap(textwrap.dedent(''.join(module.info['Description'])).strip(), width=70)
|
|
for line in lines:
|
|
print "\t" + line
|
|
|
|
print "\n"
|
|
|
|
def display_credentials(creds):
|
|
|
|
print helpers.color("\nCredentials:\n", "blue")
|
|
print " CredID CredType Domain UserName Host Password"
|
|
print " ------ -------- ------ -------- ---- --------"
|
|
|
|
for cred in creds:
|
|
# (id, credtype, domain, username, password, host, notes, sid)
|
|
credID = cred[0]
|
|
credType = cred[1]
|
|
domain = cred[2]
|
|
username = cred[3]
|
|
password = cred[4]
|
|
host = cred[5]
|
|
|
|
print " %s%s%s%s%s%s" % ('{0: <8}'.format(credID), '{0: <11}'.format(credType), '{0: <25}'.format(domain), '{0: <17}'.format(username), '{0: <17}'.format(host), password)
|
|
|
|
print ""
|