$OpenBSD: patch-examples_secretsdump_py,v 1.1 2016/05/26 09:29:23 dcoppa Exp $

commit 7c34db9948dd48275d85b6cee7675a2ac1d079b9
Author: asolino <bethus@gmail.com>
Date:   Sun May 1 21:14:45 2016 -0300

Fix ERROR_DS_DIFFERENT_REPL_EPOCHS when running against a domain
with dwReplEpoch != 0

commit fcf0c2ad8f52c277bd9072da93b7028607d6ba97
Author: asolino <bethus@gmail.com>
Date:   Fri May 13 19:24:46 2016 -0300

Fix when dumping CLEARTEXT password for machine accounts

commit de8b2556789a2e78cd0a16e38f40bc567a6873c9
Author: asolino <bethus@gmail.com>
Date:   Tue Mar 29 16:54:17 2016 -0300

Fixes offline dump of SAM hashes (no NTDS.DIT)

--- examples/secretsdump.py.orig	Wed Jan  6 09:54:29 2016
+++ examples/secretsdump.py	Wed May 25 09:05:16 2016
@@ -358,6 +358,24 @@ class RemoteOperations:
             logging.debug('DRSBind() answer')
             resp.dump()
 
+        # Let's dig into the answer to check the dwReplEpoch. This field should match the one we send as part of
+        # DRSBind's DRS_EXTENSIONS_INT(). If not, it will fail later when trying to sync data.
+        drsExtensionsInt = drsuapi.DRS_EXTENSIONS_INT()
+
+        # If dwExtCaps is not included in the answer, let's just add it so we can unpack DRS_EXTENSIONS_INT right.
+        ppextServer = ''.join(resp['ppextServer']['rgb']) + '\x00' * (len(drsuapi.DRS_EXTENSIONS_INT()) - resp['ppextServer']['cb'])
+        drsExtensionsInt.fromString(ppextServer)
+
+        if drsExtensionsInt['dwReplEpoch'] != 0:
+            # Different epoch, we have to call DRSBind again
+
+            if logging.getLogger().level == logging.DEBUG:
+                logging.debug("DC's dwReplEpoch != 0, setting it to %d and calling DRSBind again" % drsExtensionsInt['dwReplEpoch'] )
+            drs['dwReplEpoch'] = drsExtensionsInt['dwReplEpoch']
+            request['pextClient']['cb'] = len(drs)
+            request['pextClient']['rgb'] = list(str(drs))
+            resp = self.__drsr.request(request)
+
         self.__hDrs = resp['phDrs']
 
         # Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges
@@ -1529,7 +1547,12 @@ class NTDSHashes:
                 elif userProperty['PropertyName'].decode('utf-16le') == 'Primary:CLEARTEXT':
                     # [MS-SAMR] 3.1.1.8.11.5 Primary:CLEARTEXT Property
                     # This credential type is the cleartext password. The value format is the UTF-16 encoded cleartext password.
-                    answer = "%s:CLEARTEXT:%s" % (userName, unhexlify(userProperty['PropertyValue']).decode('utf-16le'))
+                    try:
+                        answer = "%s:CLEARTEXT:%s" % (userName, unhexlify(userProperty['PropertyValue']).decode('utf-16le'))
+                    except UnicodeDecodeError:
+                        # This could be because we're decoding a machine password. Printing it hex
+                        answer = "%s:CLEARTEXT:0x%s" % (userName, userProperty['PropertyValue'])
+
                     self.__clearTextPwds[answer] = None
                     if clearTextFile is not None:
                         self.__writeOutput(clearTextFile, answer + '\n')
@@ -1953,6 +1976,7 @@ class DumpSecrets:
         self.__justDCNTLM = options.just_dc_ntlm
         self.__pwdLastSet = options.pwd_last_set
         self.__resumeFileName = options.resumefile
+        self.__canProcessSAMLSA = True
 
         if options.hashes is not None:
             self.__lmhash, self.__nthash = options.hashes.split(':')
@@ -2030,38 +2054,39 @@ class DumpSecrets:
                         # Let's check whether target system stores LM Hashes
                         self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy()
                 except Exception, e:
+                    self.__canProcessSAMLSA = False
                     logging.error('RemoteOperations failed: %s' % str(e))
-                else:
-                    # If RemoteOperations succeeded, then we can extract SAM and LSA
-                    if self.__justDC is False and self.__justDCNTLM is False:
-                        try:
-                            if self.__isRemote is True:
-                                SAMFileName         = self.__remoteOps.saveSAM()
-                            else:
-                                SAMFileName         = self.__samHive
 
-                            self.__SAMHashes    = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote)
-                            self.__SAMHashes.dump()
-                            if self.__outputFileName is not None:
-                                self.__SAMHashes.export(self.__outputFileName)
-                        except Exception, e:
-                            logging.error('SAM hashes extraction failed: %s' % str(e))
+            # If RemoteOperations succeeded, then we can extract SAM and LSA
+            if self.__justDC is False and self.__justDCNTLM is False and self.__canProcessSAMLSA:
+                try:
+                    if self.__isRemote is True:
+                        SAMFileName         = self.__remoteOps.saveSAM()
+                    else:
+                        SAMFileName         = self.__samHive
 
-                        try:
-                            if self.__isRemote is True:
-                                SECURITYFileName = self.__remoteOps.saveSECURITY()
-                            else:
-                                SECURITYFileName = self.__securityHive
+                    self.__SAMHashes    = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote)
+                    self.__SAMHashes.dump()
+                    if self.__outputFileName is not None:
+                        self.__SAMHashes.export(self.__outputFileName)
+                except Exception, e:
+                    logging.error('SAM hashes extraction failed: %s' % str(e))
 
-                            self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, isRemote=self.__isRemote)
-                            self.__LSASecrets.dumpCachedHashes()
-                            if self.__outputFileName is not None:
-                                self.__LSASecrets.exportCached(self.__outputFileName)
-                            self.__LSASecrets.dumpSecrets()
-                            if self.__outputFileName is not None:
-                                self.__LSASecrets.exportSecrets(self.__outputFileName)
-                        except Exception, e:
-                            logging.error('LSA hashes extraction failed: %s' % str(e))
+                try:
+                    if self.__isRemote is True:
+                        SECURITYFileName = self.__remoteOps.saveSECURITY()
+                    else:
+                        SECURITYFileName = self.__securityHive
+
+                    self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, isRemote=self.__isRemote)
+                    self.__LSASecrets.dumpCachedHashes()
+                    if self.__outputFileName is not None:
+                        self.__LSASecrets.exportCached(self.__outputFileName)
+                    self.__LSASecrets.dumpSecrets()
+                    if self.__outputFileName is not None:
+                        self.__LSASecrets.exportSecrets(self.__outputFileName)
+                except Exception, e:
+                    logging.error('LSA hashes extraction failed: %s' % str(e))
 
             # NTDS Extraction we can try regardless of RemoteOperations failing. It might still work
             if self.__isRemote is True:
