def __init__(self): self.module_name = "" self.options = None self.options_globals = Config( os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf")) # Database pointer. self.db = Database() # Machine table is cleaned to be filled from configuration file at each start. self.db.clean_machines()
def __init__(self): self.module_name = "" self.options = None self.options_globals = Config(os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf")) # Database pointer. self.db = Database() # Machine table is cleaned to be filled from configuration file at each start. self.db.clean_machines()
def run(self): """Run manager thread.""" success = self.launch_analysis() Database().complete(self.task.id, success) self.process_results() log.debug("Releasing database task #%d with status %s", self.task.id, success) log.info("Task #%d: analysis procedure completed", self.task.id)
def __init__(self, task_id): """@param analysis_path: analysis folder path. """ self.task = Database().view_task(task_id).to_dict() self.analysis_path = os.path.join(CUCKOO_ROOT, "storage", "analyses", str(task_id)) self.cfg = Config(cfg=os.path.join(CUCKOO_ROOT, "conf", "reporting.conf"))
def run(self): """Run debug analysis. @return: debug information dict. """ self.key = "debug" debug = {"log": "", "errors": []} if os.path.exists(self.log_path): try: debug["log"] = codecs.open(self.log_path, "rb", "utf-8").read() except ValueError as e: raise CuckooProcessingError("Error decoding %s: %s" % (self.log_path, e)) except (IOError, OSError) as e: raise CuckooProcessingError("Error opening %s: %s" % (self.log_path, e)) for error in Database().view_errors(int(self.task["id"])): debug["errors"].append(error.message) return debug
class MachineManager(object): """Base abstract class for analysis machine manager.""" def __init__(self): self.module_name = "" self.options = None self.options_globals = Config( os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf")) # Database pointer. self.db = Database() # Machine table is cleaned to be filled from configuration file at each start. self.db.clean_machines() def set_options(self, options): """Set machine manager options. @param options: machine manager options dict. """ self.options = options def initialize(self, module_name): """Read and load machines configuration, try to check the configuration. @param module_name: module name. """ # Load. self._initialize(module_name) # Run initialization checks. self._initialize_check() def _initialize(self, module_name): """Read configuration. @param module_name: module name. """ self.module_name = module_name mmanager_opts = self.options.get(module_name) for machine_id in mmanager_opts["machines"].strip().split(","): try: machine_opts = self.options.get(machine_id.strip()) machine = Dictionary() machine.id = machine_id.strip() machine.label = machine_opts["label"].strip() machine.platform = machine_opts["platform"].strip() machine.ip = machine_opts["ip"].strip() self.db.add_machine(name=machine.id, label=machine.label, ip=machine.ip, platform=machine.platform) except (AttributeError, CuckooOperationalError): log.warning( "Configuration details about machine %s are missing. Continue", machine_id) continue def _initialize_check(self): """Runs checks against virtualization software when a machine manager is initialized. @note: in machine manager modules you may override or superclass his method. @raise CuckooMachineError: if a misconfiguration or a unkown vm state is found. """ try: configured_vm = self._list() except NotImplementedError: return for machine in self.machines(): if machine.label not in configured_vm: raise CuckooCriticalError( "Configured machine {0} was not detected or it's not in proper state" .format(machine.label)) if not self.options_globals.timeouts.vm_state: raise CuckooCriticalError( "Virtual machine state change timeout setting not found, please add it to the config file" ) def machines(self): """List virtual machines. @return: virtual machines list """ return self.db.list_machines() def availables(self): """How many machines are free. @return: free machines count. """ return self.db.count_machines_available() def acquire(self, machine_id=None, platform=None): """Acquire a machine to start analysis. @param machine_id: machine ID. @param platform: machine platform. @return: machine or None. """ if machine_id: return self.db.lock_machine(name=machine_id) elif platform: return self.db.lock_machine(platform=platform) else: return self.db.lock_machine() def release(self, label=None): """Release a machine. @param label: machine name. """ self.db.unlock_machine(label) def running(self): """Returns running virtual machines. @return: running virtual machines list. """ return self.db.list_machines(locked=True) def shutdown(self): """Shutdown the machine manager. Kills all alive machines. @raise CuckooMachineError: if unable to stop machine. """ if self.running().count() > 0: log.info("Still %s guests alive. Shutting down...", self.running().count()) for machine in self.running(): try: self.stop(machine.label) except CuckooMachineError as e: log.warning( "Unable to shutdown machine %s, please check " "manually. Error: %s", machine.label, e) def set_status(self, label, status): """Set status for a virtual machine. @param label: virtual machine label @param status: new virtual machine status """ self.db.set_machine_status(label, status) def start(self, label=None): """Start a machine. @param label: machine name. @raise NotImplementedError: this method is abstract. """ raise NotImplementedError def stop(self, label=None): """Stop a machine. @param label: machine name. @raise NotImplementedError: this method is abstract. """ raise NotImplementedError def _list(self): """Lists virtual machines configured. @raise NotImplementedError: this method is abstract. """ raise NotImplementedError def dump_memory(self, path): """Takes a memory dump of a machine. @param path: path to where to store the memory dump. """ raise NotImplementedError def _wait_status(self, label, state): """Waits for a vm status. @param label: virtual machine name. @param state: virtual machine status, accepts more than one states in a list. @raise CuckooMachineError: if default waiting timeout expire. """ # This block was originally suggested by Loic Jaquemet. waitme = 0 try: current = self._status(label) except NameError: return if isinstance(state, str): state = [state] while current not in state: log.debug( "Waiting %i cuckooseconds for machine %s to switch to status %s", waitme, label, state) if waitme > int(self.options_globals.timeouts.vm_state): raise CuckooMachineError( "Timeout hit while for machine {0} to change status". format(label)) time.sleep(1) waitme += 1 current = self._status(label)
def main(): parser = argparse.ArgumentParser() parser.add_argument("target", type=str, help="URL, path to the file or folder to analyze") parser.add_argument("--url", action="store_true", default=False, help="Specify whether the target is an URL", required=False) parser.add_argument("--package", type=str, action="store", default="", help="Specify an analysis package", required=False) parser.add_argument("--custom", type=str, action="store", default="", help="Specify any custom value", required=False) parser.add_argument("--timeout", type=int, action="store", default=0, help="Specify an analysis timeout", required=False) parser.add_argument( "--options", type=str, action="store", default="", help= "Specify options for the analysis package (e.g. \"name=value,name2=value2\")", required=False) parser.add_argument( "--priority", type=int, action="store", default=1, help="Specify a priority for the analysis represented by an integer", required=False) parser.add_argument( "--machine", type=str, action="store", default="", help="Specify the identifier of a machine you want to use", required=False) parser.add_argument( "--platform", type=str, action="store", default="", help= "Specify the operating system platform you want to use (windows/darwin/linux)", required=False) parser.add_argument( "--memory", action="store_true", default=False, help="Enable to take a memory dump of the analysis machine", required=False) parser.add_argument( "--enforce-timeout", action="store_true", default=False, help="Enable to force the analysis to run for the full timeout period", required=False) try: args = parser.parse_args() except IOError as e: parser.error(e) return False db = Database() # Try to get input as utf-8. try: target = args.target.decode("utf-8") except UnicodeEncodeError: target = args.target if args.url: task_id = db.add_url(target, package=args.package, timeout=args.timeout, options=args.options, priority=args.priority, machine=args.machine, platform=args.platform, custom=args.custom, memory=args.memory, enforce_timeout=args.enforce_timeout) print( bold(green("Success")) + ": URL \"{0}\" added as task with ID {1}".format(target, task_id)) else: # Get absolute path to deal with relative. path = os.path.abspath(target) if not os.path.exists(path): print( bold(red("Error")) + ": the specified file/folder does not exist at path \"{0}\"". format(path)) return False files = [] if os.path.isdir(path): for dirname, dirnames, filenames in os.walk(path): for file_name in filenames: file_path = os.path.join(dirname, file_name) if os.path.isfile(file_path): files.append(file_path) else: files.append(path) for file_path in files: task_id = db.add_path(file_path=file_path, package=args.package, timeout=args.timeout, options=args.options, priority=args.priority, machine=args.machine, platform=args.platform, custom=args.custom, memory=args.memory, enforce_timeout=args.enforce_timeout) if task_id: print( bold(green("Success")) + ": File \"{0}\" added as task with ID {1}".format( file_path, task_id)) else: print(bold(red("Error")) + ": adding task to database")
def emit(self, record): if hasattr(record, "task_id"): db = Database() db.add_error(record.msg, int(record.task_id))
def __init__(self): self.running = True self.cfg = Config() self.db = Database()
class Scheduler: """Tasks Scheduler. This class is responsible for the main execution loop of the tool. It prepares the analysis machines and keep waiting and loading for new analysis tasks. Whenever a new task is available, it launches AnalysisManager which will take care of running the full analysis process and operating with the assigned analysis machine. """ def __init__(self): self.running = True self.cfg = Config() self.db = Database() def initialize(self): """Initialize the machine manager.""" global mmanager mmanager_name = self.cfg.cuckoo.machine_manager log.info("Using \"%s\" machine manager", mmanager_name) # Get registered class name. Only one machine manager is imported, # therefore there should be only one class in the list. plugin = list_plugins("machinemanagers")[0] # Initialize the machine manager. mmanager = plugin() # Find its configuration file. conf = os.path.join(CUCKOO_ROOT, "conf", "%s.conf" % mmanager_name) if not os.path.exists(conf): raise CuckooCriticalError("The configuration file for machine " "manager \"{0}\" does not exist at path: " "{1}".format(mmanager_name, conf)) # Provide a dictionary with the configuration options to the # machine manager instance. mmanager.set_options(Config(conf)) # Initialize the machine manager. mmanager.initialize(mmanager_name) # At this point all the available machines should have been identified # and added to the list. If none were found, Cuckoo needs to abort the # execution. if mmanager.machines().count() == 0: raise CuckooCriticalError("No machines available") else: log.info("Loaded %s machine/s", mmanager.machines().count()) def stop(self): """Stop scheduler.""" self.running = False # Shutdown machine manager (used to kill machines that still alive). mmanager.shutdown() def start(self): """Start scheduler.""" self.initialize() log.info("Waiting for analysis tasks...") # This loop runs forever. while self.running: time.sleep(1) # If no machines are available, it's pointless to fetch for # pending tasks. Loop over. if mmanager.availables() == 0: continue # Fetch a pending analysis task. task = self.db.fetch_and_process() if task: log.debug("Processing task #%s", task.id) # Initialize the analysis manager. analysis = AnalysisManager(task) # Start. analysis.start()
class Scheduler: """Tasks Scheduler. This class is responsible for the main execution loop of the tool. It prepares the analysis machines and keep waiting and loading for new analysis tasks. Whenever a new task is available, it launches AnalysisManager which will take care of running the full analysis process and operating with the assigned analysis machine. """ def __init__(self): self.running = True self.cfg = Config() self.db = Database() def initialize(self): """Initialize the machine manager.""" global mmanager mmanager_name = self.cfg.cuckoo.machine_manager log.info("Using \"%s\" machine manager", mmanager_name) # Get registered class name. Only one machine manager is imported, # therefore there should be only one class in the list. plugin = list_plugins("machinemanagers")[0] # Initialize the machine manager. mmanager = plugin() # Find its configuration file. conf = os.path.join(CUCKOO_ROOT, "conf", "%s.conf" % mmanager_name) if not os.path.exists(conf): raise CuckooCriticalError( "The configuration file for machine " "manager \"{0}\" does not exist at path: " "{1}".format(mmanager_name, conf)) # Provide a dictionary with the configuration options to the # machine manager instance. mmanager.set_options(Config(conf)) # Initialize the machine manager. mmanager.initialize(mmanager_name) # At this point all the available machines should have been identified # and added to the list. If none were found, Cuckoo needs to abort the # execution. if mmanager.machines().count() == 0: raise CuckooCriticalError("No machines available") else: log.info("Loaded %s machine/s", mmanager.machines().count()) def stop(self): """Stop scheduler.""" self.running = False # Shutdown machine manager (used to kill machines that still alive). mmanager.shutdown() def start(self): """Start scheduler.""" self.initialize() log.info("Waiting for analysis tasks...") # This loop runs forever. while self.running: time.sleep(1) # If no machines are available, it's pointless to fetch for # pending tasks. Loop over. if mmanager.availables() == 0: continue # Fetch a pending analysis task. task = self.db.fetch_and_process() if task: log.debug("Processing task #%s", task.id) # Initialize the analysis manager. analysis = AnalysisManager(task) # Start. analysis.start()
def launch_analysis(self): """Start analysis.""" sniffer = None succeeded = False log.info("Starting analysis of %s \"%s\" (task=%d)", self.task.category.upper(), self.task.target, self.task.id) # Initialize the the analysis folders. if not self.init_storage(): return False if self.task.category == "file": # Store a copy of the original file. if not self.store_file(): return False # Generate the analysis configuration file. options = self.build_options() # Acquire analysis machine. machine = self.acquire_machine() # At this point we can tell the Resultserver about it Resultserver().add_task(self.task, machine) # If enabled in the configuration, start the tcpdump instance. if self.cfg.sniffer.enabled: sniffer = Sniffer(self.cfg.sniffer.tcpdump) sniffer.start(interface=self.cfg.sniffer.interface, host=machine.ip, file_path=os.path.join(self.storage, "dump.pcap")) try: # Mark the selected analysis machine in the database as started. guest_log = Database().guest_start(self.task.id, machine.name, machine.label, mmanager.__class__.__name__) # Start the machine. mmanager.start(machine.label) except CuckooMachineError as e: log.error(str(e), extra={"task_id": self.task.id}) # Stop the sniffer. if sniffer: sniffer.stop() return False else: try: # Initialize the guest manager. guest = GuestManager(machine.name, machine.ip, machine.platform) # Start the analysis. guest.start_analysis(options) except CuckooGuestError as e: log.error(str(e), extra={"task_id": self.task.id}) # Stop the sniffer. if sniffer: sniffer.stop() return False else: # Wait for analysis completion. try: guest.wait_for_completion() succeeded = True except CuckooGuestError as e: log.error(str(e), extra={"task_id": self.task.id}) succeeded = False # Retrieve the analysis results and store them. try: guest.save_results(self.storage) succeeded = True except CuckooGuestError as e: log.error(str(e), extra={"task_id": self.task.id}) succeeded = False finally: # Stop the sniffer. if sniffer: sniffer.stop() # Take a memory dump of the machine before shutting it off. if self.cfg.cuckoo.memory_dump or self.task.memory: try: mmanager.dump_memory( machine.label, os.path.join(self.storage, "memory.dmp")) except NotImplementedError: log.error("The memory dump functionality is not available " "for current machine manager") except CuckooMachineError as e: log.error(e) try: # Stop the analysis machine. mmanager.stop(machine.label) except CuckooMachineError as e: log.warning("Unable to stop machine %s: %s", machine.label, e) # Market the machine in the database as stopped. Database().guest_stop(guest_log) try: # Release the analysis machine. mmanager.release(machine.label) except CuckooMachineError as e: log.error( "Unable to release machine %s, reason %s. " "You might need to restore it manually", machine.label, e) # after all this, we can make the Resultserver forget about it Resultserver().del_task(self.task, machine) return succeeded
def main(): parser = argparse.ArgumentParser() parser.add_argument("target", type=str, help="URL, path to the file or folder to analyze") parser.add_argument("--url", action="store_true", default=False, help="Specify whether the target is an URL", required=False) parser.add_argument("--package", type=str, action="store", default="", help="Specify an analysis package", required=False) parser.add_argument("--custom", type=str, action="store", default="", help="Specify any custom value", required=False) parser.add_argument("--timeout", type=int, action="store", default=0, help="Specify an analysis timeout", required=False) parser.add_argument("--options", type=str, action="store", default="", help="Specify options for the analysis package (e.g. \"name=value,name2=value2\")", required=False) parser.add_argument("--priority", type=int, action="store", default=1, help="Specify a priority for the analysis represented by an integer", required=False) parser.add_argument("--machine", type=str, action="store", default="", help="Specify the identifier of a machine you want to use", required=False) parser.add_argument("--platform", type=str, action="store", default="", help="Specify the operating system platform you want to use (windows/darwin/linux)", required=False) parser.add_argument("--memory", action="store_true", default=False, help="Enable to take a memory dump of the analysis machine", required=False) parser.add_argument("--enforce-timeout", action="store_true", default=False, help="Enable to force the analysis to run for the full timeout period", required=False) try: args = parser.parse_args() except IOError as e: parser.error(e) return False db = Database() # Try to get input as utf-8. try: target = args.target.decode("utf-8") except UnicodeEncodeError: target = args.target if args.url: task_id = db.add_url(target, package=args.package, timeout=args.timeout, options=args.options, priority=args.priority, machine=args.machine, platform=args.platform, custom=args.custom, memory=args.memory, enforce_timeout=args.enforce_timeout) print(bold(green("Success")) + ": URL \"{0}\" added as task with ID {1}".format(target, task_id)) else: # Get absolute path to deal with relative. path = os.path.abspath(target) if not os.path.exists(path): print(bold(red("Error")) + ": the specified file/folder does not exist at path \"{0}\"".format(path)) return False files = [] if os.path.isdir(path): for dirname, dirnames, filenames in os.walk(path): for file_name in filenames: file_path = os.path.join(dirname, file_name) if os.path.isfile(file_path): files.append(file_path) else: files.append(path) for file_path in files: task_id = db.add_path(file_path=file_path, package=args.package, timeout=args.timeout, options=args.options, priority=args.priority, machine=args.machine, platform=args.platform, custom=args.custom, memory=args.memory, enforce_timeout=args.enforce_timeout) if task_id: print(bold(green("Success")) + ": File \"{0}\" added as task with ID {1}".format(file_path, task_id)) else: print(bold(red("Error")) + ": adding task to database")
def __init__(self, task_id): """@param task_id: ID of the analyses to process.""" self.task = Database().view_task(task_id).to_dict() self.analysis_path = os.path.join(CUCKOO_ROOT, "storage", "analyses", str(task_id))
except ImportError: sys.stderr.write("ERROR: Bottle library is missing") sys.exit(1) logging.basicConfig() sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) from lib.dragon.core.database import Database from lib.dragon.common.constants import CUCKOO_ROOT from lib.dragon.common.utils import store_temp_file # Templating engine. env = Environment() env.loader = FileSystemLoader(os.path.join(CUCKOO_ROOT, "data", "html")) # Global db pointer. db = Database() @hook("after_request") def custom_headers(): """Set some custom headers across all HTTP responses.""" response.headers["Server"] = "Machete Server" response.headers["X-Content-Type-Options"] = "nosniff" response.headers["X-Frame-Options"] = "DENY" response.headers["X-XSS-Protection"] = "1; mode=block" response.headers["Pragma"] = "no-cache" response.headers["Cache-Control"] = "no-cache" response.headers["Expires"] = "0" @route("/") def index(): context = {}
class MachineManager(object): """Base abstract class for analysis machine manager.""" def __init__(self): self.module_name = "" self.options = None self.options_globals = Config(os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf")) # Database pointer. self.db = Database() # Machine table is cleaned to be filled from configuration file at each start. self.db.clean_machines() def set_options(self, options): """Set machine manager options. @param options: machine manager options dict. """ self.options = options def initialize(self, module_name): """Read and load machines configuration, try to check the configuration. @param module_name: module name. """ # Load. self._initialize(module_name) # Run initialization checks. self._initialize_check() def _initialize(self, module_name): """Read configuration. @param module_name: module name. """ self.module_name = module_name mmanager_opts = self.options.get(module_name) for machine_id in mmanager_opts["machines"].strip().split(","): try: machine_opts = self.options.get(machine_id.strip()) machine = Dictionary() machine.id = machine_id.strip() machine.label = machine_opts["label"].strip() machine.platform = machine_opts["platform"].strip() machine.ip = machine_opts["ip"].strip() self.db.add_machine(name=machine.id, label=machine.label, ip=machine.ip, platform=machine.platform) except (AttributeError, CuckooOperationalError): log.warning("Configuration details about machine %s are missing. Continue", machine_id) continue def _initialize_check(self): """Runs checks against virtualization software when a machine manager is initialized. @note: in machine manager modules you may override or superclass his method. @raise CuckooMachineError: if a misconfiguration or a unkown vm state is found. """ try: configured_vm = self._list() except NotImplementedError: return for machine in self.machines(): if machine.label not in configured_vm: raise CuckooCriticalError("Configured machine {0} was not detected or it's not in proper state".format(machine.label)) if not self.options_globals.timeouts.vm_state: raise CuckooCriticalError("Virtual machine state change timeout setting not found, please add it to the config file") def machines(self): """List virtual machines. @return: virtual machines list """ return self.db.list_machines() def availables(self): """How many machines are free. @return: free machines count. """ return self.db.count_machines_available() def acquire(self, machine_id=None, platform=None): """Acquire a machine to start analysis. @param machine_id: machine ID. @param platform: machine platform. @return: machine or None. """ if machine_id: return self.db.lock_machine(name=machine_id) elif platform: return self.db.lock_machine(platform=platform) else: return self.db.lock_machine() def release(self, label=None): """Release a machine. @param label: machine name. """ self.db.unlock_machine(label) def running(self): """Returns running virtual machines. @return: running virtual machines list. """ return self.db.list_machines(locked=True) def shutdown(self): """Shutdown the machine manager. Kills all alive machines. @raise CuckooMachineError: if unable to stop machine. """ if self.running().count() > 0: log.info("Still %s guests alive. Shutting down...", self.running().count()) for machine in self.running(): try: self.stop(machine.label) except CuckooMachineError as e: log.warning("Unable to shutdown machine %s, please check " "manually. Error: %s", machine.label, e) def set_status(self, label, status): """Set status for a virtual machine. @param label: virtual machine label @param status: new virtual machine status """ self.db.set_machine_status(label, status) def start(self, label=None): """Start a machine. @param label: machine name. @raise NotImplementedError: this method is abstract. """ raise NotImplementedError def stop(self, label=None): """Stop a machine. @param label: machine name. @raise NotImplementedError: this method is abstract. """ raise NotImplementedError def _list(self): """Lists virtual machines configured. @raise NotImplementedError: this method is abstract. """ raise NotImplementedError def dump_memory(self, path): """Takes a memory dump of a machine. @param path: path to where to store the memory dump. """ raise NotImplementedError def _wait_status(self, label, state): """Waits for a vm status. @param label: virtual machine name. @param state: virtual machine status, accepts more than one states in a list. @raise CuckooMachineError: if default waiting timeout expire. """ # This block was originally suggested by Loic Jaquemet. waitme = 0 try: current = self._status(label) except NameError: return if isinstance(state, str): state = [state] while current not in state: log.debug("Waiting %i cuckooseconds for machine %s to switch to status %s", waitme, label, state) if waitme > int(self.options_globals.timeouts.vm_state): raise CuckooMachineError("Timeout hit while for machine {0} to change status".format(label)) time.sleep(1) waitme += 1 current = self._status(label)