Merge pull request #636 from malcomvetter/master
Capture OSX credentials from the Prompt Module in the Empire DBwebsockets-multiuser
commit
8b8741242e
|
@ -64,7 +64,7 @@ def validate_ip(IP):
|
|||
"""
|
||||
Uses iptools to validate an IP.
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
validate_IPv4 = iptools.ipv4.validate_ip(IP)
|
||||
validate_IPv6 = iptools.ipv6.validate_ip(IP)
|
||||
|
||||
|
@ -90,7 +90,7 @@ def validate_ntlm(data):
|
|||
|
||||
def generate_ip_list(s):
|
||||
"""
|
||||
Takes a comma separated list of IP/range/CIDR addresses and
|
||||
Takes a comma separated list of IP/range/CIDR addresses and
|
||||
generates an IP range list.
|
||||
"""
|
||||
|
||||
|
@ -102,7 +102,7 @@ def generate_ip_list(s):
|
|||
ranges = ""
|
||||
if s and s != "":
|
||||
parts = s.split(",")
|
||||
|
||||
|
||||
for part in parts:
|
||||
p = part.split("-")
|
||||
if len(p) == 2:
|
||||
|
@ -118,7 +118,7 @@ def generate_ip_list(s):
|
|||
return eval("iptools.IpRangeList("+ranges+")")
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -210,13 +210,13 @@ def strip_powershell_comments(data):
|
|||
Strip block comments, line comments, empty lines, verbose statements,
|
||||
and debug statements from a PowerShell source file.
|
||||
"""
|
||||
|
||||
|
||||
# strip block comments
|
||||
strippedCode = re.sub(re.compile('<#.*?#>', re.DOTALL), '\n', data)
|
||||
|
||||
# strip blank lines, lines starting with #, and verbose/debug statements
|
||||
strippedCode = "\n".join([line for line in strippedCode.split('\n') if ((line.strip() != '') and (not line.strip().startswith("#")) and (not line.strip().lower().startswith("write-verbose ")) and (not line.strip().lower().startswith("write-debug ")) )])
|
||||
|
||||
|
||||
return strippedCode
|
||||
|
||||
|
||||
|
@ -236,7 +236,7 @@ def get_powerview_psreflect_overhead(script):
|
|||
else:
|
||||
# otherwise extracting from PowerView
|
||||
pattern = re.compile(r'\n\$Mod =.*\[\'wtsapi32\'\]', re.DOTALL)
|
||||
|
||||
|
||||
try:
|
||||
return strip_powershell_comments(pattern.findall(script)[0])
|
||||
except:
|
||||
|
@ -246,7 +246,7 @@ def get_powerview_psreflect_overhead(script):
|
|||
|
||||
def get_dependent_functions(code, functionNames):
|
||||
"""
|
||||
Helper that takes a chunk of PowerShell code and a set of function
|
||||
Helper that takes a chunk of PowerShell code and a set of function
|
||||
names and returns the unique set of function names within the script block.
|
||||
"""
|
||||
|
||||
|
@ -306,13 +306,13 @@ def find_all_dependent_functions(functions, functionsToProcess, resultFunctions=
|
|||
def generate_dynamic_powershell_script(script, functionNames):
|
||||
"""
|
||||
Takes a PowerShell script and a function name (or array of function names,
|
||||
generates a dictionary of "[functionNames] -> functionCode", and recursively
|
||||
generates a dictionary of "[functionNames] -> functionCode", and recursively
|
||||
maps all dependent functions for the specified function name.
|
||||
|
||||
A script is returned with only the code necessary for the given
|
||||
functionName, stripped of comments and whitespace.
|
||||
|
||||
Note: for PowerView, it will also dynamically detect if psreflect
|
||||
Note: for PowerView, it will also dynamically detect if psreflect
|
||||
overhead is needed and add it to the result script.
|
||||
"""
|
||||
|
||||
|
@ -334,7 +334,7 @@ def generate_dynamic_powershell_script(script, functionNames):
|
|||
# start building the new result script
|
||||
functionDependencies = []
|
||||
|
||||
for functionName in functionNames:
|
||||
for functionName in functionNames:
|
||||
functionDependencies += find_all_dependent_functions(functions, functionName, [])
|
||||
functionDependencies = unique(functionDependencies)
|
||||
|
||||
|
@ -368,12 +368,12 @@ def parse_credentials(data):
|
|||
if parts[0].startswith("Hostname:"):
|
||||
return parse_mimikatz(data)
|
||||
|
||||
# collection/prompt output
|
||||
# powershell/collection/prompt output
|
||||
elif parts[0].startswith("[+] Prompted credentials:"):
|
||||
|
||||
|
||||
parts = parts[0].split("->")
|
||||
if len(parts) == 2:
|
||||
|
||||
|
||||
username = parts[1].split(":",1)[0].strip()
|
||||
password = parts[1].split(":",1)[1].strip()
|
||||
|
||||
|
@ -382,13 +382,20 @@ def parse_credentials(data):
|
|||
username = username.split("\\")[1].strip()
|
||||
else:
|
||||
domain = ""
|
||||
|
||||
|
||||
return [("plaintext", domain, username, password, "", "")]
|
||||
|
||||
else:
|
||||
print color("[!] Error in parsing prompted credential output.")
|
||||
return None
|
||||
|
||||
# python/collection/prompt (Mac OS)
|
||||
elif "text returned:" in parts[0]:
|
||||
parts2 = parts[0].split("text returned:")
|
||||
if len(parts2) >= 2:
|
||||
password = parts2[-1]
|
||||
return [("plaintext", "", "", password, "", "")]
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -430,7 +437,7 @@ def parse_mimikatz(data):
|
|||
|
||||
lines2 = match.split("\n")
|
||||
username, domain, password = "", "", ""
|
||||
|
||||
|
||||
for line in lines2:
|
||||
try:
|
||||
if "Username" in line:
|
||||
|
@ -443,7 +450,7 @@ def parse_mimikatz(data):
|
|||
pass
|
||||
|
||||
if username != "" and password != "" and password != "(null)":
|
||||
|
||||
|
||||
sid = ""
|
||||
|
||||
# substitute the FQDN in if it matches
|
||||
|
@ -564,7 +571,7 @@ def get_datetime():
|
|||
Return the current date/time
|
||||
"""
|
||||
return strftime("%Y-%m-%d %H:%M:%S", localtime())
|
||||
|
||||
|
||||
|
||||
def get_file_datetime():
|
||||
"""
|
||||
|
@ -608,7 +615,7 @@ def lhost():
|
|||
return socket.inet_ntoa(fcntl.ioctl(
|
||||
s.fileno(),
|
||||
0x8915, # SIOCGIFADDR
|
||||
struct.pack('256s', str(ifname[:15]))
|
||||
struct.pack('256s', ifname[:15])
|
||||
)[20:24])
|
||||
except IOError as e:
|
||||
return ""
|
||||
|
@ -627,7 +634,7 @@ def lhost():
|
|||
for ifname in interfaces:
|
||||
if "lo" not in ifname:
|
||||
try:
|
||||
ip = get_interface_ip(ifname)
|
||||
ip = get_interface_ip(ifname)
|
||||
if ip != "":
|
||||
break
|
||||
except:
|
||||
|
@ -640,11 +647,11 @@ def color(string, color=None):
|
|||
"""
|
||||
Change text color for the Linux terminal.
|
||||
"""
|
||||
|
||||
|
||||
attr = []
|
||||
# bold
|
||||
attr.append('1')
|
||||
|
||||
|
||||
if color:
|
||||
if color.lower() == "red":
|
||||
attr.append('31')
|
||||
|
@ -671,7 +678,7 @@ def color(string, color=None):
|
|||
def unique(seq, idfun=None):
|
||||
"""
|
||||
Uniquifies a list, order preserving.
|
||||
|
||||
|
||||
from http://www.peterbe.com/plog/uniqifiers-benchmark
|
||||
"""
|
||||
if idfun is None:
|
||||
|
@ -692,7 +699,7 @@ def unique(seq, idfun=None):
|
|||
def uniquify_tuples(tuples):
|
||||
"""
|
||||
Uniquifies Mimikatz tuples based on the password.
|
||||
|
||||
|
||||
cred format- (credType, domain, username, password, hostname, sid)
|
||||
"""
|
||||
seen = set()
|
||||
|
@ -737,7 +744,7 @@ def complete_path(text, line, arg=False):
|
|||
else:
|
||||
# if we have "command path"
|
||||
argData = line.split()[0:]
|
||||
|
||||
|
||||
if not argData or len(argData) == 1:
|
||||
completions = os.listdir('./')
|
||||
else:
|
||||
|
@ -745,7 +752,7 @@ def complete_path(text, line, arg=False):
|
|||
if part == '':
|
||||
dir = './'
|
||||
elif dir == '':
|
||||
dir = '/'
|
||||
dir = '/'
|
||||
|
||||
completions = []
|
||||
for f in os.listdir(dir):
|
||||
|
|
Loading…
Reference in New Issue