github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/samrdump.py (about)

     1  #!/usr/bin/env python
     2  # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
     3  #
     4  # This software is provided under under a slightly modified version
     5  # of the Apache Software License. See the accompanying LICENSE file
     6  # for more information.
     7  #
     8  # Description: DCE/RPC SAMR dumper.
     9  #
    10  # Author:
    11  #  Javier Kohen <jkohen@coresecurity.com>
    12  #  Alberto Solino (@agsolino)
    13  #
    14  # Reference for:
    15  #  DCE/RPC for SAMR
    16  from __future__ import division
    17  from __future__ import print_function
    18  import sys
    19  import logging
    20  import argparse
    21  import codecs
    22  
    23  from datetime import datetime
    24  from impacket.examples import logger
    25  from impacket import version
    26  from impacket.nt_errors import STATUS_MORE_ENTRIES
    27  from impacket.dcerpc.v5 import transport, samr
    28  from impacket.dcerpc.v5.rpcrt import DCERPCException
    29  from impacket.smb import SMB_DIALECT
    30  
    31  class ListUsersException(Exception):
    32      pass
    33  
    34  class SAMRDump:
    35      def __init__(self, username='', password='', domain='', hashes=None,
    36                   aesKey=None, doKerberos=False, kdcHost=None, port=445, csvOutput=False):
    37  
    38          self.__username = username
    39          self.__password = password
    40          self.__domain = domain
    41          self.__lmhash = ''
    42          self.__nthash = ''
    43          self.__aesKey = aesKey
    44          self.__doKerberos = doKerberos
    45          self.__kdcHost = kdcHost
    46          self.__port = port
    47          self.__csvOutput = csvOutput
    48  
    49          if hashes is not None:
    50              self.__lmhash, self.__nthash = hashes.split(':')
    51  
    52      @staticmethod
    53      def getUnixTime(t):
    54          t -= 116444736000000000
    55          t /= 10000000
    56          return t
    57  
    58      def dump(self, remoteName, remoteHost):
    59          """Dumps the list of users and shares registered present at
    60          remoteName. remoteName is a valid host name or IP address.
    61          """
    62  
    63          entries = []
    64  
    65          logging.info('Retrieving endpoint list from %s' % remoteName)
    66  
    67          stringbinding = r'ncacn_np:%s[\pipe\samr]' % remoteName
    68          logging.debug('StringBinding %s'%stringbinding)
    69          rpctransport = transport.DCERPCTransportFactory(stringbinding)
    70          rpctransport.set_dport(self.__port)
    71          rpctransport.setRemoteHost(remoteHost)
    72  
    73          if hasattr(rpctransport,'preferred_dialect'):
    74              rpctransport.preferred_dialect(SMB_DIALECT)
    75          if hasattr(rpctransport, 'set_credentials'):
    76              # This method exists only for selected protocol sequences.
    77              rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
    78                                           self.__nthash, self.__aesKey)
    79          rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
    80  
    81          try:
    82              entries = self.__fetchList(rpctransport)
    83          except Exception as e:
    84              logging.critical(str(e))
    85  
    86          # Display results.
    87  
    88          if self.__csvOutput is True:
    89              print('#Name,RID,FullName,PrimaryGroupId,BadPasswordCount,LogonCount,PasswordLastSet,PasswordDoesNotExpire,AccountIsDisabled,UserComment,ScriptPath')
    90  
    91          for entry in entries:
    92              (username, uid, user) = entry
    93              pwdLastSet = (user['PasswordLastSet']['HighPart'] << 32) + user['PasswordLastSet']['LowPart']
    94              if pwdLastSet == 0:
    95                  pwdLastSet = '<never>'
    96              else:
    97                  pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(pwdLastSet)))
    98  
    99              if user['UserAccountControl'] & samr.USER_DONT_EXPIRE_PASSWORD:
   100                  dontExpire = 'True'
   101              else:
   102                  dontExpire = 'False'
   103  
   104              if user['UserAccountControl'] & samr.USER_ACCOUNT_DISABLED:
   105                  accountDisabled = 'True'
   106              else:
   107                  accountDisabled = 'False'
   108  
   109              if self.__csvOutput is True:
   110                  print('%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s' % (username, uid, user['FullName'], user['PrimaryGroupId'],
   111                                                        user['BadPasswordCount'], user['LogonCount'],pwdLastSet,
   112                                                        dontExpire, accountDisabled, user['UserComment'].replace(',','.'),
   113                                                        user['ScriptPath']  ))
   114              else:
   115                  base = "%s (%d)" % (username, uid)
   116                  print(base + '/FullName:', user['FullName'])
   117                  print(base + '/UserComment:', user['UserComment'])
   118                  print(base + '/PrimaryGroupId:', user['PrimaryGroupId'])
   119                  print(base + '/BadPasswordCount:', user['BadPasswordCount'])
   120                  print(base + '/LogonCount:', user['LogonCount'])
   121                  print(base + '/PasswordLastSet:',pwdLastSet)
   122                  print(base + '/PasswordDoesNotExpire:',dontExpire)
   123                  print(base + '/AccountIsDisabled:',accountDisabled)
   124                  print(base + '/ScriptPath:', user['ScriptPath'])
   125  
   126          if entries:
   127              num = len(entries)
   128              if 1 == num:
   129                  logging.info('Received one entry.')
   130              else:
   131                  logging.info('Received %d entries.' % num)
   132          else:
   133              logging.info('No entries received.')
   134  
   135  
   136      def __fetchList(self, rpctransport):
   137          dce = rpctransport.get_dce_rpc()
   138  
   139          entries = []
   140  
   141          dce.connect()
   142          dce.bind(samr.MSRPC_UUID_SAMR)
   143  
   144          try:
   145              resp = samr.hSamrConnect(dce)
   146              serverHandle = resp['ServerHandle'] 
   147  
   148              resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle)
   149              domains = resp['Buffer']['Buffer']
   150  
   151              print('Found domain(s):')
   152              for domain in domains:
   153                  print(" . %s" % domain['Name'])
   154  
   155              logging.info("Looking up users in domain %s" % domains[0]['Name'])
   156  
   157              resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle,domains[0]['Name'] )
   158  
   159              resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId'])
   160              domainHandle = resp['DomainHandle']
   161  
   162              status = STATUS_MORE_ENTRIES
   163              enumerationContext = 0
   164              while status == STATUS_MORE_ENTRIES:
   165                  try:
   166                      resp = samr.hSamrEnumerateUsersInDomain(dce, domainHandle, enumerationContext = enumerationContext)
   167                  except DCERPCException as e:
   168                      if str(e).find('STATUS_MORE_ENTRIES') < 0:
   169                          raise 
   170                      resp = e.get_packet()
   171  
   172                  for user in resp['Buffer']['Buffer']:
   173                      r = samr.hSamrOpenUser(dce, domainHandle, samr.MAXIMUM_ALLOWED, user['RelativeId'])
   174                      print("Found user: %s, uid = %d" % (user['Name'], user['RelativeId'] ))
   175                      info = samr.hSamrQueryInformationUser2(dce, r['UserHandle'],samr.USER_INFORMATION_CLASS.UserAllInformation)
   176                      entry = (user['Name'], user['RelativeId'], info['Buffer']['All'])
   177                      entries.append(entry)
   178                      samr.hSamrCloseHandle(dce, r['UserHandle'])
   179  
   180                  enumerationContext = resp['EnumerationContext'] 
   181                  status = resp['ErrorCode']
   182  
   183          except ListUsersException as e:
   184              logging.critical("Error listing users: %s" % e)
   185  
   186          dce.disconnect()
   187  
   188          return entries
   189  
   190  
   191  # Process command-line arguments.
   192  if __name__ == '__main__':
   193      # Init the example's logger theme
   194      logger.init()
   195      # Explicitly changing the stdout encoding format
   196      if sys.stdout.encoding is None:
   197          # Output is redirected to a file
   198          sys.stdout = codecs.getwriter('utf8')(sys.stdout)
   199      print(version.BANNER)
   200  
   201      parser = argparse.ArgumentParser(add_help = True, description = "This script downloads the list of users for the "
   202                                                                      "target system.")
   203  
   204      parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
   205      parser.add_argument('-csv', action='store_true', help='Turn CSV output')
   206      parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
   207  
   208      group = parser.add_argument_group('connection')
   209  
   210      group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
   211                         'ommited it use the domain part (FQDN) specified in the target parameter')
   212      group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If '
   213                         'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS '
   214                         'name and you cannot resolve it')
   215      group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
   216                         help='Destination port to connect to SMB Server')
   217  
   218      group = parser.add_argument_group('authentication')
   219  
   220      group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
   221      group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
   222      group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
   223                         '(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
   224                         'ones specified in the command line')
   225      group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
   226                                                                              '(128 or 256 bits)')
   227  
   228      if len(sys.argv)==1:
   229          parser.print_help()
   230          sys.exit(1)
   231  
   232      options = parser.parse_args()
   233  
   234      if options.debug is True:
   235          logging.getLogger().setLevel(logging.DEBUG)
   236      else:
   237          logging.getLogger().setLevel(logging.INFO)
   238  
   239      import re
   240  
   241      domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
   242          options.target).groups('')
   243  
   244      #In case the password contains '@'
   245      if '@' in remoteName:
   246          password = password + '@' + remoteName.rpartition('@')[0]
   247          remoteName = remoteName.rpartition('@')[2]
   248          
   249      if domain is None:
   250          domain = ''
   251  
   252      if options.target_ip is None:
   253          options.target_ip = remoteName
   254  
   255      if options.aesKey is not None:
   256          options.k = True
   257  
   258      if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
   259          from getpass import getpass
   260          password = getpass("Password:")
   261  
   262      dumper = SAMRDump(username, password, domain, options.hashes, options.aesKey, options.k, options.dc_ip, int(options.port), options.csv)
   263      dumper.dump(remoteName, options.target_ip)