redo black
parent
a4b18d261b
commit
1f1ddedf34
49
cme/cmedb.py
49
cme/cmedb.py
|
@ -73,10 +73,7 @@ def complete_import(text, line):
|
|||
"""
|
||||
Tab-complete 'import' commands
|
||||
"""
|
||||
commands = (
|
||||
"empire",
|
||||
"metasploit"
|
||||
)
|
||||
commands = ("empire", "metasploit")
|
||||
mline = line.partition(" ")[2]
|
||||
offs = len(mline) - len(text)
|
||||
return [s[offs:] for s in commands if s.startswith(mline)]
|
||||
|
@ -138,7 +135,9 @@ class DatabaseNavigator(cmd.Cmd):
|
|||
# Users
|
||||
if command == "creds":
|
||||
if len(line) < 3:
|
||||
print("[-] invalid arguments, export creds <simple|detailed> <filename>")
|
||||
print(
|
||||
"[-] invalid arguments, export creds <simple|detailed> <filename>"
|
||||
)
|
||||
return
|
||||
|
||||
filename = line[2]
|
||||
|
@ -178,7 +177,9 @@ class DatabaseNavigator(cmd.Cmd):
|
|||
# Hosts
|
||||
elif command == "hosts":
|
||||
if len(line) < 3:
|
||||
print("[-] invalid arguments, export hosts <simple|detailed|signing> <filename>")
|
||||
print(
|
||||
"[-] invalid arguments, export hosts <simple|detailed|signing> <filename>"
|
||||
)
|
||||
return
|
||||
|
||||
csv_header_simple = (
|
||||
|
@ -225,19 +226,13 @@ class DatabaseNavigator(cmd.Cmd):
|
|||
# Shares
|
||||
elif command == "shares":
|
||||
if len(line) < 3:
|
||||
print("[-] invalid arguments, export shares <simple|detailed> <filename>")
|
||||
print(
|
||||
"[-] invalid arguments, export shares <simple|detailed> <filename>"
|
||||
)
|
||||
return
|
||||
|
||||
shares = self.db.get_shares()
|
||||
csv_header = (
|
||||
"id",
|
||||
"host",
|
||||
"userid",
|
||||
"name",
|
||||
"remark",
|
||||
"read",
|
||||
"write"
|
||||
)
|
||||
csv_header = ("id", "host", "userid", "name", "remark", "read", "write")
|
||||
filename = line[2]
|
||||
|
||||
if line[1].lower() == "simple":
|
||||
|
@ -267,16 +262,14 @@ class DatabaseNavigator(cmd.Cmd):
|
|||
# Local Admin
|
||||
elif command == "local_admins":
|
||||
if len(line) < 3:
|
||||
print("[-] invalid arguments, export local_admins <simple|detailed> <filename>")
|
||||
print(
|
||||
"[-] invalid arguments, export local_admins <simple|detailed> <filename>"
|
||||
)
|
||||
return
|
||||
|
||||
# These values don't change between simple and detailed
|
||||
local_admins = self.db.get_admin_relations()
|
||||
csv_header = (
|
||||
"id",
|
||||
"userid",
|
||||
"host"
|
||||
)
|
||||
csv_header = ("id", "userid", "host")
|
||||
filename = line[2]
|
||||
|
||||
if line[1].lower() == "simple":
|
||||
|
@ -300,7 +293,9 @@ class DatabaseNavigator(cmd.Cmd):
|
|||
print("[+] Local Admins exported")
|
||||
elif command == "dpapi":
|
||||
if len(line) < 3:
|
||||
print("[-] invalid arguments, export dpapi <simple|detailed> <filename>")
|
||||
print(
|
||||
"[-] invalid arguments, export dpapi <simple|detailed> <filename>"
|
||||
)
|
||||
return
|
||||
|
||||
# These values don't change between simple and detailed
|
||||
|
@ -346,7 +341,9 @@ class DatabaseNavigator(cmd.Cmd):
|
|||
filename = line[2]
|
||||
write_list(filename, writable_keys)
|
||||
else:
|
||||
print("[-] Invalid argument, specify creds, hosts, local_admins, shares or dpapi")
|
||||
print(
|
||||
"[-] Invalid argument, specify creds, hosts, local_admins, shares or dpapi"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def help_export():
|
||||
|
@ -366,9 +363,7 @@ class DatabaseNavigator(cmd.Cmd):
|
|||
return
|
||||
|
||||
if line == "empire":
|
||||
headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
headers = {"Content-Type": "application/json"}
|
||||
# Pull the username and password from the config file
|
||||
payload = {
|
||||
"username": self.config.get("Empire", "username"),
|
||||
|
|
|
@ -278,7 +278,9 @@ class connection(object):
|
|||
return True
|
||||
elif self.hash_login(domain, username, password):
|
||||
return True
|
||||
elif cred_type == "plaintext" and not self.over_fail_limit(username):
|
||||
elif cred_type == "plaintext" and not self.over_fail_limit(
|
||||
username
|
||||
):
|
||||
if self.args.kerberos:
|
||||
if self.kerberos_login(
|
||||
domain,
|
||||
|
|
|
@ -96,9 +96,7 @@ def main():
|
|||
|
||||
if args.darrell:
|
||||
links = (
|
||||
open(path_join(DATA_PATH, "videos_for_darrell.harambe"))
|
||||
.read()
|
||||
.splitlines()
|
||||
open(path_join(DATA_PATH, "videos_for_darrell.harambe")).read().splitlines()
|
||||
)
|
||||
try:
|
||||
webbrowser.open(random.choice(links))
|
||||
|
@ -121,11 +119,7 @@ def main():
|
|||
|
||||
module_server = None
|
||||
targets = []
|
||||
server_port_dict = {
|
||||
"http": 80,
|
||||
"https": 443,
|
||||
"smb": 445
|
||||
}
|
||||
server_port_dict = {"http": 80, "https": 443, "smb": 445}
|
||||
|
||||
if hasattr(args, "cred_id") and args.cred_id:
|
||||
for cred_id in args.cred_id:
|
||||
|
@ -211,21 +205,29 @@ def main():
|
|||
|
||||
if not module.opsec_safe:
|
||||
if ignore_opsec:
|
||||
cme_logger.debug(f"ignore_opsec is set in the configuration, skipping prompt")
|
||||
cme_logger.display(f"Ignore OPSEC in configuration is set and OPSEC unsafe module loaded")
|
||||
cme_logger.debug(
|
||||
f"ignore_opsec is set in the configuration, skipping prompt"
|
||||
)
|
||||
cme_logger.display(
|
||||
f"Ignore OPSEC in configuration is set and OPSEC unsafe module loaded"
|
||||
)
|
||||
else:
|
||||
ans = input(highlight(
|
||||
ans = input(
|
||||
highlight(
|
||||
"[!] Module is not opsec safe, are you sure you want to run this? [Y/n] ",
|
||||
"red",
|
||||
))
|
||||
)
|
||||
)
|
||||
if ans.lower() not in ["y", "yes", ""]:
|
||||
exit(1)
|
||||
|
||||
if not module.multiple_hosts and len(targets) > 1:
|
||||
ans = input(highlight(
|
||||
ans = input(
|
||||
highlight(
|
||||
"[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n] ",
|
||||
"red",
|
||||
))
|
||||
)
|
||||
)
|
||||
if ans.lower() not in ["y", "yes", ""]:
|
||||
exit(1)
|
||||
|
||||
|
@ -238,11 +240,7 @@ def main():
|
|||
|
||||
# loading a module server multiple times will obviously fail
|
||||
try:
|
||||
context = Context(
|
||||
db,
|
||||
cme_logger,
|
||||
args
|
||||
)
|
||||
context = Context(db, cme_logger, args)
|
||||
module_server = CMEServer(
|
||||
module,
|
||||
context,
|
||||
|
@ -256,19 +254,25 @@ def main():
|
|||
except Exception as e:
|
||||
cme_logger.error(f"Error loading module server for {module}: {e}")
|
||||
|
||||
cme_logger.debug(f"proto_object: {protocol_object}, type: {type(protocol_object)}")
|
||||
cme_logger.debug(
|
||||
f"proto_object: {protocol_object}, type: {type(protocol_object)}"
|
||||
)
|
||||
cme_logger.debug(f"proto object dir: {dir(protocol_object)}")
|
||||
# get currently set modules, otherwise default to empty list
|
||||
current_modules = getattr(protocol_object, "module", [])
|
||||
current_modules.append(module)
|
||||
setattr(protocol_object, "module", current_modules)
|
||||
cme_logger.debug(f"proto object module after adding: {protocol_object.module}")
|
||||
cme_logger.debug(
|
||||
f"proto object module after adding: {protocol_object.module}"
|
||||
)
|
||||
|
||||
if hasattr(args, "ntds") and args.ntds and not args.userntds:
|
||||
ans = input(highlight(
|
||||
ans = input(
|
||||
highlight(
|
||||
"[!] Dumping the ntds can crash the DC on Windows Server 2019. Use the option --user <user> to dump a specific user safely or the module -M ntdsutil [Y/n] ",
|
||||
"red",
|
||||
))
|
||||
)
|
||||
)
|
||||
if ans.lower() not in ["y", "yes", ""]:
|
||||
exit(1)
|
||||
|
||||
|
|
|
@ -7,40 +7,28 @@ from cme.cmedb import DatabaseNavigator, print_table, print_help
|
|||
|
||||
class navigator(DatabaseNavigator):
|
||||
def display_creds(self, creds):
|
||||
data = [[
|
||||
"CredID",
|
||||
"Admin On",
|
||||
"CredType",
|
||||
"Domain",
|
||||
"UserName",
|
||||
"Password"
|
||||
]]
|
||||
data = [["CredID", "Admin On", "CredType", "Domain", "UserName", "Password"]]
|
||||
|
||||
for cred in creds:
|
||||
links = self.db.get_admin_relations(user_id=cred[0])
|
||||
data.append([
|
||||
data.append(
|
||||
[
|
||||
cred[0], # cred_id
|
||||
str(len(links)) + " Host(s)",
|
||||
cred[1], # cred_type
|
||||
cred[2], # domain
|
||||
cred[3], # username
|
||||
cred[4], # password
|
||||
])
|
||||
]
|
||||
)
|
||||
print_table(data, title="Credentials")
|
||||
|
||||
def display_hosts(self, hosts):
|
||||
data = [[
|
||||
"HostID",
|
||||
"Admins",
|
||||
"IP",
|
||||
"Hostname",
|
||||
"Domain",
|
||||
"OS",
|
||||
"DB Instances"
|
||||
]]
|
||||
data = [["HostID", "Admins", "IP", "Hostname", "Domain", "OS", "DB Instances"]]
|
||||
for host in hosts:
|
||||
links = self.db.get_admin_relations(host_id=host[0])
|
||||
data.append([
|
||||
data.append(
|
||||
[
|
||||
host[0],
|
||||
str(len(links)) + " Cred(s)",
|
||||
host[1],
|
||||
|
@ -48,7 +36,8 @@ class navigator(DatabaseNavigator):
|
|||
host[3],
|
||||
host[4],
|
||||
host[5],
|
||||
])
|
||||
]
|
||||
)
|
||||
print_table(data, title="Hosts")
|
||||
|
||||
def do_hosts(self, line):
|
||||
|
@ -63,34 +52,16 @@ class navigator(DatabaseNavigator):
|
|||
if len(hosts) > 1:
|
||||
self.display_hosts(hosts)
|
||||
elif len(hosts) == 1:
|
||||
data = [[
|
||||
"HostID",
|
||||
"IP",
|
||||
"Hostname",
|
||||
"Domain",
|
||||
"OS"
|
||||
]]
|
||||
data = [["HostID", "IP", "Hostname", "Domain", "OS"]]
|
||||
host_id_list = []
|
||||
|
||||
for host in hosts:
|
||||
host_id_list.append(host[0])
|
||||
data.append([
|
||||
host[0],
|
||||
host[1],
|
||||
host[2],
|
||||
host[3],
|
||||
host[4]
|
||||
])
|
||||
data.append([host[0], host[1], host[2], host[3], host[4]])
|
||||
|
||||
print_table(data, title="Host(s)")
|
||||
|
||||
data = [[
|
||||
"CredID",
|
||||
"CredType",
|
||||
"Domain",
|
||||
"UserName",
|
||||
"Password"
|
||||
]]
|
||||
data = [["CredID", "CredType", "Domain", "UserName", "Password"]]
|
||||
for host_id in host_id_list:
|
||||
links = self.db.get_admin_relations(host_id=host_id)
|
||||
|
||||
|
@ -99,13 +70,7 @@ class navigator(DatabaseNavigator):
|
|||
creds = self.db.get_credentials(filter_term=cred_id)
|
||||
|
||||
for cred in creds:
|
||||
data.append([
|
||||
cred[0],
|
||||
cred[4],
|
||||
cred[1],
|
||||
cred[2],
|
||||
cred[3]
|
||||
])
|
||||
data.append([cred[0], cred[4], cred[1], cred[2], cred[3]])
|
||||
print_table(data, title="Credential(s) with Admin Access")
|
||||
|
||||
def do_creds(self, line):
|
||||
|
@ -143,34 +108,16 @@ class navigator(DatabaseNavigator):
|
|||
self.display_creds(creds)
|
||||
else:
|
||||
creds = self.db.get_credentials(filter_term=filter_term)
|
||||
data = [[
|
||||
"CredID",
|
||||
"CredType",
|
||||
"Domain",
|
||||
"UserName",
|
||||
"Password"
|
||||
]]
|
||||
data = [["CredID", "CredType", "Domain", "UserName", "Password"]]
|
||||
cred_id_list = []
|
||||
|
||||
for cred in creds:
|
||||
cred_id_list.append(cred[0])
|
||||
data.append([
|
||||
cred[0],
|
||||
cred[1],
|
||||
cred[2],
|
||||
cred[3],
|
||||
cred[4]
|
||||
])
|
||||
data.append([cred[0], cred[1], cred[2], cred[3], cred[4]])
|
||||
|
||||
print_table(data, title="Credential(s)")
|
||||
|
||||
data = [[
|
||||
"HostID",
|
||||
"IP",
|
||||
"Hostname",
|
||||
"Domain",
|
||||
"OS"
|
||||
]]
|
||||
data = [["HostID", "IP", "Hostname", "Domain", "OS"]]
|
||||
for cred_id in cred_id_list:
|
||||
links = self.db.get_admin_relations(user_id=cred_id)
|
||||
|
||||
|
@ -179,19 +126,16 @@ class navigator(DatabaseNavigator):
|
|||
hosts = self.db.get_hosts(host_id)
|
||||
|
||||
for host in hosts:
|
||||
data.append([
|
||||
host[0],
|
||||
host[1],
|
||||
host[2],
|
||||
host[3],
|
||||
host[4]
|
||||
])
|
||||
data.append([host[0], host[1], host[2], host[3], host[4]])
|
||||
print_table(data, title="Admin Access to Host(s)")
|
||||
|
||||
def do_clear_database(self, line):
|
||||
if (input(
|
||||
if (
|
||||
input(
|
||||
"This will destroy all data in the current database, are you SURE you want to run this? (y/n): "
|
||||
) == "y"):
|
||||
)
|
||||
== "y"
|
||||
):
|
||||
self.db.clear_database()
|
||||
|
||||
@staticmethod
|
||||
|
@ -207,10 +151,7 @@ class navigator(DatabaseNavigator):
|
|||
"""
|
||||
Tab-complete 'creds' commands
|
||||
"""
|
||||
commands = (
|
||||
"add",
|
||||
"remove"
|
||||
)
|
||||
commands = ("add", "remove")
|
||||
mline = line.partition(" ")[2]
|
||||
offs = len(mline) - len(text)
|
||||
return [s[offs:] for s in commands if s.startswith(mline)]
|
||||
|
@ -219,12 +160,7 @@ class navigator(DatabaseNavigator):
|
|||
"""
|
||||
Tab-complete 'creds' commands
|
||||
"""
|
||||
commands = (
|
||||
"add",
|
||||
"remove",
|
||||
"hash",
|
||||
"plaintext"
|
||||
)
|
||||
commands = ("add", "remove", "hash", "plaintext")
|
||||
mline = line.partition(" ")[2]
|
||||
offs = len(mline) - len(text)
|
||||
return [s[offs:] for s in commands if s.startswith(mline)]
|
||||
|
|
|
@ -174,22 +174,21 @@ class rdp(connection):
|
|||
|
||||
def print_host_info(self):
|
||||
if self.domain is None:
|
||||
self.logger.display(f"Probably old, doesn't not support HYBRID or HYBRID_EX (nla:{self.nla})")
|
||||
self.logger.display(
|
||||
f"Probably old, doesn't not support HYBRID or HYBRID_EX (nla:{self.nla})"
|
||||
)
|
||||
else:
|
||||
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain}) (nla:{self.nla})")
|
||||
self.logger.display(
|
||||
f"{self.server_os} (name:{self.hostname}) (domain:{self.domain}) (nla:{self.nla})"
|
||||
)
|
||||
return True
|
||||
|
||||
def create_conn_obj(self):
|
||||
self.target = RDPTarget(
|
||||
ip=self.host,
|
||||
domain="FAKE",
|
||||
timeout=self.args.rdp_timeout
|
||||
ip=self.host, domain="FAKE", timeout=self.args.rdp_timeout
|
||||
)
|
||||
self.auth = NTLMCredential(
|
||||
secret="pass",
|
||||
username="user",
|
||||
domain="FAKE",
|
||||
stype=asyauthSecret.PASS
|
||||
secret="pass", username="user", domain="FAKE", stype=asyauthSecret.PASS
|
||||
)
|
||||
|
||||
self.check_nla()
|
||||
|
@ -304,7 +303,9 @@ class rdp(connection):
|
|||
if not password:
|
||||
password = getenv("KRB5CCNAME") if not password else password
|
||||
if "/" in password:
|
||||
self.logger.fail("Kerberos ticket need to be on the local directory")
|
||||
self.logger.fail(
|
||||
"Kerberos ticket need to be on the local directory"
|
||||
)
|
||||
return False
|
||||
ccache = CCache.loadFile(getenv("KRB5CCNAME"))
|
||||
ticketCreds = ccache.credentials[0]
|
||||
|
@ -409,9 +410,7 @@ class rdp(connection):
|
|||
asyncio.run(self.connect_rdp())
|
||||
|
||||
self.admin_privs = True
|
||||
self.logger.success(
|
||||
f"{domain}\\{username}:{password} {self.mark_pwned()}"
|
||||
)
|
||||
self.logger.success(f"{domain}\\{username}:{password} {self.mark_pwned()}")
|
||||
if not self.args.local_auth:
|
||||
add_user_bh(username, domain, self.logger, self.config)
|
||||
if not self.args.continue_on_success:
|
||||
|
@ -487,9 +486,7 @@ class rdp(connection):
|
|||
async def screen(self):
|
||||
try:
|
||||
self.conn = RDPConnection(
|
||||
iosettings=self.iosettings,
|
||||
target=self.target,
|
||||
credentials=self.auth
|
||||
iosettings=self.iosettings, target=self.target, credentials=self.auth
|
||||
)
|
||||
await self.connect_rdp()
|
||||
except Exception as e:
|
||||
|
|
|
@ -168,9 +168,7 @@ class smb(connection):
|
|||
@staticmethod
|
||||
def proto_args(parser, std_parser, module_parser):
|
||||
smb_parser = parser.add_parser(
|
||||
"smb",
|
||||
help="own stuff using SMB",
|
||||
parents=[std_parser, module_parser]
|
||||
"smb", help="own stuff using SMB", parents=[std_parser, module_parser]
|
||||
)
|
||||
smb_parser.add_argument(
|
||||
"-H",
|
||||
|
@ -245,19 +243,14 @@ class smb(connection):
|
|||
)
|
||||
|
||||
cgroup = smb_parser.add_argument_group(
|
||||
"Credential Gathering",
|
||||
"Options for gathering credentials"
|
||||
"Credential Gathering", "Options for gathering credentials"
|
||||
)
|
||||
cegroup = cgroup.add_mutually_exclusive_group()
|
||||
cegroup.add_argument(
|
||||
"--sam",
|
||||
action="store_true",
|
||||
help="dump SAM hashes from target systems"
|
||||
"--sam", action="store_true", help="dump SAM hashes from target systems"
|
||||
)
|
||||
cegroup.add_argument(
|
||||
"--lsa",
|
||||
action="store_true",
|
||||
help="dump LSA secrets from target systems"
|
||||
"--lsa", action="store_true", help="dump LSA secrets from target systems"
|
||||
)
|
||||
cegroup.add_argument(
|
||||
"--ntds",
|
||||
|
@ -271,14 +264,13 @@ class smb(connection):
|
|||
choices={"password", "cookies"},
|
||||
nargs="?",
|
||||
const="password",
|
||||
help="dump DPAPI secrets from target systems, can dump cookies if you add \"cookies\"\n(default: password)",
|
||||
help='dump DPAPI secrets from target systems, can dump cookies if you add "cookies"\n(default: password)',
|
||||
)
|
||||
# cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
|
||||
# cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')
|
||||
|
||||
ngroup = smb_parser.add_argument_group(
|
||||
"Credential Gathering",
|
||||
"Options for gathering credentials"
|
||||
"Credential Gathering", "Options for gathering credentials"
|
||||
)
|
||||
ngroup.add_argument(
|
||||
"--mkfile",
|
||||
|
@ -286,30 +278,20 @@ class smb(connection):
|
|||
help="DPAPI option. File with masterkeys in form of {GUID}:SHA1",
|
||||
)
|
||||
ngroup.add_argument(
|
||||
"--pvk",
|
||||
action="store",
|
||||
help="DPAPI option. File with domain backupkey"
|
||||
"--pvk", action="store", help="DPAPI option. File with domain backupkey"
|
||||
)
|
||||
ngroup.add_argument(
|
||||
"--enabled",
|
||||
action="store_true",
|
||||
help="Only dump enabled targets from DC"
|
||||
"--enabled", action="store_true", help="Only dump enabled targets from DC"
|
||||
)
|
||||
ngroup.add_argument(
|
||||
"--user",
|
||||
dest="userntds",
|
||||
type=str,
|
||||
help="Dump selected user from DC"
|
||||
"--user", dest="userntds", type=str, help="Dump selected user from DC"
|
||||
)
|
||||
|
||||
egroup = smb_parser.add_argument_group(
|
||||
"Mapping/Enumeration",
|
||||
"Options for Mapping/Enumerating"
|
||||
"Mapping/Enumeration", "Options for Mapping/Enumerating"
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--shares",
|
||||
action="store_true",
|
||||
help="enumerate shares and access"
|
||||
"--shares", action="store_true", help="enumerate shares and access"
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--filter-shares",
|
||||
|
@ -317,24 +299,16 @@ class smb(connection):
|
|||
help="Filter share by access, option 'read' 'write' or 'read,write'",
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--sessions",
|
||||
action="store_true",
|
||||
help="enumerate active sessions"
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--disks",
|
||||
action="store_true",
|
||||
help="enumerate disks"
|
||||
"--sessions", action="store_true", help="enumerate active sessions"
|
||||
)
|
||||
egroup.add_argument("--disks", action="store_true", help="enumerate disks")
|
||||
egroup.add_argument(
|
||||
"--loggedon-users-filter",
|
||||
action="store",
|
||||
help="only search for specific user, works with regex",
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--loggedon-users",
|
||||
action="store_true",
|
||||
help="enumerate logged on users"
|
||||
"--loggedon-users", action="store_true", help="enumerate logged on users"
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--users",
|
||||
|
@ -365,9 +339,7 @@ class smb(connection):
|
|||
help="enumerate local groups, if a group is specified then its members are enumerated",
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--pass-pol",
|
||||
action="store_true",
|
||||
help="dump password policy"
|
||||
"--pass-pol", action="store_true", help="dump password policy"
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--rid-brute",
|
||||
|
@ -378,10 +350,7 @@ class smb(connection):
|
|||
help="enumerate users by bruteforcing RID's (default: 4000)",
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--wmi",
|
||||
metavar="QUERY",
|
||||
type=str,
|
||||
help="issues the specified WMI query"
|
||||
"--wmi", metavar="QUERY", type=str, help="issues the specified WMI query"
|
||||
)
|
||||
egroup.add_argument(
|
||||
"--wmi-namespace",
|
||||
|
@ -391,14 +360,10 @@ class smb(connection):
|
|||
)
|
||||
|
||||
sgroup = smb_parser.add_argument_group(
|
||||
"Spidering",
|
||||
"Options for spidering shares"
|
||||
"Spidering", "Options for spidering shares"
|
||||
)
|
||||
sgroup.add_argument(
|
||||
"--spider",
|
||||
metavar="SHARE",
|
||||
type=str,
|
||||
help="share to spider"
|
||||
"--spider", metavar="SHARE", type=str, help="share to spider"
|
||||
)
|
||||
sgroup.add_argument(
|
||||
"--spider-folder",
|
||||
|
@ -408,9 +373,7 @@ class smb(connection):
|
|||
help="folder to spider (default: root share directory)",
|
||||
)
|
||||
sgroup.add_argument(
|
||||
"--content",
|
||||
action="store_true",
|
||||
help="enable file content searching"
|
||||
"--content", action="store_true", help="enable file content searching"
|
||||
)
|
||||
sgroup.add_argument(
|
||||
"--exclude-dirs",
|
||||
|
@ -437,14 +400,11 @@ class smb(connection):
|
|||
help="max spider recursion depth (default: infinity & beyond)",
|
||||
)
|
||||
sgroup.add_argument(
|
||||
"--only-files",
|
||||
action="store_true",
|
||||
help="only spider files"
|
||||
"--only-files", action="store_true", help="only spider files"
|
||||
)
|
||||
|
||||
tgroup = smb_parser.add_argument_group(
|
||||
"Files",
|
||||
"Options for put and get remote files"
|
||||
"Files", "Options for put and get remote files"
|
||||
)
|
||||
tgroup.add_argument(
|
||||
"--put-file",
|
||||
|
@ -465,8 +425,7 @@ class smb(connection):
|
|||
)
|
||||
|
||||
cgroup = smb_parser.add_argument_group(
|
||||
"Command Execution",
|
||||
"Options for executing commands"
|
||||
"Command Execution", "Options for executing commands"
|
||||
)
|
||||
cgroup.add_argument(
|
||||
"--exec-method",
|
||||
|
@ -489,9 +448,7 @@ class smb(connection):
|
|||
help="force the PowerShell command to run in a 32-bit process",
|
||||
)
|
||||
cgroup.add_argument(
|
||||
"--no-output",
|
||||
action="store_true",
|
||||
help="do not retrieve command output"
|
||||
"--no-output", action="store_true", help="do not retrieve command output"
|
||||
)
|
||||
cegroup = cgroup.add_mutually_exclusive_group()
|
||||
cegroup.add_argument(
|
||||
|
@ -507,13 +464,10 @@ class smb(connection):
|
|||
help="execute the specified PowerShell command",
|
||||
)
|
||||
psgroup = smb_parser.add_argument_group(
|
||||
"Powershell Obfuscation",
|
||||
"Options for PowerShell script obfuscation"
|
||||
"Powershell Obfuscation", "Options for PowerShell script obfuscation"
|
||||
)
|
||||
psgroup.add_argument(
|
||||
"--obfs",
|
||||
action="store_true",
|
||||
help="Obfuscate PowerShell scripts"
|
||||
"--obfs", action="store_true", help="Obfuscate PowerShell scripts"
|
||||
)
|
||||
psgroup.add_argument(
|
||||
"--amsi-bypass",
|
||||
|
@ -676,11 +630,14 @@ class smb(connection):
|
|||
|
||||
from impacket.ldap import ldapasn1 as ldapasn1_impacket
|
||||
|
||||
results = [r for r in results if isinstance(r, ldapasn1_impacket.SearchResultEntry)]
|
||||
results = [
|
||||
r for r in results if isinstance(r, ldapasn1_impacket.SearchResultEntry)
|
||||
]
|
||||
if len(results) != 0:
|
||||
for host in results:
|
||||
values = {
|
||||
str(attr["type"]).lower(): str(attr["vals"][0]) for attr in host["attributes"]
|
||||
str(attr["type"]).lower(): str(attr["vals"][0])
|
||||
for attr in host["attributes"]
|
||||
}
|
||||
if "mslaps-encryptedpassword" in values:
|
||||
self.logger.fail(
|
||||
|
@ -694,7 +651,9 @@ class smb(connection):
|
|||
elif "ms-mcs-admpwd" in values:
|
||||
msMCSAdmPwd = values["ms-mcs-admpwd"]
|
||||
else:
|
||||
self.logger.fail("No result found with attribute ms-MCS-AdmPwd or msLAPS-Password")
|
||||
self.logger.fail(
|
||||
"No result found with attribute ms-MCS-AdmPwd or msLAPS-Password"
|
||||
)
|
||||
logging.debug(
|
||||
f"Host: {sAMAccountName:<20} Password: {msMCSAdmPwd} {self.hostname}"
|
||||
)
|
||||
|
@ -1768,7 +1727,9 @@ class smb(connection):
|
|||
policy_handle,
|
||||
lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation,
|
||||
)
|
||||
domain_sid = resp["PolicyInformation"]["PolicyAccountDomainInfo"]["DomainSid"].formatCanonical()
|
||||
domain_sid = resp["PolicyInformation"]["PolicyAccountDomainInfo"][
|
||||
"DomainSid"
|
||||
].formatCanonical()
|
||||
|
||||
so_far = 0
|
||||
simultaneous = 1000
|
||||
|
@ -1786,10 +1747,7 @@ class smb(connection):
|
|||
sids.append(f"{domain_sid}-{i:d}")
|
||||
try:
|
||||
lsat.hLsarLookupSids(
|
||||
dce,
|
||||
policy_handle,
|
||||
sids,
|
||||
lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta
|
||||
dce, policy_handle, sids, lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta
|
||||
)
|
||||
except DCERPCException as e:
|
||||
if str(e).find("STATUS_NONE_MAPPED") >= 0:
|
||||
|
@ -1803,22 +1761,28 @@ class smb(connection):
|
|||
for n, item in enumerate(resp["TranslatedNames"]["Names"]):
|
||||
if item["Use"] != SID_NAME_USE.SidTypeUnknown:
|
||||
rid = so_far + n
|
||||
domain = resp["ReferencedDomains"]["Domains"][item["DomainIndex"]]["Name"]
|
||||
domain = resp["ReferencedDomains"]["Domains"][item["DomainIndex"]][
|
||||
"Name"
|
||||
]
|
||||
user = item["Name"]
|
||||
sid_type = SID_NAME_USE.enumItems(item["Use"]).name
|
||||
self.logger.highlight(f"{rid}: {domain}\\{user} ({sid_type})")
|
||||
entries.append({
|
||||
entries.append(
|
||||
{
|
||||
"rid": rid,
|
||||
"domain": domain,
|
||||
"username": user,
|
||||
"sidtype": sid_type,
|
||||
})
|
||||
}
|
||||
)
|
||||
so_far += simultaneous
|
||||
dce.disconnect()
|
||||
return entries
|
||||
|
||||
def put_file(self):
|
||||
self.logger.display(f"Copying {self.args.put_file[0]} to {self.args.put_file[1]}")
|
||||
self.logger.display(
|
||||
f"Copying {self.args.put_file[0]} to {self.args.put_file[1]}"
|
||||
)
|
||||
with open(self.args.put_file[0], "rb") as file:
|
||||
try:
|
||||
self.conn.putFile(self.args.share, self.args.put_file[1], file.read)
|
||||
|
@ -1829,7 +1793,9 @@ class smb(connection):
|
|||
self.logger.fail(f"Error writing file to share {self.args.share}: {e}")
|
||||
|
||||
def get_file(self):
|
||||
self.logger.display(f"Copying {self.args.get_file[0]} to {self.args.get_file[1]}")
|
||||
self.logger.display(
|
||||
f"Copying {self.args.get_file[0]} to {self.args.get_file[1]}"
|
||||
)
|
||||
file_handle = self.args.get_file[1]
|
||||
if self.args.append_host:
|
||||
file_handle = f"{self.hostname}-{self.args.get_file[1]}"
|
||||
|
@ -1944,10 +1910,11 @@ class smb(connection):
|
|||
dc_conn = DPLootSMBConnection(dc_target)
|
||||
dc_conn.connect() # Connect to DC
|
||||
if dc_conn.is_admin():
|
||||
self.logger.success("User is Domain Administrator, exporting domain backupkey...")
|
||||
self.logger.success(
|
||||
"User is Domain Administrator, exporting domain backupkey..."
|
||||
)
|
||||
backupkey_triage = BackupkeyTriage(
|
||||
target=dc_target,
|
||||
conn=dc_conn
|
||||
target=dc_target, conn=dc_conn
|
||||
)
|
||||
backupkey = backupkey_triage.triage_backupkey()
|
||||
self.pvkbytes = backupkey.backupkey_v2
|
||||
|
@ -1980,7 +1947,9 @@ class smb(connection):
|
|||
|
||||
plaintexts = {
|
||||
username: password
|
||||
for _, _, username, password, _, _ in self.db.get_credentials(cred_type="plaintext")
|
||||
for _, _, username, password, _, _ in self.db.get_credentials(
|
||||
cred_type="plaintext"
|
||||
)
|
||||
}
|
||||
nthashes = {
|
||||
username: nt.split(":")[1] if ":" in nt else nt
|
||||
|
@ -1993,7 +1962,9 @@ class smb(connection):
|
|||
|
||||
# Collect User and Machine masterkeys
|
||||
try:
|
||||
self.logger.display("Collecting User and Machine masterkeys, grab a coffee and be patient...")
|
||||
self.logger.display(
|
||||
"Collecting User and Machine masterkeys, grab a coffee and be patient..."
|
||||
)
|
||||
masterkeys_triage = MasterkeysTriage(
|
||||
target=target,
|
||||
conn=conn,
|
||||
|
@ -2011,7 +1982,9 @@ class smb(connection):
|
|||
logging.fail("No masterkeys looted")
|
||||
return
|
||||
|
||||
self.logger.success(f"Got {highlight(len(masterkeys))} decrypted masterkeys. Looting secrets...")
|
||||
self.logger.success(
|
||||
f"Got {highlight(len(masterkeys))} decrypted masterkeys. Looting secrets..."
|
||||
)
|
||||
|
||||
try:
|
||||
# Collect User and Machine Credentials Manager secrets
|
||||
|
@ -2055,9 +2028,7 @@ class smb(connection):
|
|||
# Collect Chrome Based Browser stored secrets
|
||||
dump_cookies = True if self.args.dpapi == "cookies" else False
|
||||
browser_triage = BrowserTriage(
|
||||
target=target,
|
||||
conn=conn,
|
||||
masterkeys=masterkeys
|
||||
target=target, conn=conn, masterkeys=masterkeys
|
||||
)
|
||||
browser_credentials, cookies = browser_triage.triage_browsers(
|
||||
gather_cookies=dump_cookies
|
||||
|
@ -2089,9 +2060,7 @@ class smb(connection):
|
|||
try:
|
||||
# Collect User Internet Explorer stored secrets
|
||||
vaults_triage = VaultsTriage(
|
||||
target=target,
|
||||
conn=conn,
|
||||
masterkeys=masterkeys
|
||||
target=target, conn=conn, masterkeys=masterkeys
|
||||
)
|
||||
vaults = vaults_triage.triage_vaults()
|
||||
except Exception as e:
|
||||
|
|
|
@ -28,7 +28,9 @@ class CMESMBServer(threading.Thread):
|
|||
except Exception as e:
|
||||
errno, message = e.args
|
||||
if errno == 98 and message == "Address already in use":
|
||||
logger.error("Error starting SMB server on port 445: the port is already in use")
|
||||
logger.error(
|
||||
"Error starting SMB server on port 445: the port is already in use"
|
||||
)
|
||||
else:
|
||||
logger.error(f"Error starting SMB server on port 445: {message}")
|
||||
exit(1)
|
||||
|
|
Loading…
Reference in New Issue