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)