async def handle_incoming(self): """ Reads data bytes from the socket and dispatches it to the incoming queue """ try: while not self.disconnected.is_set( ) or not self.shutdown_evt.is_set(): try: msg_data = b'' data = await self.reader.readexactly(24) msg_data += data response_header = MSRPCRespHeader(msg_data) data = await self.reader.readexactly( response_header['frag_len'] - 24) msg_data += data await self.in_queue.put((msg_data, None)) except Exception as e: await self.in_queue.put((None, e)) return except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('[DCERPCTCPConnection] handle_incoming %s' % str(e)) await self.in_queue.put((None, e)) await self.disconnect()
async def handle_outgoing(self): """ Reads SMBv1/2 outgoing message data bytes from out_queue, wraps them in NetBIOS object, then serializes them, then sends them to socket_out_queue """ try: while True: smb_msg_data = await self.out_queue.get() if smb_msg_data is None: return data = b'\x00' data += len(smb_msg_data).to_bytes(3, byteorder='big', signed = False) data += smb_msg_data await self.socket_out_queue.put(data) except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('NetBIOSTransport handle_outgoing') await self.stop() finally: await self.stop() self.out_task_finished.set()
async def __handle_incoming(self): try: data = b'' while True: x, err = await self.connection.in_queue.get() if err is not None: raise err data += x if len(data) >= 24: #MSRPCRespHeader._SIZE response_header = MSRPCRespHeader(data) while len(data) < response_header['frag_len']: x, err = await self.connection.in_queue.get() if err is not None: raise err data += x response_data = data[:response_header['frag_len']] data = data[response_header['frag_len']:] await self.msg_in_queue.put(response_data) except asyncio.CancelledError: self.exception_evt.set() return except Exception as e: logger.exception('__handle_incoming') self.exception_evt.set() return
async def worker(self): try: while True: indata = await self.task_q.get() if indata is None: return tid, target = indata try: await asyncio.wait_for(self.__executor(tid, target), timeout=self.max_runtime) except asyncio.CancelledError: return except asyncio.TimeoutError as e: await self.res_q.put( EnumResult(tid, target, None, error=e, status=EnumResultStatus.ERROR)) await self.res_q.put( EnumResult(tid, target, None, status=EnumResultStatus.FINISHED)) continue except Exception as e: logger.exception('worker') continue except asyncio.CancelledError: return except Exception as e: return 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 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 handle_incoming(self): """ Reads data bytes from the socket and dispatches it to the incoming queue """ try: lasterror = None while not self.disconnected.is_set(): try: data = await self.reader.read(10240) await self.in_queue.put((data, None)) except asyncio.CancelledError as e: lasterror = e break except Exception as e: logger.exception('[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 dcsync(self, target_domain=None, target_users=[]): try: if isinstance(target_users, str): target_users = [target_users] if self.samr is None: await self.connect_rpc('SAMR') if target_domain is None: logger.debug('No domain defined, fetching it from SAMR') logger.debug('Fetching domains...') async for domain, err in self.samr.list_domains(): if err is not None: raise err 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: _, err = await drsuapi.connect() if err is not None: raise err _, err = await drsuapi.open() if err is not None: raise err 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, err = await drsuapi.get_user_secrets(username) yield secrets, err 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, err in self.samr.list_domain_users( domain_handle): if err is not None: yield None, err return logger.debug('username: %s' % username) secrets, err = await drsuapi.get_user_secrets(username) if err is not None: yield None, err return logger.debug('secrets: %s' % secrets) yield secrets, None except Exception as e: yield None, e return
async def __handle_smb_in(self): """ Waits from SMB messages from the NetBIOSTransport in_queue, and fills the connection table. This function started automatically when calling connect. """ try: while True: msg, err = await self.netbios_transport.in_queue.get() self.activity_at = datetime.datetime.utcnow() if err is not None: logger.error( '__handle_smb_in got error from transport layer %s' % err) #setting all outstanding events to finished for mid in self.OutstandingResponsesEvent: self.OutstandingResponses[mid] = None self.OutstandingResponsesEvent[mid].set() await self.terminate() return logger.log( 1, '__handle_smb_in got new message with Id %s' % msg.header.MessageId) if isinstance(msg, SMB2Transform): #message is encrypted #this point we should decrypt it and only store the decrypted part in the OutstandingResponses table #but for now we just thropw exception bc encryption is not implemented raise Exception( 'Encrypted SMBv2 message recieved, but encryption is not yet supported!' ) if msg.header.Status == NTStatus.PENDING: self.pending_table[msg.header.MessageId] = SMBPendingMsg( msg.header.MessageId, self.OutstandingResponses, self.OutstandingResponsesEvent) await self.pending_table[msg.header.MessageId].run() continue if msg.header.MessageId in self.pending_table: await self.pending_table[msg.header.MessageId].stop() del self.pending_table[msg.header.MessageId] self.OutstandingResponses[msg.header.MessageId] = msg if msg.header.MessageId in self.OutstandingResponsesEvent: self.OutstandingResponsesEvent[msg.header.MessageId].set() else: #here we are loosing messages, the functionality for "PENDING" and "SHARING_VIOLATION" should be implemented continue except asyncio.CancelledError: #the SMB connection is terminating return except: logger.exception('__handle_smb_in')
async def do_logout(self): if self.machine is not None: await self.machine.close() self.machine = None if self.connection is not None: try: await self.connection.terminate() except Exception as e: logger.exception('connection.close') self.connection = None
async def close(self): if self.dce: if self.handle: for hive_name in self.hive_handles: try: await rrp.hBaseRegCloseKey(self.dce, self.hive_handles[hive_name]) except Exception as e: logger.exception('reg close 1') pass try: await rrp.hBaseRegCloseKey(self.dce, self.handle) except Exception as e: logger.exception('reg close 2') pass try: await self.dce.disconnect() except Exception as e: logger.exception('reg close 3') pass if self.service_manager: try: await self.service_manager.close() except Exception as e: logger.exception('reg close 4') pass return True, None
async def run(self): try: _, err = await self.setup() if err is not None: raise err gen_task = asyncio.create_task(self.__generate_targets()) await asyncio.gather(*self.workers) await self.result_processing_task return True, None except Exception as e: logger.exception('run') return None, e
async def close(self): if self.dce: for hid in self.policy_handles: try: await lsad.hLsarClose(self.dce, self.policy_handles[hid]) except: logger.exception() pass try: await self.dce.disconnect() except: pass return
async def handle_outgoing(self, client): """ Reads data bytes from the outgoing queue and dispatches it to the socket """ try: while not self.shutdown_evt.is_set(): data = await client.out_queue.get() client.writer.write(data) await client.writer.drain() except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('[TCPSocket] handle_outgoing %s' % str(e))
async def __handle_incoming(self): try: while True: data, res = await self.connection.in_queue.get() if data is None: self.__last_exception = res self.exception_evt.set() return self.buffer += data self.data_in_evt.set() except asyncio.CancelledError: return except Exception as e: logger.exception('__handle_incoming') return
async def handle_outgoing(self): """ Reads data bytes from the outgoing queue and dispatches it to the socket """ try: while not self.disconnected.is_set(): data = await self.out_queue.get() print('SOCKS5 data out %s' % data) self.writer.write(data) await self.writer.drain() except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('[TCPSocket] handle_outgoing %s' % str(e)) await self.disconnect()
async def handle_incoming(self): """ Reads data bytes from the socket and dispatches it to the incoming queue """ try: while not self.disconnected.is_set( ) or not self.shutdown_evt.is_set(): data = await self.reader.read(4096) await self.in_queue.put(data) except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('[TCPSocket] handle_incoming %s' % str(e)) await self.disconnect()
async def handle_incoming(self, client): """ Reads data bytes from the socket and dispatches it to the incoming queue """ while not self.shutdown_evt.is_set(): data = await asyncio.gather(*[client.reader.read(4096)], return_exceptions=True) if isinstance(data[0], bytes): await client.in_queue.put(data[0]) elif isinstance(data[0], asyncio.CancelledError): return elif isinstance(data[0], Exception): logger.exception('[TCPSocket] handle_incoming %s' % str(data[0])) return
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 run(self): try: _, err = await self.setup() if err is not None: raise err gen_task = asyncio.create_task(self.__generate_targets()) await asyncio.gather(*self.workers) await self.result_processing_task return True, None except Exception as e: logger.exception('run') return None, e finally: if self.ext_result_q is not None: await self.ext_result_q.put( EnumResultFinal(None, 'finished', None, None, None))
async def __generate_targets(self): try: for target_gen in self.target_gens: async for uid, target, err in target_gen.generate(): if err is not None: print('Target gen error! %s' % err) break if target in self.exclude_target: continue self.__total_targets += 1 await self.task_q.put((uid, target)) await asyncio.sleep(0) self.__gens_finished = True except Exception as e: logger.exception('targetgen')
async def handle_outgoing(self): """ Reads data bytes from the outgoing queue and dispatches it to the socket """ try: while not self.disconnected.is_set( ) or not self.shutdown_evt.is_set(): data = await self.out_queue.get() self.writer.write(data) await self.writer.drain() except asyncio.CancelledError: #the connection is terminating return except Exception as e: logger.exception('[DCERPCTCPConnection] handle_outgoing %s' % str(e)) await self.disconnect()
async def handle_incoming(self): """ Reads data bytes from the socket and dispatches it to the incoming queue """ try: timeout = int(self.target.proxy.timeout) while not self.disconnected.is_set(): data = await asyncio.gather( *[asyncio.wait_for(self.reader.read(4096), timeout)], return_exceptions=True) if isinstance(data[0], bytes): if data[0] == b'': await self.in_queue.put( (None, Exception( 'Socks5 server terminated the connection!'))) await self.disconnect() #print('%s : %s' % (self.writer.get_extra_info('peername')[0], data[0])) #print('SOCKS5 data in %s' % data[0]) await self.in_queue.put((data[0], None)) elif isinstance(data[0], asyncio.CancelledError): #print('SOCKS5 data in CANCELLED') return elif isinstance(data[0], Exception): #print('SOCKS5 data in exception') logger.exception('[SOCKS5] handle_incoming %s' % str(data[0])) await self.in_queue.put((None, data[0])) await self.disconnect() return #print('SOCKS5 data in EXITING') except asyncio.CancelledError: await self.in_queue.put((None, asyncio.CancelledError)) return except Exception as e: import traceback traceback.print_exc() #print('SOCKS5 data in ERROR!') await self.in_queue.put((None, e)) return
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 not self.shutdown_evt.is_set() or not self.stop_evt.is_set(): data = await self.socket_in_queue.get() #parse buffer += data buffer = await self.parse_buffer(buffer) except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('NetBIOSTransport handle_incoming') await self.stop()
async def handle_incoming(self): """ Reads data bytes from the socket and dispatches it to the incoming queue """ while not self.disconnected.is_set(): data = await asyncio.gather(*[self.reader.read(4096)], return_exceptions=True) if isinstance(data[0], bytes): #print('%s : %s' % (self.writer.get_extra_info('peername')[0], data[0])) await self.in_queue.put((data[0], None)) elif isinstance(data[0], asyncio.CancelledError): return elif isinstance(data[0], Exception): logger.exception('[TCPSocket] handle_incoming %s' % str(data[0])) await self.in_queue.put((None, data[0])) await self.disconnect() return
async def worker(self): try: while True: indata = await self.task_q.get() if indata is None: return tid, target = indata try: await asyncio.wait_for(self.__executor(tid, target), timeout=60) except asyncio.CancelledError: return except Exception as e: logger.exception('worker') continue except asyncio.CancelledError: return except Exception as e: return e
async def result_processing(self): try: out_buffer = [] final_iter = False while True: try: if len(out_buffer) >= 10000 or final_iter: out_data = '\r\n'.join(out_buffer) out_buffer = [] if self.out_file is not None: with open(self.out_file, 'a+', newline = '') as f: f.write(out_data) else: print(out_data) if final_iter: asyncio.create_task(self.terminate()) return er = await self.res_q.get() if er.status == EnumResultStatus.FINISHED: self.__total_finished += 1 out_buffer.append('[P][%s/%s][%s]' % (self.__total_targets, self.__total_finished, str(self.__gens_finished))) if self.__total_finished == self.__total_targets and self.__gens_finished is True: final_iter = True continue if er.result is not None: obj, otype, err = er.result if otype is not None: out_buffer.append('[%s] %s' % (otype[0].upper(), obj.unc_path)) if err is not None: out_buffer.append('[E] %s %s' % (err, obj.unc_path)) except asyncio.CancelledError: return except Exception as e: logger.exception('result_processing inner') continue except asyncio.CancelledError: return except Exception as e: logger.exception('result_processing')
async def connect(self): con = asyncio.open_connection(self.ip, self.port) try: self.reader, self.writer = await asyncio.wait_for( con, int(self.timeout)) except asyncio.TimeoutError: logger.debug('[DCERPCTCPConnection] Connection timeout') raise SMBConnectionTimeoutException() except ConnectionRefusedError: logger.debug('[DCERPCTCPConnection] Connection refused') raise SMBConnectionRefusedException() except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('[DCERPCTCPConnection] connect generic exception') raise e asyncio.ensure_future(self.handle_incoming()) asyncio.ensure_future(self.handle_outgoing()) return
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 #parse buffer += data buffer = await self.parse_buffer(buffer) except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('NetBIOSTransport handle_incoming error') await self.in_queue.put( (None, e) ) await self.stop()
async def handle_outgoing(self): """ Reads SMBv1/2 outgoing message objects from out_queue, wraps them in NetBIOS object, then serializes them, then sends them to socket_out_queue """ try: while not self.shutdown_evt.is_set() or not self.stop_evt.is_set(): smb_msg = await self.out_queue.get() #print(smb_msg) smb_msg_data = smb_msg.to_bytes() data = b'\x00' data += len(smb_msg_data).to_bytes(3, byteorder='big', signed=False) data += smb_msg_data await self.socket_out_queue.put(data) except asyncio.CancelledError: #the SMB connection is terminating return except Exception as e: logger.exception('NetBIOSTransport handle_outgoing') await self.stop()