def cuckoo_main(max_analysis_count=0): """Cuckoo main loop. @param max_analysis_count: kill cuckoo after this number of analyses """ rs, sched = None, None def stop(): if sched: sched.running = False if rs: rs.instance.stop() Pidfile("cuckoo").remove() if sched: sched.stop() def handle_sigterm(sig, f): stop() # Handle a SIGTERM, to reduce the chance of Cuckoo exiting without # cleaning signal.signal(signal.SIGTERM, handle_sigterm) try: rs = ResultServer() sched = Scheduler(max_analysis_count) sched.start() except KeyboardInterrupt: log.info("CTRL+C detected! Stopping.. This can take a few seconds") finally: if Pidfile("cuckoo").exists(): stop()
def get_task_id(self, label): analysistasks = ResultServer().analysistasks for task_ip in analysistasks: if analysistasks[task_ip][1].label is label: return analysistasks[task_ip][0].id return None
def start_run(self): self.set_analysis_status(Analysis.STARTING) log.info( "Starting analysis (task #%s, options: '%s') type '%s'. %d URLs", self.task.id, self.options["options"], self.task.type, len(self.task.targets)) ResultServer().add_task(self.task.db_task, self.machine, self.rt) self.aux.start() try: self.machinery.start(self.machine.label, self.task.db_task) except CuckooMachineSnapshotError as e: log.error( "Unable to restore to the snapshot for this Virtual Machine! " "Does your VM have a proper Snapshot and can you revert to it " "manually? VM: %s, error: %s", self.machine.name, e) return False except CuckooMachineError as e: log.error("Error starting Virtual Machine! VM: %s, error: %s", self.machine.name, e) return False # Enable network routing if not self.route.route_network(): log.error("Failed to use chosen route for the analysis") self.ev_client.send_event( "massurltaskfailure", { "taskid": self.task.id, "error": "Failed to use chosen route '%s'. " "Inspect the log" % self.route.route, "status": self.analysis.status }) return False # By the time start returns it will have fully started the Virtual # Machine. We can now safely release the machine lock. self.release_machine_lock() # Request scheduler action for status 'starting' self.request_scheduler_action(Analysis.STARTING) try: self.guest_manager.start_analysis( self.options, self.task.options.get("monitor", "latest")) except CuckooGuestCriticalTimeout as e: log.error( "Critical timeout reached while starting virtual" " machine. %s", e) return False except CuckooGuestError as e: log.error("Failed to prepare guest for analysis: %s", e) return False return self.analysis.status == Analysis.STARTING
def cuckoo_main(max_analysis_count=0): """Cuckoo main loop. @param max_analysis_count: kill cuckoo after this number of analyses """ try: ResultServer() sched = Scheduler(max_analysis_count) sched.start() except KeyboardInterrupt: sched.stop()
def _allocate_new_machine(self): """ allocating/creating new EC2 instance(autoscale option) """ # read configuration file machinery_options = self.options.get("aws") autoscale_options = self.options.get("autoscale") # If configured, use specific network interface for this # machine, else use the default value. interface = autoscale_options["interface"] if autoscale_options.get( "interface") else machinery_options.get("interface") resultserver_ip = autoscale_options[ "resultserver_ip"] if autoscale_options.get( "resultserver_ip") else config("cuckoo:resultserver:ip") if autoscale_options.get("resultserver_port"): resultserver_port = autoscale_options["resultserver_port"] else: # The ResultServer port might have been dynamically changed, # get it from the ResultServer singleton. Also avoid import # recursion issues by importing ResultServer here. from cuckoo.core.resultserver import ResultServer resultserver_port = ResultServer().port log.info("All machines are busy, allocating new machine") self.dynamic_machines_sequence += 1 self.dynamic_machines_count += 1 new_machine_name = "cuckoo_autoscale_%03d" % self.dynamic_machines_sequence instance = self._create_instance(tags=[{ "Key": "Name", "Value": new_machine_name }, { "Key": self.AUTOSCALE_CUCKOO, "Value": "True" }]) if instance is None: return False self.ec2_machines[instance.id] = instance # sets "new_machine" object in configuration object to avoid raising an exception setattr(self.options, new_machine_name, {}) # add machine to DB self.db.add_machine(name=new_machine_name, label=instance.id, ip=instance.private_ip_address, platform=autoscale_options["platform"], options=autoscale_options["options"], tags=autoscale_options["tags"], interface=interface, snapshot=None, resultserver_ip=resultserver_ip, resultserver_port=resultserver_port) return True
def _initialize(self, module_name): """Read configuration. @param module_name: module name. """ machinery = self.options.get(module_name) for vmname in machinery["machines"]: options = self.options.get(vmname) # If configured, use specific network interface for this # machine, else use the default value. if options.get("interface"): interface = options["interface"] else: interface = machinery.get("interface") if options.get("resultserver_ip"): ip = options["resultserver_ip"] else: ip = config("cuckoo:resultserver:ip") if options.get("resultserver_port"): port = options["resultserver_port"] else: # The ResultServer port might have been dynamically changed, # get it from the ResultServer singleton. Also avoid import # recursion issues by importing ResultServer here. from cuckoo.core.resultserver import ResultServer port = ResultServer().port self.db.add_machine( name=vmname, label=options[self.LABEL], ip=options.ip, platform=options.platform, options=options.get("options", ""), tags=options.tags, interface=interface, snapshot=options.snapshot, resultserver_ip=ip, resultserver_port=port )
def stop_and_wait(self): if self.rt.sock: try: # Use the realtime protocol to request the analyzer to stop. So # that the analysis, logs etc can be closed gracefully. self.rt.send_command_blocking(RealTimeMessages.stop_analyzer(), maxwait=3) except RealtimeError: log.warning("No response from analyzer to stopping request") self.set_analysis_status(Analysis.STOPPING) # Stop all Auxiliary modules self.aux.stop() # Wait for the guest manager wait to stop before stopping the machine. # We want any exception messages to be retrieved from the agent. if self.gm_wait_th.is_alive(): self.gm_wait_th.join(timeout=6) # Stop the analysis machine. try: self.machinery.stop(self.machine.label) except CuckooMachineError as e: log.warning( "Unable to stop machine %s: %s", self.machine.label, e, ) # After all this, we can make the ResultServer forget about the # internal state for this analysis task. ResultServer().del_task(self.task.db_task, self.machine) # Drop the network routing rules if any. self.route.unroute_network()
def launch_analysis(self): """Start analysis.""" succeeded = False if self.task.category == "file" or self.task.category == "archive": target = os.path.basename(self.task.target) else: target = self.task.target log.info("Starting analysis of %s \"%s\" (task #%d, options \"%s\")", self.task.category.upper(), target, self.task.id, emit_options(self.task.options), extra={ "action": "task.init", "status": "starting", "task_id": self.task.id, "target": target, "category": self.task.category, "package": self.task.package, "options": emit_options(self.task.options), "custom": self.task.custom, }) # Initialize the analysis. if not self.init(): logger("Failed to initialize", action="task.init", status="error") return False # Acquire analysis machine. try: self.acquire_machine() except CuckooOperationalError as e: machine_lock.release() log.error("Cannot acquire machine: %s", e, extra={ "action": "vm.acquire", "status": "error", }) return False self.rs_port = self.machine.resultserver_port or ResultServer().port # At this point we can tell the ResultServer about it. try: ResultServer().add_task(self.task, self.machine) except Exception as e: machinery.release(self.machine.label) self.errors.put(e) # Initialize the guest manager. self.guest_manager = GuestManager(self.machine.name, self.machine.ip, self.machine.platform, self.task.id, self) self.aux = RunAuxiliary(self.task, self.machine, self.guest_manager) self.aux.start() # Generate the analysis configuration file. options = self.build_options() # Check if the current task has remotecontrol # enabled before starting the machine. control_enabled = (config("cuckoo:remotecontrol:enabled") and "remotecontrol" in self.task.options) if control_enabled: try: machinery.enable_remote_control(self.machine.label) except NotImplementedError: log.error( "Remote control support has not been implemented for the " "configured machinery module: %s", config("cuckoo:cuckoo:machinery")) try: unlocked = False self.interface = None # Mark the selected analysis machine in the database as started. guest_log = self.db.guest_start(self.task.id, self.machine.name, self.machine.label, machinery.__class__.__name__) logger("Starting VM", action="vm.start", status="pending", vmname=self.machine.name) # Start the machine. machinery.start(self.machine.label, self.task) logger("Started VM", action="vm.start", status="success", vmname=self.machine.name) # retrieve the port used for remote control if control_enabled: try: params = machinery.get_remote_control_params( self.machine.label) self.db.set_machine_rcparams(self.machine.label, params) except NotImplementedError: log.error( "Remote control support has not been implemented for the " "configured machinery module: %s", config("cuckoo:cuckoo:machinery")) # Enable network routing. self.route_network() # By the time start returns it will have fully started the Virtual # Machine. We can now safely release the machine lock. machine_lock.release() unlocked = True # Run and manage the components inside the guest unless this # machine has the "noagent" option specified (please refer to the # wait_finish() function for more details on this function). if "noagent" not in self.machine.options: self.guest_manage(options) else: self.wait_finish() succeeded = True except CuckooMachineSnapshotError as e: log.error( "Unable to restore to the snapshot for this Virtual Machine! " "Does your VM have a proper Snapshot and can you revert to it " "manually? VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.resume", "status": "error", "vmname": self.machine.name, }) except CuckooMachineError as e: if not unlocked: machine_lock.release() log.error("Error starting Virtual Machine! VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.start", "status": "error", "vmname": self.machine.name, }) except CuckooGuestCriticalTimeout as e: if not unlocked: machine_lock.release() log.error( "Error from machine '%s': it appears that this Virtual " "Machine hasn't been configured properly as the Cuckoo Host " "wasn't able to connect to the Guest. There could be a few " "reasons for this, please refer to our documentation on the " "matter: %s", self.machine.name, faq("troubleshooting-vm-network-configuration"), extra={ "error_action": "vmrouting", "action": "guest.handle", "status": "error", "task_id": self.task.id, }) except CuckooGuestError as e: if not unlocked: machine_lock.release() log.error("Error from the Cuckoo Guest: %s", e, extra={ "action": "guest.handle", "status": "error", "task_id": self.task.id, }) finally: # Stop Auxiliary modules. if not self.stopped_aux: self.stopped_aux = True self.aux.stop() # Take a memory dump of the machine before shutting it off. if self.cfg.cuckoo.memory_dump or self.task.memory: logger("Taking full memory dump", action="vm.memdump", status="pending", vmname=self.machine.name) try: dump_path = os.path.join(self.storage, "memory.dmp") machinery.dump_memory(self.machine.label, dump_path) logger("Taken full memory dump", action="vm.memdump", status="success", vmname=self.machine.name) except NotImplementedError: log.error( "The memory dump functionality is not available for " "the current machine manager.", extra={ "action": "vm.memdump", "status": "error", "vmname": self.machine.name, }) except CuckooMachineError as e: log.error("Machinery error: %s", e, extra={ "action": "vm.memdump", "status": "error", }) logger("Stopping VM", action="vm.stop", status="pending", vmname=self.machine.name) try: # Stop the analysis machine. machinery.stop(self.machine.label) except CuckooMachineError as e: log.warning("Unable to stop machine %s: %s", self.machine.label, e, extra={ "action": "vm.stop", "status": "error", "vmname": self.machine.name, }) logger("Stopped VM", action="vm.stop", status="success", vmname=self.machine.name) # Disable remote control after stopping the machine # if it was enabled for the task. if control_enabled: try: machinery.disable_remote_control(self.machine.label) except NotImplementedError: log.error( "Remote control support has not been implemented for the " "configured machinery module: %s", config("cuckoo:cuckoo:machinery")) # Mark the machine in the database as stopped. Unless this machine # has been marked as dead, we just keep it as "started" in the # database so it'll not be used later on in this session. self.db.guest_stop(guest_log) # After all this, we can make the ResultServer forget about the # internal state for this analysis task. ResultServer().del_task(self.task, self.machine) # Drop the network routing rules if any. if not self.unrouted_network: self.unroute_network() try: # Release the analysis machine. But only if the machine has # not turned dead yet. machinery.release(self.machine.label) except CuckooMachineError as e: log.error( "Unable to release machine %s, reason %s. You might need " "to restore it manually.", self.machine.label, e, extra={ "action": "vm.release", "status": "error", "vmname": self.machine.name, }) return succeeded
def stop_and_wait(self): """Stop the analysis by stopping the aux modules, optionally dumping VM memory, stopping the VM and deleting the task from the resultserver.""" self.set_analysis_status(Analysis.STOPPING) # Stop all Auxiliary modules self.aux.stop() # If enabled, make a full memory dump of the machine # before it shuts down if config("cuckoo:cuckoo:memory_dump") or self.task.memory: logger("Taking full memory dump", action="vm.memdump", status="pending", vmname=self.machine.name) try: dump_path = os.path.join(self.task.path, "memory.dmp") self.machinery.dump_memory(self.machine.label, dump_path) logger("Taken full memory dump", action="vm.memdump", status="success", vmname=self.machine.name) except NotImplementedError: log.error( "The memory dump functionality is not available for " "the current machine manager.", extra={ "action": "vm.memdump", "status": "error", "vmname": self.machine.name, }) except CuckooMachineError as e: log.error("Machinery error: %s", e, extra={ "action": "vm.memdump", "status": "error", }) logger("Stopping VM", action="vm.stop", status="pending", vmname=self.machine.name) # Stop the analysis machine. try: self.machinery.stop(self.machine.label) except CuckooMachineError as e: log.warning("Unable to stop machine %s: %s", self.machine.label, e, extra={ "action": "vm.stop", "status": "error", "vmname": self.machine.name, }) logger("Stopped VM", action="vm.stop", status="success", vmname=self.machine.name) # Disable remote control after stopping the machine # if it was enabled for the task. if self.control_enabled: try: self.machinery.disable_remote_control(self.machine.label) except NotImplementedError: log.exception( "Remote control support has not been implemented " "for machinery %s.", self.machine.manager) # After all this, we can make the ResultServer forget about the # internal state for this analysis task. ResultServer().del_task(self.task.db_task, self.machine) # Drop the network routing rules if any. self.route.unroute_network()
def start_and_wait(self): """Start the analysis by running the auxiliary modules, adding the task to the resultserver, starting the machine and running a guest manager.""" # Set guest status to starting and start analysis machine self.set_analysis_status(Analysis.STARTING) target = self.target.target if self.target.target and self.target.is_file: target = os.path.basename(target) log.info( "Starting analysis (task #%s, options: '%s') type '%s'." " Target: %s '%s'", self.task.id, self.options["options"], self.task.type, self.target.category, target, extra={ "action": "task.init", "status": "starting", "task_id": self.task.id, "target": target, "category": self.target.category, "package": self.task.package, "options": self.options["options"], "custom": self.task.custom, "type": self.task.type }) ResultServer().add_task(self.task.db_task, self.machine, self.rt) # Start auxiliary modules self.aux.start() if self.control_enabled: try: self.machinery.enable_remote_control(self.machine.label) except NotImplementedError: self.control_enabled = False log.exception( "Remote control support has not been implemented " "for machinery %s.", self.machine.manager) # Json log for performance measurement purposes logger("Starting VM", action="vm.start", status="pending", vmname=self.machine.name) try: self.machinery.start(self.machine.label, self.task.db_task) except CuckooMachineSnapshotError as e: log.error( "Unable to restore to the snapshot for this Virtual Machine! " "Does your VM have a proper Snapshot and can you revert to it " "manually? VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.resume", "status": "error", "vmname": self.machine.name, }) return False except CuckooMachineError as e: log.error("Error starting Virtual Machine! VM: %s, error: %s", self.machine.name, e, extra={ "action": "vm.start", "status": "error", "vmname": self.machine.name, }) return False logger("Started VM", action="vm.start", status="success", vmname=self.machine.name) # retrieve the port used for remote control if self.control_enabled: try: params = self.machinery.get_remote_control_params( self.machine.label) self.db.set_machine_rcparams(self.machine.label, params) except NotImplementedError: log.exception( "Remote control support has not been implemented " "for machinery %s.", self.machine.manager) # Enable network routing self.route.route_network() # By the time start returns it will have fully started the Virtual # Machine. We can now safely release the machine lock. self.release_machine_lock() # Request scheduler action for status 'starting' self.request_scheduler_action(Analysis.STARTING) # Choose the correct way of waiting or managing the agent and # execute it try: self.manage() except CuckooGuestCriticalTimeout as e: log.error( "Error from machine '%s': it appears that this Virtual " "Machine hasn't been configured properly as the Cuckoo Host " "wasn't able to connect to the Guest. There could be a few " "reasons for this, please refer to our documentation on the " "matter: %s", self.machine.name, faq("troubleshooting-vm-network-configuration"), extra={ "error_action": "vmrouting", "action": "guest.handle", "status": "error", "task_id": self.task.id, }) except CuckooGuestError as e: log.error("Error from the Cuckoo Guest: %s", e, extra={ "action": "guest.handle", "status": "error", "task_id": self.task.id, }) return True