async def handle_incoming(self): """ Reads data bytes from the socket_in_queue and parses the NetBIOS messages and the SMBv1/2 messages. Dispatches the SMBv1/2 message objects. """ try: buffer = b'' while True: data, err = await self.socket_in_queue.get() if err is not None: raise err if data == b'': buffer = await self.parse_buffer(buffer) raise Exception('Remote end terminated the connection') #parse buffer += data buffer = await self.parse_buffer(buffer) except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.debug('NetBIOSTransport handle_incoming error. Reason: %s' % e) await self.in_queue.put((None, e)) await self.stop()
async def hBaseRegEnumValue(dce, hKey, dwIndex, dataLen=256): request = BaseRegEnumValue() request['hKey'] = hKey request['dwIndex'] = dwIndex retries = 1 # We need to be aware the size might not be enough, so let's catch ERROR_MORE_DATA exception while True: try: # Only the maximum length field of the lpValueNameIn is used to determine the buffer length to be allocated # by the service. Specify a string with a zero length but maximum length set to the largest buffer size # needed to hold the value names. request.fields['lpValueNameIn'].fields['MaximumLength'] = dataLen*2 request.fields['lpValueNameIn'].fields['Data'].fields['Data'].fields['MaximumCount'] = dataLen request['lpData'] = b' ' * dataLen request['lpcbData'] = dataLen request['lpcbLen'] = dataLen resp = await dce.request(request) except DCERPCSessionError as e: if retries > 1: LOG.debug('Too many retries when calling hBaseRegEnumValue, aborting') raise if e.get_error_code() == system_errors.ERROR_MORE_DATA: # We need to adjust the size retries +=1 dataLen = e.get_packet()['lpcbData'] continue else: raise else: break return resp
async def connect(self): """ Main function to be called, connects to the target specified in settings, and starts reading/writing. """ #self.settings = settings con = asyncio.open_connection(self.settings.get_ip(), self.settings.get_port()) try: self.reader, self.writer = await asyncio.wait_for( con, int(self.settings.timeout)) except asyncio.TimeoutError: logger.debug('[TCPSocket] Connection timeout') raise SMBConnectionTimeoutException() except ConnectionRefusedError: logger.debug('[TCPSocket] Connection refused') raise SMBConnectionRefusedException() except asyncio.CancelledError: #the SMB connection is terminating raise asyncio.CancelledError except Exception as e: logger.exception('[TCPSocket] connect generic exception') raise e self.incoming_task = asyncio.create_task(self.handle_incoming()) self.outgoing_task = asyncio.create_task(self.handle_outgoing()) return
async def hBaseRegQueryValue(dce, hKey, lpValueName, dataLen=512): raise Exception('MODIFICATION ASD TEST NEEDED!!!') request = BaseRegQueryValue() request['hKey'] = hKey request['lpValueName'] = checkNullString(lpValueName) retries = 1 # We need to be aware the size might not be enough, so let's catch ERROR_MORE_DATA exception while True: try: request['lpData'] = b' ' * dataLen request['lpcbData'] = dataLen request['lpcbLen'] = dataLen resp = await dce.request(request) except DCERPCSessionError as e: if retries > 1: LOG.debug( 'Too many retries when calling hBaseRegQueryValue, aborting' ) raise if e.get_error_code() == system_errors.ERROR_MORE_DATA: # We need to adjust the size dataLen = e.get_packet()['lpcbData'] continue else: raise else: break # Returns # ( dataType, data ) return resp['lpType'], unpackValue(resp['lpType'], resp['lpData'])
async def do_login(self, url = None): """Connects to the remote machine""" try: if self.conn_url is None and url is None: print('No url was set, cant do logon') if url is not None: self.conn_url = SMBConnectionURL(url) cred = self.conn_url.get_credential() if cred.secret is None and cred.username is None and cred.domain is None: self.is_anon = True self.connection = self.conn_url.get_connection() logger.debug(self.conn_url.get_credential()) logger.debug(self.conn_url.get_target()) _, err = await self.connection.login() if err is not None: raise err self.machine = SMBMachine(self.connection) if self.silent is False: print('Login success') return True, None except Exception as e: traceback.print_exc() print('Login failed! Reason: %s' % str(e)) return False, e
async def check_service_status(self, service_name): if not self.handle: await self.open() if service_name not in self.service_handles: await self.open_service(service_name) # Let's check its status ans = await scmr.hRQueryServiceStatus( self.dce, self.service_handles[service_name]) if ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_STOPPED: logger.info('Service %s is in stopped state' % service_name) # Let's check its configuration if service is stopped, maybe it's disabled :s ans = await scmr.hRQueryServiceConfigW(self.__scmr, self.__serviceHandle) if ans['lpServiceConfig']['dwStartType'] == 0x4: logger.info('Service %s is disabled' % service_name) return SMBRemoteServiceStatus.DISABLED else: return SMBRemoteServiceStatus.STOPPED elif ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_RUNNING: logger.debug('Service %s is already running' % service_name) return SMBRemoteServiceStatus.RUNNING else: raise Exception('Unknown service state 0x%x - Aborting' % ans['CurrentState'])
async def handle_incoming(self): """ Reads data bytes from the socket and dispatches it to the incoming queue """ try: lasterror = None msgsize = None while not self.disconnected.is_set(): try: data = await self.reader.readexactly(4) msgsize = int.from_bytes(data[1:], byteorder='big', signed = False) data = await self.reader.readexactly(msgsize) await self.in_queue.put( (data, None) ) if data == b'': return except asyncio.CancelledError as e: lasterror = e break except Exception as e: logger.debug('[TCPSocket] handle_incoming %s' % str(e)) lasterror = e break except asyncio.CancelledError: return except Exception as e: lasterror = e finally: if self.in_queue is not None: await self.in_queue.put( (None, lasterror) ) await self.disconnect()
async def open(self): if not self.dce: await rr(self.connect()) await rr(self.dce.bind(drsuapi.MSRPC_UUID_DRSUAPI)) request = drsuapi.DRSBind() request['puuidClientDsa'] = drsuapi.NTDSAPI_CLIENT_GUID drs = drsuapi.DRS_EXTENSIONS_INT() drs['cb'] = len(drs) #- 4 drs['dwFlags'] = drsuapi.DRS_EXT_GETCHGREQ_V6 | drsuapi.DRS_EXT_GETCHGREPLY_V6 | drsuapi.DRS_EXT_GETCHGREQ_V8 | \ drsuapi.DRS_EXT_STRONG_ENCRYPTION drs['SiteObjGuid'] = drsuapi.NULLGUID drs['Pid'] = 0 drs['dwReplEpoch'] = 0 drs['dwFlagsExt'] = 0 drs['ConfigObjGUID'] = drsuapi.NULLGUID # I'm uber potential (c) Ben drs['dwExtCaps'] = 0xffffffff request['pextClient']['cb'] = len(drs) request['pextClient']['rgb'] = list(drs.getData()) resp, _ = await rr(self.dce.request(request)) # 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 = b''.join(resp['ppextServer']['rgb']) + b'\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 logger.level == logging.DEBUG: logger.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(drs.getData()) resp, _ = await rr(self.dce.request(request)) self.handle = resp['phDrs'] # Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges resp, _ = await rr( drsuapi.hDRSDomainControllerInfo(self.dce, self.handle, self.domainname, 2)) if logger.level == logging.DEBUG: logger.debug('DRSDomainControllerInfo() answer %s' % resp.dump()) if resp['pmsgOut']['V2']['cItems'] > 0: self.__NtdsDsaObjectGuid = resp['pmsgOut']['V2']['rItems'][0][ 'NtdsDsaObjectGuid'] else: logger.error("Couldn't get DC info for domain %s" % self.domainname) raise Exception('Fatal, aborting!') return True, None
async def DRSGetNCChanges(self, guid, req_attributes={}): try: if self.handle is None: await rr(self.open()) logger.debug('Calling DRSGetNCChanges for %s ' % guid) request = drsuapi.DRSGetNCChanges() request['hDrs'] = self.handle request['dwInVersion'] = 8 request['pmsgIn']['tag'] = 8 request['pmsgIn']['V8'][ 'uuidDsaObjDest'] = self.__NtdsDsaObjectGuid request['pmsgIn']['V8'][ 'uuidInvocIdSrc'] = self.__NtdsDsaObjectGuid dsName = drsuapi.DSNAME() dsName['SidLen'] = 0 dsName['Guid'] = string_to_bin(guid) #guid.to_bytes() dsName['Sid'] = '' dsName['NameLen'] = 0 dsName['StringName'] = ('\x00') dsName['structLen'] = len(dsName.getData()) request['pmsgIn']['V8']['pNC'] = dsName request['pmsgIn']['V8']['usnvecFrom']['usnHighObjUpdate'] = 0 request['pmsgIn']['V8']['usnvecFrom']['usnHighPropUpdate'] = 0 request['pmsgIn']['V8']['pUpToDateVecDest'] = NULL request['pmsgIn']['V8'][ 'ulFlags'] = drsuapi.DRS_INIT_SYNC | drsuapi.DRS_WRIT_REP request['pmsgIn']['V8']['cMaxObjects'] = 1 request['pmsgIn']['V8']['cMaxBytes'] = 0 request['pmsgIn']['V8']['ulExtendedOp'] = drsuapi.EXOP_REPL_OBJ if self.__ppartialAttrSet is None: self.__prefixTable = [] self.__ppartialAttrSet = drsuapi.PARTIAL_ATTR_VECTOR_V1_EXT() self.__ppartialAttrSet['dwVersion'] = 1 self.__ppartialAttrSet['cAttrs'] = len(req_attributes) for attId in list(req_attributes.values()): self.__ppartialAttrSet['rgPartialAttr'].append( drsuapi.MakeAttid(self.__prefixTable, attId)) request['pmsgIn']['V8']['pPartialAttrSet'] = self.__ppartialAttrSet request['pmsgIn']['V8']['PrefixTableDest']['PrefixCount'] = len( self.__prefixTable) request['pmsgIn']['V8']['PrefixTableDest'][ 'pPrefixEntry'] = self.__prefixTable request['pmsgIn']['V8']['pPartialAttrSetEx1'] = NULL data, err = await self.dce.request(request) return data, err except Exception as e: print('err!') return None, e
async def DRSGetNT4ChangeLog(self): try: logger.debug('Calling DRSGetNT4ChangeLog') resp, err = await drsuapi.hDRSGetNT4ChangeLog( self.dce, self.handle) if err is not None: raise err return resp, None except Exception as e: return None, e
async def DRSGetNT4ChangeLog(self): if self.handle is None: await rr(self.open()) try: logger.debug('Calling DRSGetNT4ChangeLog') resp, _ = await rr( drsuapi.hDRSGetNT4ChangeLog(self.dce, self.handle)) return resp, None except Exception as e: return None, e
def __getitem__(self, key): if key == 'Data': try: return ''.join([chr(i) for i in self.fields[key]]) except ValueError: # We might have Unicode chars in here, let's use unichr instead LOG.debug('ValueError exception on %s' % self.fields[key]) LOG.debug('Switching to unichr()') return ''.join([chr(i) for i in self.fields[key]]) else: return NDR.__getitem__(self, key)
async def DRSCrackNames( self, formatOffered=drsuapi.DS_NAME_FORMAT.DS_DISPLAY_NAME, formatDesired=drsuapi.DS_NAME_FORMAT.DS_FQDN_1779_NAME, name=''): if self.handle is None: await self.open() logger.debug('Calling DRSCrackNames for %s ' % name) resp = await drsuapi.hDRSCrackNames(self.dce, self.handle, 0, formatOffered, formatDesired, (name, )) return resp
async def DRSCrackNames( self, formatOffered=drsuapi.DS_NAME_FORMAT.DS_DISPLAY_NAME, formatDesired=drsuapi.DS_NAME_FORMAT.DS_FQDN_1779_NAME, name=''): try: logger.debug('Calling DRSCrackNames for %s' % name) resp, err = await drsuapi.hDRSCrackNames(self.dce, self.handle, 0, formatOffered, formatDesired, (name, )) return resp, None except Exception as e: return None, e
async def dcsync(self, target_domain=None, target_users=[]): if target_domain is None: logger.debug('No domain defined, fetching it from SAMR') logger.debug('Fetching domains...') async for domain in self.samr.list_domains(): if domain == 'Builtin': continue if target_domain is None: #using th first available target_domain = domain logger.debug('Domain available: %s' % domain) async with SMBDRSUAPI(self.connection, target_domain) as drsuapi: try: await drsuapi.connect() await drsuapi.open() except Exception as e: logger.exception('Failed to connect to DRSUAPI!') raise e logger.debug('Using domain: %s' % target_domain) if len(target_users) > 0: for username in target_users: secrets = await drsuapi.get_user_secrets(username) yield secrets else: domain_sid = await self.samr.get_domain_sid(target_domain) domain_handle = await self.samr.open_domain(domain_sid) async for username, user_sid in self.samr.list_domain_users( domain_handle): secrets = await drsuapi.get_user_secrets(username) yield secrets
async def DRSGetNCChanges(self, userEntry): if self.handle is None: self.open() logger.debug('Calling DRSGetNCChanges for %s ' % userEntry) request = drsuapi.DRSGetNCChanges() request['hDrs'] = self.__hDrs request['dwInVersion'] = 8 request['pmsgIn']['tag'] = 8 request['pmsgIn']['V8']['uuidDsaObjDest'] = self.__NtdsDsaObjectGuid request['pmsgIn']['V8']['uuidInvocIdSrc'] = self.__NtdsDsaObjectGuid dsName = drsuapi.DSNAME() dsName['SidLen'] = 0 dsName['Guid'] = string_to_bin(userEntry[1:-1]) dsName['Sid'] = '' dsName['NameLen'] = 0 dsName['StringName'] = ('\x00') dsName['structLen'] = len(dsName.getData()) request['pmsgIn']['V8']['pNC'] = dsName request['pmsgIn']['V8']['usnvecFrom']['usnHighObjUpdate'] = 0 request['pmsgIn']['V8']['usnvecFrom']['usnHighPropUpdate'] = 0 request['pmsgIn']['V8']['pUpToDateVecDest'] = NULL request['pmsgIn']['V8'][ 'ulFlags'] = drsuapi.DRS_INIT_SYNC | drsuapi.DRS_WRIT_REP request['pmsgIn']['V8']['cMaxObjects'] = 1 request['pmsgIn']['V8']['cMaxBytes'] = 0 request['pmsgIn']['V8']['ulExtendedOp'] = drsuapi.EXOP_REPL_OBJ if self.__ppartialAttrSet is None: self.__prefixTable = [] self.__ppartialAttrSet = drsuapi.PARTIAL_ATTR_VECTOR_V1_EXT() self.__ppartialAttrSet['dwVersion'] = 1 self.__ppartialAttrSet['cAttrs'] = len(NTDSHashes.ATTRTYP_TO_ATTID) for attId in list(NTDSHashes.ATTRTYP_TO_ATTID.values()): self.__ppartialAttrSet['rgPartialAttr'].append( drsuapi.MakeAttid(self.__prefixTable, attId)) request['pmsgIn']['V8']['pPartialAttrSet'] = self.__ppartialAttrSet request['pmsgIn']['V8']['PrefixTableDest']['PrefixCount'] = len( self.__prefixTable) request['pmsgIn']['V8']['PrefixTableDest'][ 'pPrefixEntry'] = self.__prefixTable request['pmsgIn']['V8']['pPartialAttrSetEx1'] = NULL return self.dce.request(request)
async def handle_incoming(self): """ Reads data bytes from the socket_in_queue and parses the NetBIOS messages and the SMBv1/2 messages. Dispatches the SMBv1/2 message objects. """ try: buffer = b'' lastcall = False while not lastcall: if self.__total_size == -1: if len(buffer) > 5: self.__total_size = int.from_bytes( buffer[1:4], byteorder='big', signed=False) + 4 while self.__total_size > -1 and len( buffer) >= self.__total_size: if self.__total_size > -1 and len( buffer) >= self.__total_size: msg_data = buffer[:self.__total_size][4:] buffer = buffer[self.__total_size:] self.__total_size = -1 if len(buffer) > 5: self.__total_size = int.from_bytes( buffer[1:4], byteorder='big', signed=False) + 4 #print('%s nbmsg! ' % (self.network_transport.writer.get_extra_info('peername')[0], )) #print('[NetBIOS] MSG dispatched') await self.in_queue.put((msg_data, None)) data, err = await self.socket_in_queue.get() if err is not None: raise err if data == b'': lastcall = True buffer += data raise Exception('Remote end terminated the connection') except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.debug('NetBIOSTransport handle_incoming error. Reason: %s' % e) await self.in_queue.put((None, e)) await self.stop()
async def do_lsass(self): try: res, err = await self.machine.task_dump_lsass() if err is not None: print(str(err)) print(res) await res.close() return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e
async def do_login(self, url=None): try: if self.conn_url is None and url is None: print('No url was set, cant do logon') if url is not None: self.conn_url = SMBConnectionURL(url) self.connection = self.conn_url.get_connection() logger.debug(self.conn_url.get_credential()) logger.debug(self.conn_url.get_target()) await self.connection.login() self.machine = SMBMachine(self.connection) except Exception as e: traceback.print_exc() else: print('Login success')
async def connect(self): try: con = asyncio.open_connection(self.ip, self.port) self.reader, self.writer = await asyncio.wait_for(con, None) except asyncio.TimeoutError: logger.debug('[DCERPCTCPConnection] Connection timeout') return None, SMBConnectionTimeoutException() except ConnectionRefusedError: logger.debug('[DCERPCTCPConnection] Connection refused') return None, SMBConnectionRefusedException() except Exception as e: logger.exception('[DCERPCTCPConnection] connect generic exception') return None, e self.__incoming_task = asyncio.create_task(self.handle_incoming()) self.__outgoing_task = asyncio.create_task(self.handle_outgoing()) return True, None
async def handle_incoming_noparse(self): """ Reads data bytes from the socket_in_queue and parses the NetBIOS messages and the SMBv1/2 messages. Dispatches the SMBv1/2 message objects. """ try: while True: data, err = await self.socket_in_queue.get() if err is not None: raise err await self.in_queue.put( (data, err) ) except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.debug('NetBIOSTransport handle_incoming error. Reason: %s' % e) await self.in_queue.put( (None, e) ) await self.stop()
async def do_taskregister(self, template_file, task_name = None): """Registers a new scheduled task""" try: with open(template_file, 'r') as f: template = f.read() res, err = await self.machine.tasks_register(template, task_name = task_name) if err is not None: logger.info('[!] Failed to register new task!') raise err return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_tasks(self): """List scheduled tasks """ try: async for taskname, err in self.machine.tasks_list(): if err is not None: raise err print(taskname) return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_printerbug(self, attacker_ip): """Printerbug""" try: res, err = await self.machine.printerbug(attacker_ip) if err is not None: print(str(err)) print(res) return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_users(self, domain = None): """List users in domain""" try: async for username, user_sid, err in self.machine.list_domain_users(domain): if err is not None: print(str(err)) print('%s %s' % (username, user_sid)) return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_dcsync(self, username = None): """It's a suprse tool that will help us later""" try: users = [] if username is not None: users.append(username) async for secret, err in self.machine.dcsync(target_users=users): if err is not None: raise err if secret is None: continue print(str(secret)) return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_mkdir(self, directory_name): """Creates a directory on the remote share""" try: _, err = await self.machine.create_subdirectory(directory_name, self.__current_directory) if err is not None: raise err print('Directory created!') _, err = await self.do_refreshcurdir() if err is not None: raise err return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_reglistusers(self): """Saves a registry hive to a file on remote share""" try: users, err = await self.machine.reg_list_users() if err is not None: raise err for user in users: print(user) return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_put(self, file_name): """Uploads a file to the remote share""" try: basename = ntpath.basename(file_name) dst = '\\%s\\%s\\%s' % (self.__current_share.name, self.__current_directory.fullpath , basename) _, err = await self.machine.put_file(file_name, dst) if err is not None: print('Failed to put file! Reason: %s' % err) return False, err print('File uploaded!') _, err = await self.do_refreshcurdir() if err is not None: raise err return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e
async def do_del(self, file_name): """Removes a file from the remote share""" try: basename = ntpath.basename(file_name) dst = '\\%s\\%s\\%s' % (self.__current_share.name, self.__current_directory.fullpath , basename) _, err = await self.machine.del_file(dst) if err is not None: raise err print('File deleted!') _, err = await self.do_refreshcurdir() if err is not None: raise err return True, None except SMBException as e: logger.debug(traceback.format_exc()) print(e.pprint()) return None, e except SMBMachineException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except DCERPCException as e: logger.debug(traceback.format_exc()) print(str(e)) return None, e except Exception as e: traceback.print_exc() return None, e