github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/reg.py (about) 1 #!/usr/bin/env python 2 # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 3 # 4 # This software is provided under a slightly modified version 5 # of the Apache Software License. See the accompanying LICENSE file 6 # for more information. 7 # 8 # Description: Remote registry manipulation tool. 9 # The idea is to provide similar functionality as the REG.EXE Windows utility. 10 # 11 # e.g: 12 # ./reg.py Administrator:password@targetMachine query -keyName HKLM\\Software\\Microsoft\\WBEM -s 13 # 14 # Author: 15 # Manuel Porto (@manuporto) 16 # Alberto Solino (@agsolino) 17 # 18 # Reference for: [MS-RRP] 19 # 20 from __future__ import division 21 from __future__ import print_function 22 import argparse 23 import codecs 24 import logging 25 import sys 26 import time 27 from struct import unpack 28 29 from impacket import version 30 from impacket.dcerpc.v5 import transport, rrp, scmr, rpcrt 31 from impacket.examples import logger 32 from impacket.system_errors import ERROR_NO_MORE_ITEMS 33 from impacket.structure import hexdump 34 from impacket.smbconnection import SMBConnection 35 36 37 class RemoteOperations: 38 def __init__(self, smbConnection, doKerberos, kdcHost=None): 39 self.__smbConnection = smbConnection 40 self.__smbConnection.setTimeout(5 * 60) 41 self.__serviceName = 'RemoteRegistry' 42 self.__stringBindingWinReg = r'ncacn_np:445[\pipe\winreg]' 43 self.__rrp = None 44 self.__regHandle = None 45 46 self.__doKerberos = doKerberos 47 self.__kdcHost = kdcHost 48 49 self.__disabled = False 50 self.__shouldStop = False 51 self.__started = False 52 53 self.__stringBindingSvcCtl = r'ncacn_np:445[\pipe\svcctl]' 54 self.__scmr = None 55 56 def getRRP(self): 57 return self.__rrp 58 59 def __connectSvcCtl(self): 60 rpc = transport.DCERPCTransportFactory(self.__stringBindingSvcCtl) 61 rpc.set_smb_connection(self.__smbConnection) 62 self.__scmr = rpc.get_dce_rpc() 63 self.__scmr.connect() 64 self.__scmr.bind(scmr.MSRPC_UUID_SCMR) 65 66 def connectWinReg(self): 67 rpc = transport.DCERPCTransportFactory(self.__stringBindingWinReg) 68 rpc.set_smb_connection(self.__smbConnection) 69 self.__rrp = rpc.get_dce_rpc() 70 self.__rrp.connect() 71 self.__rrp.bind(rrp.MSRPC_UUID_RRP) 72 73 def __checkServiceStatus(self): 74 # Open SC Manager 75 ans = scmr.hROpenSCManagerW(self.__scmr) 76 self.__scManagerHandle = ans['lpScHandle'] 77 # Now let's open the service 78 ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__serviceName) 79 self.__serviceHandle = ans['lpServiceHandle'] 80 # Let's check its status 81 ans = scmr.hRQueryServiceStatus(self.__scmr, self.__serviceHandle) 82 if ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_STOPPED: 83 logging.info('Service %s is in stopped state' % self.__serviceName) 84 self.__shouldStop = True 85 self.__started = False 86 elif ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_RUNNING: 87 logging.debug('Service %s is already running' % self.__serviceName) 88 self.__shouldStop = False 89 self.__started = True 90 else: 91 raise Exception('Unknown service state 0x%x - Aborting' % ans['CurrentState']) 92 93 # Let's check its configuration if service is stopped, maybe it's disabled :s 94 if self.__started is False: 95 ans = scmr.hRQueryServiceConfigW(self.__scmr, self.__serviceHandle) 96 if ans['lpServiceConfig']['dwStartType'] == 0x4: 97 logging.info('Service %s is disabled, enabling it' % self.__serviceName) 98 self.__disabled = True 99 scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType=0x3) 100 logging.info('Starting service %s' % self.__serviceName) 101 scmr.hRStartServiceW(self.__scmr, self.__serviceHandle) 102 time.sleep(1) 103 104 def enableRegistry(self): 105 self.__connectSvcCtl() 106 self.__checkServiceStatus() 107 self.connectWinReg() 108 109 def __restore(self): 110 # First of all stop the service if it was originally stopped 111 if self.__shouldStop is True: 112 logging.info('Stopping service %s' % self.__serviceName) 113 scmr.hRControlService(self.__scmr, self.__serviceHandle, scmr.SERVICE_CONTROL_STOP) 114 if self.__disabled is True: 115 logging.info('Restoring the disabled state for service %s' % self.__serviceName) 116 scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType=0x4) 117 118 def finish(self): 119 self.__restore() 120 if self.__rrp is not None: 121 self.__rrp.disconnect() 122 if self.__scmr is not None: 123 self.__scmr.disconnect() 124 125 126 class RegHandler: 127 def __init__(self, username, password, domain, options): 128 self.__username = username 129 self.__password = password 130 self.__domain = domain 131 self.__options = options 132 self.__action = options.action.upper() 133 self.__lmhash = '' 134 self.__nthash = '' 135 self.__aesKey = options.aesKey 136 self.__doKerberos = options.k 137 self.__kdcHost = options.dc_ip 138 self.__smbConnection = None 139 self.__remoteOps = None 140 141 # It's possible that this is defined somewhere, but I couldn't find where 142 self.__regValues = {0: 'REG_NONE', 1: 'REG_SZ', 2: 'REG_EXPAND_SZ', 3: 'REG_BINARY', 4: 'REG_DWORD', 143 5: 'REG_DWORD_BIG_ENDIAN', 6: 'REG_LINK', 7: 'REG_MULTI_SZ', 11: 'REG_QWORD'} 144 145 if options.hashes is not None: 146 self.__lmhash, self.__nthash = options.hashes.split(':') 147 148 def connect(self, remoteName, remoteHost): 149 self.__smbConnection = SMBConnection(remoteName, remoteHost, sess_port=int(self.__options.port)) 150 151 if self.__doKerberos: 152 self.__smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, 153 self.__nthash, self.__aesKey, self.__kdcHost) 154 else: 155 self.__smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) 156 157 def run(self, remoteName, remoteHost): 158 self.connect(remoteName, remoteHost) 159 self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost) 160 161 try: 162 self.__remoteOps.enableRegistry() 163 except Exception as e: 164 logging.debug(str(e)) 165 logging.warning('Cannot check RemoteRegistry status. Hoping it is started...') 166 self.__remoteOps.connectWinReg() 167 168 try: 169 dce = self.__remoteOps.getRRP() 170 171 if self.__action == 'QUERY': 172 self.query(dce, self.__options.keyName) 173 else: 174 logging.error('Method %s not implemented yet!' % self.__action) 175 except (Exception, KeyboardInterrupt) as e: 176 #import traceback 177 #traceback.print_exc() 178 logging.critical(str(e)) 179 finally: 180 if self.__remoteOps: 181 self.__remoteOps.finish() 182 183 def query(self, dce, keyName): 184 # Let's strip the root key 185 try: 186 rootKey = keyName.split('\\')[0] 187 subKey = '\\'.join(keyName.split('\\')[1:]) 188 except Exception: 189 raise Exception('Error parsing keyName %s' % keyName) 190 191 if rootKey.upper() == 'HKLM': 192 ans = rrp.hOpenLocalMachine(dce) 193 elif rootKey.upper() == 'HKU': 194 ans = rrp.hOpenCurrentUser(dce) 195 elif rootKey.upper() == 'HKCR': 196 ans = rrp.hOpenClassesRoot(dce) 197 else: 198 raise Exception('Invalid root key %s ' % rootKey) 199 200 hRootKey = ans['phKey'] 201 202 ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, 203 samDesired=rrp.MAXIMUM_ALLOWED | rrp.KEY_ENUMERATE_SUB_KEYS | rrp.KEY_QUERY_VALUE) 204 205 if self.__options.v: 206 print(keyName) 207 value = rrp.hBaseRegQueryValue(dce, ans2['phkResult'], self.__options.v) 208 print('\t' + self.__options.v + '\t' + self.__regValues.get(value[0], 'KEY_NOT_FOUND') + '\t', str(value[1])) 209 elif self.__options.ve: 210 print(keyName) 211 value = rrp.hBaseRegQueryValue(dce, ans2['phkResult'], '') 212 print('\t' + '(Default)' + '\t' + self.__regValues.get(value[0], 'KEY_NOT_FOUND') + '\t', str(value[1])) 213 elif self.__options.s: 214 self.__print_all_subkeys_and_entries(dce, subKey + '\\', ans2['phkResult'], 0) 215 else: 216 print(keyName) 217 self.__print_key_values(dce, ans2['phkResult']) 218 i = 0 219 while True: 220 try: 221 key = rrp.hBaseRegEnumKey(dce, ans2['phkResult'], i) 222 print(keyName + '\\' + key['lpNameOut'][:-1]) 223 i += 1 224 except Exception: 225 break 226 # ans5 = rrp.hBaseRegGetVersion(rpc, ans2['phkResult']) 227 # ans3 = rrp.hBaseRegEnumKey(rpc, ans2['phkResult'], 0) 228 229 def __print_key_values(self, rpc, keyHandler): 230 i = 0 231 while True: 232 try: 233 ans4 = rrp.hBaseRegEnumValue(rpc, keyHandler, i) 234 lp_value_name = ans4['lpValueNameOut'][:-1] 235 if len(lp_value_name) == 0: 236 lp_value_name = '(Default)' 237 lp_type = ans4['lpType'] 238 lp_data = b''.join(ans4['lpData']) 239 print('\t' + lp_value_name + '\t' + self.__regValues.get(lp_type, 'KEY_NOT_FOUND') + '\t', end=' ') 240 self.__parse_lp_data(lp_type, lp_data) 241 i += 1 242 except rrp.DCERPCSessionError as e: 243 if e.get_error_code() == ERROR_NO_MORE_ITEMS: 244 break 245 246 def __print_all_subkeys_and_entries(self, rpc, keyName, keyHandler, index): 247 index = 0 248 while True: 249 try: 250 subkey = rrp.hBaseRegEnumKey(rpc, keyHandler, index) 251 index += 1 252 ans = rrp.hBaseRegOpenKey(rpc, keyHandler, subkey['lpNameOut'], 253 samDesired=rrp.MAXIMUM_ALLOWED | rrp.KEY_ENUMERATE_SUB_KEYS) 254 newKeyName = keyName + subkey['lpNameOut'][:-1] + '\\' 255 print(newKeyName) 256 self.__print_key_values(rpc, ans['phkResult']) 257 self.__print_all_subkeys_and_entries(rpc, newKeyName, ans['phkResult'], 0) 258 except rrp.DCERPCSessionError as e: 259 if e.get_error_code() == ERROR_NO_MORE_ITEMS: 260 break 261 except rpcrt.DCERPCException as e: 262 if str(e).find('access_denied') >= 0: 263 logging.error('Cannot access subkey %s, bypassing it' % subkey['lpNameOut'][:-1]) 264 continue 265 elif str(e).find('rpc_x_bad_stub_data') >= 0: 266 logging.error('Fault call, cannot retrieve value for %s, bypassing it' % subkey['lpNameOut'][:-1]) 267 return 268 raise 269 270 @staticmethod 271 def __parse_lp_data(valueType, valueData): 272 try: 273 if valueType == rrp.REG_SZ or valueType == rrp.REG_EXPAND_SZ: 274 if type(valueData) is int: 275 print('NULL') 276 else: 277 print("%s" % (valueData.decode('utf-16le')[:-1])) 278 elif valueType == rrp.REG_BINARY: 279 print('') 280 hexdump(valueData, '\t') 281 elif valueType == rrp.REG_DWORD: 282 print("0x%x" % (unpack('<L', valueData)[0])) 283 elif valueType == rrp.REG_QWORD: 284 print("0x%x" % (unpack('<Q', valueData)[0])) 285 elif valueType == rrp.REG_NONE: 286 try: 287 if len(valueData) > 1: 288 print('') 289 hexdump(valueData, '\t') 290 else: 291 print(" NULL") 292 except: 293 print(" NULL") 294 elif valueType == rrp.REG_MULTI_SZ: 295 print("%s" % (valueData.decode('utf-16le')[:-2])) 296 else: 297 print("Unknown Type 0x%x!" % valueType) 298 hexdump(valueData) 299 except Exception as e: 300 logging.debug('Exception thrown when printing reg value %s', str(e)) 301 print('Invalid data') 302 pass 303 304 305 if __name__ == '__main__': 306 307 # Init the example's logger theme 308 logger.init() 309 # Explicitly changing the stdout encoding format 310 if sys.stdout.encoding is None: 311 # Output is redirected to a file 312 sys.stdout = codecs.getwriter('utf8')(sys.stdout) 313 print(version.BANNER) 314 315 parser = argparse.ArgumentParser(add_help=True, description="Windows Register manipulation script.") 316 317 parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>') 318 parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') 319 subparsers = parser.add_subparsers(help='actions', dest='action') 320 321 # A query command 322 query_parser = subparsers.add_parser('query', help='Returns a list of the next tier of subkeys and entries that ' 323 'are located under a specified subkey in the registry.') 324 query_parser.add_argument('-keyName', action='store', required=True, 325 help='Specifies the full path of the subkey. The ' 326 'keyName must include a valid root key. Valid root keys for the local computer are: HKLM,' 327 ' HKU.') 328 query_parser.add_argument('-v', action='store', metavar="VALUENAME", required=False, help='Specifies the registry ' 329 'value name that is to be queried. If omitted, all value names for keyName are returned. ') 330 query_parser.add_argument('-ve', action='store_true', default=False, required=False, help='Queries for the default ' 331 'value or empty value name') 332 query_parser.add_argument('-s', action='store_true', default=False, help='Specifies to query all subkeys and value ' 333 'names recursively.') 334 335 # An add command 336 # add_parser = subparsers.add_parser('add', help='Adds a new subkey or entry to the registry') 337 338 # An delete command 339 # delete_parser = subparsers.add_parser('delete', help='Deletes a subkey or entries from the registry') 340 341 # A copy command 342 # copy_parser = subparsers.add_parser('copy', help='Copies a registry entry to a specified location in the remote ' 343 # 'computer') 344 345 # A save command 346 # save_parser = subparsers.add_parser('save', help='Saves a copy of specified subkeys, entries, and values of the ' 347 # 'registry in a specified file.') 348 349 # A load command 350 # load_parser = subparsers.add_parser('load', help='Writes saved subkeys and entries back to a different subkey in ' 351 # 'the registry.') 352 353 # An unload command 354 # unload_parser = subparsers.add_parser('unload', help='Removes a section of the registry that was loaded using the ' 355 # 'reg load operation.') 356 357 # A compare command 358 # compare_parser = subparsers.add_parser('compare', help='Compares specified registry subkeys or entries') 359 360 # A export command 361 # status_parser = subparsers.add_parser('export', help='Creates a copy of specified subkeys, entries, and values into' 362 # 'a file') 363 364 # A import command 365 # import_parser = subparsers.add_parser('import', help='Copies a file containing exported registry subkeys, entries, ' 366 # 'and values into the remote computer\'s registry') 367 368 369 group = parser.add_argument_group('authentication') 370 371 group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') 372 group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') 373 group.add_argument('-k', action="store_true", 374 help='Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on ' 375 'target parameters. If valid credentials cannot be found, it will use the ones specified ' 376 'in the command line') 377 group.add_argument('-aesKey', action="store", metavar="hex key", 378 help='AES key to use for Kerberos Authentication (128 or 256 bits)') 379 380 group = parser.add_argument_group('connection') 381 382 group.add_argument('-dc-ip', action='store', metavar="ip address", 383 help='IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in ' 384 'the target parameter') 385 group.add_argument('-target-ip', action='store', metavar="ip address", 386 help='IP Address of the target machine. If omitted it will use whatever was specified as target. ' 387 'This is useful when target is the NetBIOS name and you cannot resolve it') 388 group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port", 389 help='Destination port to connect to SMB Server') 390 391 if len(sys.argv) == 1: 392 parser.print_help() 393 sys.exit(1) 394 395 options = parser.parse_args() 396 397 if options.debug is True: 398 logging.getLogger().setLevel(logging.DEBUG) 399 else: 400 logging.getLogger().setLevel(logging.INFO) 401 402 import re 403 404 domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match( 405 options.target).groups('') 406 407 # In case the password contains '@' 408 if '@' in remoteName: 409 password = password + '@' + remoteName.rpartition('@')[0] 410 remoteName = remoteName.rpartition('@')[2] 411 412 if options.target_ip is None: 413 options.target_ip = remoteName 414 415 if domain is None: 416 domain = '' 417 418 if options.aesKey is not None: 419 options.k = True 420 421 if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: 422 from getpass import getpass 423 424 password = getpass("Password:") 425 426 regHandler = RegHandler(username, password, domain, options) 427 try: 428 regHandler.run(remoteName, options.target_ip) 429 except Exception as e: 430 #import traceback 431 #traceback.print_exc() 432 logging.error(str(e))