def _download_file(route, url, options): socks5s = _load_socks5_operational() proxies = dict() response = False headers = {"User-Agent": choice(user_agents)} if route: if route == "tor": proxies = { "http": "socks5://127.0.0.1:9050", "https": "socks5://127.0.0.1:9050", } elif route in socks5s: proxies = { "http": "socks5://{}:{}".format(socks5s[route]["host"], socks5s[route]["port"]), "https": "socks5://{}:{}".format(socks5s[route]["host"], socks5s[route]["port"]), } # load headers for option in options.split(","): if option.startswith("dne_"): key, value = option.split("=") headers[key.replace("dne_", "")] = value try: response = requests.get(url, headers=headers, proxies=proxies) if response and response.status_code == 200: return response.content except Exception as e: log.error(e) print(e) return response
def launch_analysis(self): """Start analysis.""" succeeded = False dead_machine = False self.socks5s = _load_socks5_operational() log.info("Task #{0}: Starting analysis of {1} '{2}'".format( self.task.id, self.task.category.upper(), convert_to_printable(self.task.target))) # Initialize the analysis folders. if not self.init_storage(): log.debug("Failed to initialize the analysis folder") return False if self.task.category in ["file", "pcap", "static"]: sha256 = File(self.task.target).get_sha256() # Check whether the file has been changed for some unknown reason. # And fail this analysis if it has been modified. if not self.check_file(sha256): return False # Store a copy of the original file. if not self.store_file(sha256): return False if self.task.category in ("pcap", "static"): if self.task.category == "pcap": if hasattr(os, "symlink"): os.symlink(self.binary, os.path.join(self.storage, "dump.pcap")) else: shutil.copy(self.binary, os.path.join(self.storage, "dump.pcap")) # create the logs/files directories as # normally the resultserver would do it dirnames = ["logs", "files", "aux"] for dirname in dirnames: try: os.makedirs(os.path.join(self.storage, dirname)) except: pass return True # Acquire analysis machine. try: self.acquire_machine() self.db.set_task_vm(self.task.id, self.machine.label, self.machine.id) # At this point we can tell the ResultServer about it. except CuckooOperationalError as e: machine_lock.release() log.error("Task #{0}: Cannot acquire machine: {1}".format( self.task.id, e), exc_info=True) return False # Generate the analysis configuration file. options = self.build_options() try: ResultServer().add_task(self.task, self.machine) except Exception as e: machinery.release(self.machine.label) log.exception(e, exc_info=True) self.errors.put(e) aux = RunAuxiliary(task=self.task, machine=self.machine) try: unlocked = False # 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__) # Start the machine. machinery.start(self.machine.label) # 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 aux.start() # Initialize the guest manager. guest = GuestManager(self.machine.name, self.machine.ip, self.machine.platform, self.task.id, self) options["clock"] = self.db.update_clock(self.task.id) self.db.guest_set_status(self.task.id, "starting") # Start the analysis. guest.start_analysis(options) if self.db.guest_get_status(self.task.id) == "starting": self.db.guest_set_status(self.task.id, "running") guest.wait_for_completion() self.db.guest_set_status(self.task.id, "stopping") succeeded = True except CuckooMachineError as e: if not unlocked: machine_lock.release() log.error(str(e), extra={"task_id": self.task.id}, exc_info=True) dead_machine = True except CuckooGuestError as e: if not unlocked: machine_lock.release() log.error(str(e), extra={"task_id": self.task.id}, exc_info=True) finally: # Stop Auxiliary modules. aux.stop() # Take a memory dump of the machine before shutting it off. if self.cfg.cuckoo.memory_dump or self.task.memory: try: dump_path = get_memdump_path(self.task.id) need_space, space_available = free_space_monitor( os.path.dirname(dump_path), return_value=True) if need_space: log.error( "Not enough free disk space! Could not dump ram (Only %d MB!)", space_available) else: machinery.dump_memory(self.machine.label, dump_path) except NotImplementedError: log.error("The memory dump functionality is not available " "for the current machine manager.") except CuckooMachineError as e: log.error(e, exc_info=True) try: # Stop the analysis machine. machinery.stop(self.machine.label) except CuckooMachineError as e: log.warning( "Task #{0}: Unable to stop machine {1}: {2}".format( self.task.id, self.machine.label, e)) # 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. self.unroute_network() if dead_machine: # Remove the guest from the database, so that we can assign a # new guest when the task is being analyzed with another # machine. self.db.guest_remove(guest_log) # Remove the analysis directory that has been created so # far, as launch_analysis() is going to be doing that again. shutil.rmtree(self.storage) # This machine has turned dead, so we throw an exception here # which informs the AnalysisManager that it should analyze # this task again with another available machine. raise CuckooDeadMachine() 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("Task #{0}: Unable to release machine {1}, reason " "{2}. You might need to restore it manually.".format( self.task.id, self.machine.label, e)) return succeeded
def download_file(**kwargs): """ Example of kwargs { "errors": [], "content": content, "request": request, "task_id": [], "url": False, "params": {}, "headers": {}, "service": "tasks_create_file_API", "path": tmp_path, "fhash": False, "options": options, "only_extraction": False, } """ static, package, timeout, priority, _, machine, platform, tags, custom, memory, \ clock, enforce_timeout, shrike_url, shrike_msg, shrike_sid, shrike_refer, unique, referrer, \ tlp, tags_tasks, route, cape = parse_request_arguments(kwargs["request"]) onesuccess = False # in case if user didn't specify routing, and we have enabled random route if not route: socks5s = _load_socks5_operational() socks5s_random = "" vpn_random = "" if routing_conf.socks5.random_socks5 and socks5s: socks5s_random = choice(socks5s.values()).get("name", False) if routing_conf.vpn.random_vpn: vpn_random = choice(list(vpns.values())).get("name", False) if vpn_random and socks5s_random: route = choice((vpn_random, socks5s_random)) elif vpn_random: route = vpn_random elif socks5s_random: route = socks5s_random if package: if package == "Emotet": return "error", { "error": "Hey guy update your script, this package doesn't exist anymore" } if package.endswith("_x64"): if tags: if "x64" not in tags: tags += ",x64" else: tags = "x64" if tags: if not all([tag.strip() in all_vms_tags for tag in tags.split(",")]): return "error", { "error": "Check Tags help, you have introduced incorrect tag(s)" } elif all([tag in tags for tag in ("x64", "x86")]): return "error", { "error": "Check Tags help, you have introduced x86 and x64 tags for the same task, choose only 1" } if not kwargs.get("content", False) and kwargs.get("url", False): try: r = requests.get(kwargs["url"], params=kwargs.get("params", {}), headers=kwargs.get("headers", {}), verify=False) except requests.exceptions.RequestException as e: logging.error(e) return "error", { "error": "Provided hash not found on {}".format(kwargs["service"]) } if r.status_code == 200 and r.content != b"Hash Not Present" and b"The request requires higher privileges than provided by the access token" not in r.content: kwargs["content"] = r.content elif r.status_code == 403: return "error", { "error": "API key provided is not a valid {0} key or is not authorized for {0} downloads" .format(kwargs["service"]) } elif r.status_code == 404: return "error", { "error": "Server returns 404 from {}".format(kwargs["service"]) } else: return "error", { "error": "Was impossible to download from {0}".format(kwargs["service"]) } if not kwargs["content"]: return "error", { "error": "Error downloading file from {}".format(kwargs["service"]) } try: if kwargs.get("fhash", False): retrieved_hash = hashes[len(kwargs["fhash"])]( kwargs["content"]).hexdigest() if retrieved_hash != kwargs["fhash"].lower(): return "error", { "error": "Hashes mismatch, original hash: {} - retrieved hash: {}". format(kwargs["fhash"], retrieved_hash) } if not os.path.exists(kwargs.get("path")): f = open(kwargs["path"], 'wb') f.write(kwargs["content"]) f.close() except Exception as e: print(e) return "error", { "error": "Error writing {} storing/download file to temporary path".format( kwargs["service"]) } onesuccess = True magic_type = get_magic_type(kwargs["path"]) if disable_x64 is True and kwargs["path"] and magic_type and ( "x86-64" in magic_type or "PE32+" in magic_type): if len(kwargs["request"].FILES) == 1: return "error", {"error": "Sorry no x64 support yet"} kwargs["options"], timeout, enforce_timeout = recon( kwargs["path"], kwargs["options"], timeout, enforce_timeout) if not kwargs.get("task_machines", []): kwargs["task_machines"] = [None] platform = get_platform(magic_type) if machine.lower() == "all": kwargs["task_machines"] = [ vm.name for vm in db.list_machines(platform=platform) ] elif machine: machine_details = db.view_machine(machine) if hasattr(machine_details, "platform") and not machine_details.platform == platform: return "error", { "error": "Wrong platform, {} VM selected for {} sample".format( machine_details.platform, platform) } else: kwargs["task_machines"] = [machine] else: kwargs["task_machines"] = ["first"] for machine in kwargs.get("task_machines", []): if machine == "first": machine = None # Keep this as demux_sample_and_add_to_db in DB task_ids_new, extra_details = db.demux_sample_and_add_to_db( file_path=kwargs["path"], package=package, timeout=timeout, options=kwargs["options"], priority=priority, machine=machine, custom=custom, platform=platform, tags=tags, memory=memory, enforce_timeout=enforce_timeout, clock=clock, static=static, shrike_url=shrike_url, shrike_msg=shrike_msg, shrike_sid=shrike_sid, shrike_refer=shrike_refer, tlp=tlp, tags_tasks=tags_tasks, route=route, cape=cape, user_id=kwargs.get("user_id"), #parent_id=kwargs.get("parent_id", None), #sample_parent_id=kwargs.get("sample_parent_id", None) ) if isinstance(kwargs.get("task_ids", False), list): kwargs["task_ids"].extend(task_ids_new) else: kwargs["task_ids"] = list() kwargs["task_ids"].extend(task_ids_new) if not onesuccess: return "error", { "error": "Provided hash not found on {}".format(kwargs["service"]) } return "ok", kwargs["task_ids"]
def index(request, resubmit_hash=False): if request.method == "POST": ( static, package, timeout, priority, options, machine, platform, tags, custom, memory, clock, enforce_timeout, shrike_url, shrike_msg, shrike_sid, shrike_refer, unique, referrer, tlp, tags_tasks, route, cape, ) = parse_request_arguments(request) # This is done to remove spaces in options but not breaks custom paths options = ",".join("=".join(value.strip() for value in option.split("=", 1)) for option in options.split(",") if option and "=" in option) opt_filename = get_user_filename(options, custom) if priority and web_conf.public.enabled and web_conf.public.priority and not request.user.is_staff: priority = web_conf.public.priority if timeout and web_conf.public.enabled and web_conf.public.timeout and not request.user.is_staff: timeout = web_conf.public.timeout if options: options += "," if referrer: options += "referrer=%s," % (referrer) if request.POST.get("free"): options += "free=yes," if request.POST.get("nohuman"): options += "nohuman=yes," if request.POST.get("tor"): options += "tor=yes," if request.POST.get("process_dump"): options += "procdump=0," if request.POST.get("process_memory"): options += "procmemdump=1," if request.POST.get("import_reconstruction"): options += "import_reconstruction=1," if request.POST.get("disable_cape"): options += "disable_cape=1," if request.POST.get("kernel_analysis"): options += "kernel_analysis=yes," if request.POST.get("norefer"): options += "norefer=1," if request.POST.get("oldloader"): options += "no-iat=1," if request.POST.get("unpack"): options += "unpack=yes," # amsidump is enabled by default in the monitor for Win10+ if web_conf.amsidump.enabled and not request.POST.get("amsidump"): options += "amsidump=0," options = options[:-1] opt_apikey = False opts = get_options(options) if opts: opt_apikey = opts.get("apikey", False) status = "ok" task_ids_tmp = [] existent_tasks = {} details = { "errors": [], "content": False, "request": request, "task_ids": [], "url": False, "params": {}, "headers": {}, "service": "Local", "path": "", "fhash": False, "options": options, "only_extraction": False, "user_id": request.user.id or 0, } if "hash" in request.POST and request.POST.get( "hash", False) and request.POST.get("hash")[0] != "": resubmission_hash = request.POST.get("hash").strip() paths = db.sample_path_by_hash(resubmission_hash) if paths: content = get_file_content(paths) if not content: return render( request, "error.html", { "error": "Can't find {} on disk, {}".format( resubmission_hash, str(paths)) }) folder = os.path.join(settings.TEMP_PATH, "cape-resubmit") if not os.path.exists(folder): os.makedirs(folder) base_dir = tempfile.mkdtemp(prefix="resubmit_", dir=folder) if opt_filename: filename = base_dir + "/" + opt_filename else: filename = base_dir + "/" + sanitize_filename( resubmission_hash) path = store_temp_file(content, filename) details["path"] = path details["content"] = content status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append( {os.path.basename(filename): task_ids_tmp}) else: details["task_ids"] = task_ids_tmp if web_conf.general.get("existent_tasks", False): records = perform_search("target_sha256", resubmission_hash, search_limit=5) for record in records: existent_tasks.setdefault( record["target"]["file"]["sha256"], []).append(record) else: return render( request, "error.html", {"error": "File not found on hdd for resubmission"}) elif "sample" in request.FILES: samples = request.FILES.getlist("sample") details["service"] = "WebGUI" for sample in samples: # Error if there was only one submitted sample and it's empty. # But if there are multiple and one was empty, just ignore it. if not sample.size: details["errors"].append( {sample.name: "You uploaded an empty file."}) continue elif sample.size > settings.MAX_UPLOAD_SIZE: details["errors"].append({ sample.name: "You uploaded a file that exceeds the maximum allowed upload size specified in conf/web.conf." }) continue if opt_filename: filename = opt_filename else: filename = sanitize_filename(sample.name) # Moving sample from django temporary file to CAPE temporary storage to let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), filename) sha256 = File(path).get_sha256() if (not request.user.is_staff and (web_conf.uniq_submission.enabled or unique) and db.check_file_uniq( sha256, hours=web_conf.uniq_submission.hours)): details["errors"].append({ filename: "Duplicated file, disable unique option on submit or in conf/web.conf to force submission" }) continue if timeout and web_conf.public.enabled and web_conf.public.timeout and timeout > web_conf.public.timeout: timeout = web_conf.public.timeout details["path"] = path details["content"] = get_file_content(path) status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append( {os.path.basename(path): task_ids_tmp}) else: if web_conf.general.get("existent_tasks", False): records = perform_search("target_sha256", sha256, search_limit=5) for record in records: if record.get("target").get("file", {}).get("sha256"): existent_tasks.setdefault( record["target"]["file"]["sha256"], []).append(record) details["task_ids"] = task_ids_tmp elif "quarantine" in request.FILES: samples = request.FILES.getlist("quarantine") for sample in samples: # Error if there was only one submitted sample and it's empty. # But if there are multiple and one was empty, just ignore it. if not sample.size: if len(samples) != 1: continue return render( request, "error.html", {"error": "You uploaded an empty quarantine file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a quarantine file that exceeds the maximum allowed upload size specified in conf/web.conf." }, ) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). tmp_path = store_temp_file(sample.read(), sample.name) path = unquarantine(tmp_path) try: os.remove(tmp_path) except Exception as e: print(e) if not path: return render(request, "error.html", { "error": "You uploaded an unsupported quarantine file." }) details["path"] = path details["content"] = get_file_content(path) status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append({sample.name: task_ids_tmp}) else: details["task_ids"] = task_ids_tmp elif "static" in request.FILES: samples = request.FILES.getlist("static") for sample in samples: if not sample.size: if len(samples) != 1: continue return render(request, "error.html", {"error": "You uploaded an empty file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a file that exceeds the maximum allowed upload size specified in conf/web.conf." }, ) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), sample.name) task_id = db.add_static(file_path=path, priority=priority, tlp=tlp, user_id=request.user.id or 0) if not task_id: return render( request, "error.html", {"error": "We don't have static extractor for this"}) details["task_ids"] += task_id elif "pcap" in request.FILES: samples = request.FILES.getlist("pcap") for sample in samples: if not sample.size: if len(samples) != 1: continue return render( request, "error.html", {"error": "You uploaded an empty PCAP file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a PCAP file that exceeds the maximum allowed upload size specified in conf/web.conf." }, ) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), sample.name) if sample.name.lower().endswith(".saz"): saz = saz_to_pcap(path) if saz: try: os.remove(path) except Exception as e: pass path = saz else: return render( request, "error.html", {"error": "Conversion from SAZ to PCAP failed."}) task_id = db.add_pcap(file_path=path, priority=priority, tlp=tlp, user_id=request.user.id or 0) if task_id: details["task_ids"].append(task_id) elif "url" in request.POST and request.POST.get("url").strip(): url = request.POST.get("url").strip() if not url: return render(request, "error.html", {"error": "You specified an invalid URL!"}) url = url.replace("hxxps://", "https://").replace( "hxxp://", "http://").replace("[.]", ".") if machine.lower() == "all": machines = [ vm.name for vm in db.list_machines(platform=platform) ] elif machine: machine_details = db.view_machine(machine) if platform and hasattr( machine_details, "platform" ) and not machine_details.platform == platform: return render( request, "error.html", { "error": "Wrong platform, {} VM selected for {} sample". format(machine_details.platform, platform) }, ) else: machines = [machine] else: machines = [None] for entry in machines: task_id = db.add_url( url=url, package=package, timeout=timeout, priority=priority, options=options, machine=entry, platform=platform, tags=tags, custom=custom, memory=memory, enforce_timeout=enforce_timeout, clock=clock, shrike_url=shrike_url, shrike_msg=shrike_msg, shrike_sid=shrike_sid, shrike_refer=shrike_refer, route=route, cape=cape, tags_tasks=tags_tasks, user_id=request.user.id or 0, ) details["task_ids"].append(task_id) elif "dlnexec" in request.POST and request.POST.get("dlnexec").strip(): url = request.POST.get("dlnexec").strip() if not url: return render(request, "error.html", {"error": "You specified an invalid URL!"}) url = url.replace("hxxps://", "https://").replace( "hxxp://", "http://").replace("[.]", ".") response = _download_file(request.POST.get("route"), url, options) if not response: return render(request, "error.html", {"error": "Was impossible to retrieve url"}) name = os.path.basename(url) if not "." in name: name = get_user_filename(options, custom) or generate_fake_name() path = store_temp_file(response, name) details["path"] = path details["content"] = get_file_content(path) details["service"] = "DLnExec" details["source_url"] = url status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append({name: task_ids_tmp}) else: details["task_ids"] = task_ids_tmp elif (settings.VTDL_ENABLED and "vtdl" in request.POST and request.POST.get("vtdl", False) and request.POST.get("vtdl")[0] != ""): if not settings.VTDL_KEY or not settings.VTDL_PATH: return render( request, "error.html", { "error": "You specified VirusTotal but must edit the file and specify your VTDL_KEY variable and VTDL_PATH base directory" }, ) else: if opt_apikey: details["apikey"] = opt_apikey details = download_from_vt( request.POST.get("vtdl").strip(), details, opt_filename, settings) if details.get("task_ids"): tasks_count = len(details["task_ids"]) else: tasks_count = 0 if tasks_count > 0: data = { "tasks": details["task_ids"], "tasks_count": tasks_count, "errors": details["errors"], "existent_tasks": existent_tasks, } return render(request, "submission/complete.html", data) else: return render( request, "error.html", { "error": "Error adding task(s) to CAPE's database.", "errors": details["errors"] }) else: enabledconf = {} enabledconf["vt"] = settings.VTDL_ENABLED enabledconf["kernel"] = settings.OPT_ZER0M0N enabledconf["memory"] = processing.memory.get("enabled") enabledconf["procmemory"] = processing.procmemory.get("enabled") enabledconf["dlnexec"] = settings.DLNEXEC enabledconf["url_analysis"] = settings.URL_ANALYSIS enabledconf["tags"] = False enabledconf[ "dist_master_storage_only"] = repconf.distributed.master_storage_only enabledconf["linux_on_gui"] = web_conf.linux.enabled enabledconf["tlp"] = web_conf.tlp.enabled enabledconf["timeout"] = cfg.timeouts.default enabledconf["amsidump"] = web_conf.amsidump.enabled if all_vms_tags: enabledconf["tags"] = True if not enabledconf["tags"]: # load multi machinery tags: # Get enabled machinery machinery = cfg.cuckoo.get("machinery") if machinery == "multi": for mmachinery in Config(machinery).multi.get( "machinery").split(","): vms = [ x.strip() for x in getattr(Config( mmachinery), mmachinery).get("machines").split(",") ] if any([ "tags" in list(getattr(Config(mmachinery), vmtag).keys()) for vmtag in vms ]): enabledconf["tags"] = True break else: # Get VM names for machinery config elements vms = [ x.strip() for x in getattr(Config( machinery), machinery).get("machines").split(",") ] # Check each VM config element for tags if any([ "tags" in list(getattr(Config(machinery), vmtag).keys()) for vmtag in vms ]): enabledconf["tags"] = True packages, machines = get_form_data("windows") socks5s = _load_socks5_operational() socks5s_random = "" vpn_random = "" if routing.socks5.random_socks5 and socks5s: socks5s_random = random.choice(socks5s.values()).get("name", False) if routing.vpn.random_vpn: vpn_random = random.choice(list(vpns.values())).get("name", False) if socks5s: socks5s_random = random.choice(list(socks5s.values())).get( "name", False) random_route = False if vpn_random and socks5s_random: random_route = random.choice((vpn_random, socks5s_random)) elif vpn_random: random_route = vpn_random elif socks5s_random: random_route = socks5s_random existent_tasks = {} if resubmit_hash: if web_conf.general.get("existent_tasks", False): records = perform_search("target_sha256", resubmit_hash, search_limit=5) for record in records: existent_tasks.setdefault( record["target"]["file"]["sha256"], list()) existent_tasks[record["target"]["file"]["sha256"]].append( record) return render( request, "submission/index.html", { "packages": sorted(packages), "machines": machines, "vpns": list(vpns.values()), "random_route": random_route, "socks5s": list(socks5s.values()), "route": routing.routing.route, "internet": routing.routing.internet, "inetsim": routing.inetsim.enabled, "tor": routing.tor.enabled, "config": enabledconf, "resubmit": resubmit_hash, "tags": sorted(list(set(all_vms_tags))), "existent_tasks": existent_tasks, "all_exitnodes": all_nodes_exits_list, }, )
def index(request, resubmit_hash=False): if request.method == "POST": package = request.POST.get("package", "") timeout = min(force_int(request.POST.get("timeout")), 60 * 60 * 24) options = request.POST.get("options", "") lin_options = request.POST.get("lin_options", "") priority = force_int(request.POST.get("priority")) machine = request.POST.get("machine", "") clock = request.POST.get( "clock", datetime.datetime.now().strftime("%m-%d-%Y %H:%M:%S")) if not clock: clock = datetime.datetime.now().strftime("%m-%d-%Y %H:%M:%S") if "1970" in clock: clock = datetime.datetime.now().strftime("%m-%d-%Y %H:%M:%S") custom = request.POST.get("custom", "") memory = bool(request.POST.get("memory", False)) enforce_timeout = bool(request.POST.get("enforce_timeout", False)) referrer = validate_referrer(request.POST.get("referrer", None)) tags = request.POST.get("tags", None) static = bool(request.POST.get("static", False)) all_tags = load_vms_tags() if tags and not all( [tag.strip() in all_tags for tag in tags.split(",")]): return render(request, "error.html", { "error": "Check Tags help, you have introduced incorrect tag(s)" }) if lin_options: options = lin_options # This is done to remove spaces in options but not breaks custom paths options = ','.join('='.join(value.strip() for value in option.split("=", 1)) for option in options.split(",") if option and '=' in option) opt_filename = get_user_filename(options, custom) if options: options += "," if referrer: options += "referrer=%s," % (referrer) if request.POST.get("free"): options += "free=yes," if request.POST.get("nohuman"): options += "nohuman=yes," if request.POST.get("tor"): options += "tor=yes," if request.POST.get("route", None): options += "route={0},".format(request.POST.get("route", None)) if request.POST.get("process_dump"): options += "procdump=0," if request.POST.get("process_memory"): options += "procmemdump=1," if request.POST.get("import_reconstruction"): options += "import_reconstruction=1," if request.POST.get("disable_cape"): options += "disable_cape=1," if request.POST.get("kernel_analysis"): options += "kernel_analysis=yes," if request.POST.get("norefer"): options += "norefer=1," if request.POST.get("oldloader"): options += "loader=oldloader.exe,loader_64=oldloader_x64.exe," if request.POST.get("unpack"): options += "unpack=yes," options = options[:-1] unique = request.POST.get("unique", False) orig_options = options task_ids = [] task_machines = [] status = "ok" failed_hashes = list() task_ids_tmp = list() if "hash" in request.POST and request.POST.get( "hash", False) and request.POST.get("hash")[0] != '': resubmission_hash = request.POST.get("hash").strip() paths = db.sample_path_by_hash(resubmission_hash) if paths: paths = [ _f for _f in [ path if os.path.exists(path) else False for path in paths ] if _f ] if not paths and FULL_DB: tasks = results_db.analysis.find( {"dropped.sha256": resubmission_hash}, { "info.id": 1, "_id": 0 }) if tasks: for task in tasks or []: # grab task id and replace in path if needed aka distributed hack path = os.path.join(settings.CUCKOO_PATH, "storage", "analyses", str(task["info"]["id"]), "files", resubmission_hash) if os.path.exists(path): paths = [path] break if paths: content = False content = get_file_content(paths) if not content: return render( request, "error.html", { "error": "Can't find {} on disk, {}".format( resubmission_hash, str(paths)) }) base_dir = tempfile.mkdtemp(prefix='resubmit_', dir=settings.TEMP_PATH) if opt_filename: filename = base_dir + "/" + opt_filename else: filename = base_dir + "/" + sanitize_filename( resubmission_hash) path = store_temp_file(content, filename) headers = {} url = 'local' params = {} status, task_ids = download_file( False, content, request, db, task_ids, url, params, headers, "Local", path, package, timeout, options, priority, machine, clock, custom, memory, enforce_timeout, referrer, tags, orig_options, "", static) else: return render( request, "error.html", {"error": "File not found on hdd for resubmission"}) elif "sample" in request.FILES: samples = request.FILES.getlist("sample") for sample in samples: # Error if there was only one submitted sample and it's empty. # But if there are multiple and one was empty, just ignore it. if not sample.size: if len(samples) != 1: continue return render(request, "error.html", {"error": "You uploaded an empty file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a file that exceeds the maximum allowed upload size " "specified in web/web/local_settings.py." }) if opt_filename: filename = opt_filename else: filename = sanitize_filename(sample.name) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), filename) if unique and db.check_file_uniq(File(path).get_sha256()): return render( request, "error.html", { "error": "Duplicated file, disable unique option to force submission" }) magic_type = get_magic_type(path) if disable_x64 is True: if magic_type and ("x86-64" in magic_type or "PE32+" in magic_type): if len(samples) == 1: return render( request, "error.html", {"error": "Sorry no x64 support yet"}) else: continue orig_options, timeout, enforce_timeout = recon( path, orig_options, timeout, enforce_timeout) platform = get_platform(magic_type) if machine.lower() == "all": task_machines = [ vm.name for vm in db.list_machines(platform=platform) ] elif machine: machine_details = db.view_machine(machine) if hasattr(machine_details, "platform" ) and not machine_details.platform == platform: return render( request, "error.html", { "error": "Wrong platform, {} VM selected for {} sample". format(machine_details.platform, platform) }) else: task_machines = [machine] else: task_machines = ["first"] for entry in task_machines: if entry == "first": entry = None try: task_ids_new = db.demux_sample_and_add_to_db( file_path=path, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, platform=platform, enforce_timeout=enforce_timeout, tags=tags, clock=clock, static=static) task_ids.extend(task_ids_new) except CuckooDemuxError as err: return render(request, "error.html", {"error": err}) elif "quarantine" in request.FILES: samples = request.FILES.getlist("quarantine") for sample in samples: # Error if there was only one submitted sample and it's empty. # But if there are multiple and one was empty, just ignore it. if not sample.size: if len(samples) != 1: continue return render( request, "error.html", {"error": "You uploaded an empty quarantine file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a quarantine file that exceeds the maximum \ allowed upload size specified in web/web/local_settings.py." }) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). tmp_path = store_temp_file(sample.read(), sample.name) path = unquarantine(tmp_path) try: os.remove(tmp_path) except Exception as e: pass if not path: return render(request, "error.html", { "error": "You uploaded an unsupported quarantine file." }) if machine.lower() == "all": task_machines = [ vm.name for vm in db.list_machines(platform="windows") ] elif machine: machine_details = db.view_machine(machine) if not machine_details.platform == "windows": return render( request, "error.html", { "error": "Wrong platform, linux VM selected for {} sample" .format(machine_details.platform) }) else: task_machines = [machine] if not task_machines: task_machines = ["first"] for entry in task_machines: if entry == "first": entry = None task_ids_new = db.demux_sample_and_add_to_db( file_path=path, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, tags=tags, enforce_timeout=enforce_timeout, clock=clock) if task_ids_new: task_ids.extend(task_ids_new) elif "static" in request.FILES: samples = request.FILES.getlist("static") for sample in samples: if not sample.size: if len(samples) != 1: continue return render(request, "error.html", {"error": "You uploaded an empty file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a file that exceeds the maximum \ allowed upload size specified in web/web/local_settings.py." }) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), sample.name) task_id = db.add_static(file_path=path, priority=priority) if not task_id: return render( request, "error.html", {"error": "We don't have static extractor for this"}) task_ids.append(task_id) elif "pcap" in request.FILES: samples = request.FILES.getlist("pcap") for sample in samples: if not sample.size: if len(samples) != 1: continue return render( request, "error.html", {"error": "You uploaded an empty PCAP file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a PCAP file that exceeds the maximum \ allowed upload size specified in web/web/local_settings.py." }) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), sample.name) if sample.name.lower().endswith(".saz"): saz = saz_to_pcap(path) if saz: try: os.remove(path) except Exception as e: pass path = saz else: return render( request, "error.html", {"error": "Conversion from SAZ to PCAP failed."}) task_id = db.add_pcap(file_path=path, priority=priority) if task_id: task_ids.append(task_id) elif "url" in request.POST and request.POST.get("url").strip(): url = request.POST.get("url").strip() if not url: return render(request, "error.html", {"error": "You specified an invalid URL!"}) url = url.replace("hxxps://", "https://").replace( "hxxp://", "http://").replace("[.]", ".") if machine.lower() == "all": task_machines = [ vm.name for vm in db.list_machines(platform="windows") ] elif machine: machine_details = db.view_machine(machine) if not machine_details.platform == "windows": return render( request, "error.html", { "error": "Wrong platform, linux VM selected for {} sample". format(machine_details.platform) }) else: task_machines = [machine] else: task_machines = ["first"] for entry in task_machines: if entry == "first": entry = None task_ids_new = db.add_url(url=url, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, enforce_timeout=enforce_timeout, tags=tags, clock=clock) if task_ids_new: task_ids.extend(task_ids_new) elif "dlnexec" in request.POST and request.POST.get("dlnexec").strip(): url = request.POST.get("dlnexec").strip() if not url: return render(request, "error.html", {"error": "You specified an invalid URL!"}) url = url.replace("hxxps://", "https://").replace( "hxxp://", "http://").replace("[.]", ".") response = _download_file(request.POST.get("route", None), url, options) if not response: return render(request, "error.html", {"error": "Was impossible to retrieve url"}) name = os.path.basename(url) if not "." in name: name = get_user_filename(options, custom) or generate_fake_name() path = store_temp_file(response, name) magic_type = get_magic_type(path) platform = get_platform(magic_type) if machine.lower() == "all": task_machines = [ vm.name for vm in db.list_machines(platform=platform) ] elif machine: machine_details = db.view_machine(machine[0]) if not machine_details.platform == platform: return render( request, "error.html", { "error": "Wrong platform, {} VM selected for {} sample". format(machine_details.platform, platform) }) else: task_machines = [machine] else: task_machines = ["first"] for entry in task_machines: if entry == "first": entry = None task_ids_new = db.demux_sample_and_add_to_db( file_path=path, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, enforce_timeout=enforce_timeout, tags=tags, platform=platform, clock=clock) if task_ids_new: task_ids.extend(task_ids_new) elif settings.VTDL_ENABLED and "vtdl" in request.POST and request.POST.get("vtdl", False) \ and request.POST.get("vtdl")[0] != '': vtdl = request.POST.get("vtdl").strip() if (not settings.VTDL_PRIV_KEY and not settings.VTDL_INTEL_KEY) or not settings.VTDL_PATH: return render( request, "error.html", { "error": "You specified VirusTotal but must edit the file and specify your " "VTDL_PRIV_KEY or VTDL_INTEL_KEY variable and VTDL_PATH base directory" }) else: hashlist = [] if "," in vtdl: hashlist = [ _f for _f in vtdl.replace(" ", "").strip().split(",") if _f ] else: hashlist.append(vtdl) for h in hashlist: base_dir = tempfile.mkdtemp(prefix='cuckoovtdl', dir=settings.VTDL_PATH) task_ids_tmp = list() if opt_filename: filename = base_dir + "/" + opt_filename else: filename = base_dir + "/" + sanitize_filename(h) headers = {} paths = db.sample_path_by_hash(h) content = False if paths: content = get_file_content(paths) if settings.VTDL_PRIV_KEY: headers = {'x-apikey': settings.VTDL_PRIV_KEY} elif settings.VTDL_INTEL_KEY: headers = {'x-apikey': settings.VTDL_INTEL_KEY} url = "https://www.virustotal.com/api/v3/files/{id}/download".format( id=h) params = {} if not content: status, task_ids_tmp = download_file( False, content, request, db, task_ids, url, params, headers, "VirusTotal", filename, package, timeout, options, priority, machine, clock, custom, memory, enforce_timeout, referrer, tags, orig_options, "", static, h) else: status, task_ids_tmp = download_file( False, content, request, db, task_ids, url, params, headers, "Local", filename, package, timeout, options, priority, machine, clock, custom, memory, enforce_timeout, referrer, tags, orig_options, "", static, h) if status is "ok": task_ids = task_ids_tmp else: failed_hashes.append(h) if not isinstance(task_ids, list) and status == "error": # is render msg return task_ids if not isinstance(task_ids_tmp, list) and status == "error": # is render msg return task_ids_tmp if isinstance(task_ids, list): tasks_count = len(task_ids) else: # ToDo improve error msg tasks_count = 0 tasks_count = len(task_ids) if tasks_count > 0: data = {"tasks": task_ids, "tasks_count": tasks_count} if failed_hashes: data["failed_hashes"] = failed_hashes return render(request, "submission/complete.html", data) else: return render(request, "error.html", {"error": "Error adding task to Cuckoo's database."}) else: enabledconf = dict() enabledconf["vt"] = settings.VTDL_ENABLED enabledconf["kernel"] = settings.OPT_ZER0M0N enabledconf["memory"] = processing.memory.get("enabled") enabledconf["procmemory"] = processing.procmemory.get("enabled") enabledconf["dlnexec"] = settings.DLNEXEC enabledconf["url_analysis"] = settings.URL_ANALYSIS enabledconf["tags"] = False enabledconf[ "dist_master_storage_only"] = repconf.distributed.master_storage_only enabledconf["linux_on_gui"] = web_conf.linux.enabled all_tags = load_vms_tags() if all_tags: enabledconf["tags"] = True if not enabledconf["tags"]: #Â load multi machinery tags: # Get enabled machinery machinery = cfg.cuckoo.get("machinery") if machinery == "multi": for mmachinery in Config(machinery).multi.get( "machinery").split(","): vms = [ x.strip() for x in getattr(Config( mmachinery), mmachinery).get("machines").split(",") ] if any([ "tags" in list(getattr(Config(mmachinery), vmtag).keys()) for vmtag in vms ]): enabledconf["tags"] = True break else: # Get VM names for machinery config elements vms = [ x.strip() for x in getattr(Config( machinery), machinery).get("machines").split(",") ] # Check each VM config element for tags if any([ "tags" in list(getattr(Config(machinery), vmtag).keys()) for vmtag in vms ]): enabledconf["tags"] = True packages, machines = get_form_data("windows") socks5s = _load_socks5_operational() socks5s_random = "" if socks5s: socks5s_random = random.choice(list(socks5s.values())).get( "description", False) return render( request, "submission/index.html", { "packages": sorted(packages), "machines": machines, "vpns": list(vpns.values()), "socks5s": list(socks5s.values()), "socks5s_random": socks5s_random, "route": routing.routing.route, "internet": routing.routing.internet, "inetsim": routing.inetsim.enabled, "tor": routing.tor.enabled, "config": enabledconf, "resubmit": resubmit_hash, "tags": sorted(list(set(all_tags))) })
def download_file(**kwargs): """Example of kwargs { "errors": [], "content": content, "request": request, "task_id": [], "url": False, "params": {}, "headers": {}, "service": "tasks_create_file_API", "path": tmp_path, "fhash": False, "options": options, "only_extraction": False, } """ ( static, package, timeout, priority, _, machine, platform, tags, custom, memory, clock, enforce_timeout, shrike_url, shrike_msg, shrike_sid, shrike_refer, unique, referrer, tlp, tags_tasks, route, cape, ) = parse_request_arguments(kwargs["request"]) onesuccess = False username = False """ put here your custom username assignation from your custom auth, Ex: request_url = kwargs["request"].build_absolute_uri() if "yourdomain.com/submit/" in request_url: username = kwargs["request"].COOKIES.get("X-user") """ # in case if user didn't specify routing, and we have enabled random route if not route: socks5s = _load_socks5_operational() socks5s_random = "" vpn_random = "" if routing_conf.socks5.random_socks5 and socks5s: socks5s_random = choice(socks5s.values()).get("name", False) if routing_conf.vpn.random_vpn: vpn_random = choice(list(vpns.values())).get("name", False) if vpn_random and socks5s_random: route = choice((vpn_random, socks5s_random)) elif vpn_random: route = vpn_random elif socks5s_random: route = socks5s_random if package: if package == "Emotet": return "error", {"error": "Hey guy update your script, this package doesn't exist anymore"} if package.endswith("_x64"): if tags: if "x64" not in tags: tags += ",x64" else: tags = "x64" if tags: if not all([tag.strip() in all_vms_tags for tag in tags.split(",")]): return "error", { "error": f"Check Tags help, you have introduced incorrect tag(s). Your tags: {tags} - Supported tags: {all_vms_tags_str}" } elif all([tag in tags for tag in ("x64", "x86")]): return "error", {"error": "Check Tags help, you have introduced x86 and x64 tags for the same task, choose only 1"} if not kwargs.get("content", False) and kwargs.get("url", False): try: r = requests.get(kwargs["url"], params=kwargs.get("params", {}), headers=kwargs.get("headers", {}), verify=False) except requests.exceptions.RequestException as e: logging.error(e) return "error", {"error": "Provided hash not found on {}".format(kwargs["service"])} if ( r.status_code == 200 and r.content != b"Hash Not Present" and b"The request requires higher privileges than provided by the access token" not in r.content ): kwargs["content"] = r.content elif r.status_code == 403: return "error", { "error": "API key provided is not a valid {0} key or is not authorized for {0} downloads".format(kwargs["service"]) } elif r.status_code == 404: return "error", {"error": "Server returns 404 from {}".format(kwargs["service"])} else: return "error", {"error": "Was impossible to download from {0}".format(kwargs["service"])} if not kwargs["content"]: return "error", {"error": "Error downloading file from {}".format(kwargs["service"])} try: if kwargs.get("fhash", False): retrieved_hash = hashes[len(kwargs["fhash"])](kwargs["content"]).hexdigest() if retrieved_hash != kwargs["fhash"].lower(): return "error", { "error": "Hashes mismatch, original hash: {} - retrieved hash: {}".format(kwargs["fhash"], retrieved_hash) } if not os.path.exists(kwargs.get("path")): f = open(kwargs["path"], "wb") f.write(kwargs["content"]) f.close() except Exception as e: print(e) return "error", {"error": "Error writing {} storing/download file to temporary path".format(kwargs["service"])} # Distribute task based on route support by worker if route and route not in ("none", "None") and all_nodes_exits_list: parsed_options = get_options(kwargs["options"]) node = parsed_options.get("node") if node and route not in all_nodes_exits.get(node): return "error", {"error": f"Specified worker {node} doesn't support this route: {route}"} elif route not in all_nodes_exits_list: return "error", {"error": "Specified route doesn't exist on any worker"} if not node: # get nodes that supports this exit tmp_workers = list() for node, exitnodes in all_nodes_exits.items(): if route in exitnodes: tmp_workers.append(node) if tmp_workers: if kwargs["options"]: kwargs["options"] += ",node=" + choice(tmp_workers) else: kwargs["options"] = "node=" + choice(tmp_workers) # Remove workers prefixes if route.startswith(("socks5:", "vpn:")): route = route.replace("socks5:", "", 1).replace("vpn:", "", 1) onesuccess = True magic_type = get_magic_type(kwargs["path"]) if disable_x64 is True and kwargs["path"] and magic_type and ("x86-64" in magic_type or "PE32+" in magic_type): if len(kwargs["request"].FILES) == 1: return "error", {"error": "Sorry no x64 support yet"} kwargs["options"], timeout, enforce_timeout = recon(kwargs["path"], kwargs["options"], timeout, enforce_timeout) if not kwargs.get("task_machines", []): kwargs["task_machines"] = [None] platform = get_platform(magic_type) if platform == "linux" and not linux_enabled: return "error", {"error": "Linux binaries analysis isn't enabled"} if machine.lower() == "all": kwargs["task_machines"] = [vm.name for vm in db.list_machines(platform=platform)] elif machine: machine_details = db.view_machine(machine) if hasattr(machine_details, "platform") and not machine_details.platform == platform: return "error", {"error": "Wrong platform, {} VM selected for {} sample".format(machine_details.platform, platform)} else: kwargs["task_machines"] = [machine] else: kwargs["task_machines"] = ["first"] # Try to extract before submit to VM if not static and "dist_extract" in kwargs["options"]: static = True for machine in kwargs.get("task_machines", []): if machine == "first": machine = None # Keep this as demux_sample_and_add_to_db in DB task_ids_new, extra_details = db.demux_sample_and_add_to_db( file_path=kwargs["path"], package=package, timeout=timeout, options=kwargs["options"], priority=priority, machine=machine, custom=custom, platform=platform, tags=tags, memory=memory, enforce_timeout=enforce_timeout, clock=clock, static=static, shrike_url=shrike_url, shrike_msg=shrike_msg, shrike_sid=shrike_sid, shrike_refer=shrike_refer, tlp=tlp, tags_tasks=tags_tasks, route=route, cape=cape, user_id=kwargs.get("user_id"), username=username, source_url=kwargs.get("source_url", False) # parent_id=kwargs.get("parent_id", None), # sample_parent_id=kwargs.get("sample_parent_id", None) ) if isinstance(kwargs.get("task_ids", False), list): kwargs["task_ids"].extend(task_ids_new) else: kwargs["task_ids"] = list() kwargs["task_ids"].extend(task_ids_new) if not onesuccess: return "error", {"error": "Provided hash not found on {}".format(kwargs["service"])} return "ok", kwargs["task_ids"]
def index(request, resubmit_hash=False): remote_console = False if request.method == "POST": ( static, package, timeout, priority, options, machine, platform, tags, custom, memory, clock, enforce_timeout, shrike_url, shrike_msg, shrike_sid, shrike_refer, unique, referrer, tlp, tags_tasks, route, cape, ) = parse_request_arguments(request) # This is done to remove spaces in options but not breaks custom paths options = ",".join("=".join(value.strip() for value in option.split("=", 1)) for option in options.split(",") if option and "=" in option) opt_filename = get_user_filename(options, custom) if priority and web_conf.public.enabled and web_conf.public.priority and not request.user.is_staff: priority = web_conf.public.priority if timeout and web_conf.public.enabled and web_conf.public.timeout and not request.user.is_staff: timeout = web_conf.public.timeout if options: options += "," if referrer: options += "referrer=%s," % (referrer) if request.POST.get("free"): options += "free=yes," if request.POST.get("nohuman"): options += "nohuman=yes," if web_conf.guacamole.enabled and request.POST.get( "interactive_desktop"): remote_console = True if "nohuman=yes," not in options: options += "nohuman=yes," if request.POST.get("tor"): options += "tor=yes," if request.POST.get("process_dump"): options += "procdump=0," if request.POST.get("process_memory"): options += "procmemdump=1," if request.POST.get("import_reconstruction"): options += "import_reconstruction=1," if request.POST.get("disable_cape"): options += "disable_cape=1," if request.POST.get("kernel_analysis"): options += "kernel_analysis=yes," if request.POST.get("norefer"): options += "norefer=1," if request.POST.get("oldloader"): options += "no-iat=1," if request.POST.get("unpack"): options += "unpack=yes," job_category = False if request.POST.get("job_category"): job_category = request.POST.get("job_category") # amsidump is enabled by default in the monitor for Win10+ if web_conf.amsidump.enabled and not request.POST.get("amsidump"): options += "amsidump=0," options = options[:-1] opt_apikey = False opts = get_options(options) if opts: opt_apikey = opts.get("apikey", False) status = "ok" task_ids_tmp = [] existent_tasks = {} details = { "errors": [], "content": False, "request": request, "task_ids": [], "url": False, "params": {}, "headers": {}, "service": "Local", "path": "", "fhash": False, "options": options, "only_extraction": False, "user_id": request.user.id or 0, } task_category = False samples = [] if "hash" in request.POST and request.POST.get( "hash", False) and request.POST.get("hash")[0] != "": task_category = "resubmit" samples = request.POST.get("hash").strip().split(",") elif "sample" in request.FILES: task_category = "sample" samples = request.FILES.getlist("sample") elif "quarantine" in request.FILES: task_category = "quarantine" samples = request.FILES.getlist("quarantine") elif "static" in request.FILES: task_category = "static" samples = request.FILES.getlist("static") elif "pcap" in request.FILES: task_category = "pcap" samples = request.FILES.getlist("pcap") elif "url" in request.POST and request.POST.get("url").strip(): task_category = "url" samples = request.POST.get("url").strip() elif "dlnexec" in request.POST and request.POST.get("dlnexec").strip(): task_category = "dlnexec" samples = request.POST.get("dlnexec").strip() elif (settings.VTDL_ENABLED and "vtdl" in request.POST and request.POST.get("vtdl", False) and request.POST.get("vtdl")[0] != ""): task_category = "vtdl" samples = request.POST.get("vtdl").strip() list_of_files = [] if task_category in ("url", "dlnexec"): if not samples: return render(request, "error.html", {"error": "You specified an invalid URL!"}) for url in samples.split(","): url = url.replace("hxxps://", "https://").replace( "hxxp://", "http://").replace("[.]", ".") if task_category == "dlnexec": path, content, sha256 = process_new_dlnexec_task( url, route, options, custom) if path: list_of_files.append((content, path, sha256)) elif task_category == "url": list_of_files.append(("", url, "")) elif task_category in ("sample", "quarantine", "static", "pcap"): list_of_files, details = process_new_task_files( request, samples, details, opt_filename, unique) elif task_category == "resubmit": for hash in samples: paths = [] if len(hash) in (32, 40, 64): paths = db.sample_path_by_hash(hash) else: task_binary = os.path.join(settings.CUCKOO_PATH, "storage", "analyses", str(hash), "binary") if os.path.exists(task_binary): paths.append(task_binary) else: tmp_paths = db.find_sample(task_id=int(hash)) if not tmp_paths: details["errors"].append( {hash: "Task not found for resubmission"}) continue for tmp_sample in tmp_paths: path = False tmp_dict = tmp_sample.to_dict() if os.path.exists(tmp_dict.get("target", "")): path = tmp_dict["target"] else: tmp_tasks = db.find_sample( sample_id=tmp_dict["sample_id"]) for tmp_task in tmp_tasks: tmp_path = os.path.join( settings.CUCKOO_PATH, "storage", "binaries", tmp_task.to_dict()["sha256"]) if os.path.exists(tmp_path): path = tmp_path break if path: paths.append(path) if not paths: details["errors"].append( {hash: "File not found on hdd for resubmission"}) continue content = get_file_content(paths) if not content: details["errors"].append( {hash: f"Can't find {hash} on disk"}) continue folder = os.path.join(settings.TEMP_PATH, "cape-resubmit") if not os.path.exists(folder): os.makedirs(folder) base_dir = tempfile.mkdtemp(prefix="resubmit_", dir=folder) if opt_filename: filename = base_dir + "/" + opt_filename else: filename = base_dir + "/" + sanitize_filename(hash) path = store_temp_file(content, filename) list_of_files.append((content, path, hash)) # Hack for resubmit first find all files and then put task as proper category if job_category and job_category in ("resubmit", "sample", "quarantine", "static", "pcap", "dlnexec", "vtdl"): task_category = job_category if task_category == "resubmit": for content, path, sha256 in list_of_files: details["path"] = path details["content"] = content status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append( {os.path.basename(filename): task_ids_tmp}) else: details["task_ids"] = task_ids_tmp if web_conf.general.get("existent_tasks", False): records = perform_search("target_sha256", hash, search_limit=5) for record in records or []: existent_tasks.setdefault( record["target"]["file"]["sha256"], []).append(record) elif task_category == "sample": details["service"] = "WebGUI" for content, path, sha256 in list_of_files: if web_conf.pre_script.enabled and "pre_script" in request.FILES: pre_script = request.FILES["pre_script"] details["pre_script_name"] = request.FILES[ "pre_script"].name details["pre_script_content"] = pre_script.read() if web_conf.during_script.enabled and "during_script" in request.FILES: during_script = request.FILES["during_script"] details["during_script_name"] = request.FILES[ "during_script"].name details["during_script_content"] = during_script.read() if timeout and web_conf.public.enabled and web_conf.public.timeout and timeout > web_conf.public.timeout: timeout = web_conf.public.timeout details["path"] = path details["content"] = content status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append( {os.path.basename(path): task_ids_tmp}) else: if web_conf.general.get("existent_tasks", False): records = perform_search("target_sha256", sha256, search_limit=5) for record in records: if record.get("target").get("file", {}).get("sha256"): existent_tasks.setdefault( record["target"]["file"]["sha256"], []).append(record) details["task_ids"] = task_ids_tmp elif task_category == "quarantine": for content, tmp_path, sha256 in list_of_files: path = unquarantine(tmp_path) try: os.remove(tmp_path) except Exception as e: print(e) if not path: details["errors"].append({ os.path.basename(path): "You uploaded an unsupported quarantine file." }) continue details["path"] = path details["content"] = content status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append( {os.path.basename(path): task_ids_tmp}) else: details["task_ids"] = task_ids_tmp elif task_category == "static": for content, path, sha256 in list_of_files: task_id = db.add_static(file_path=path, priority=priority, tlp=tlp, user_id=request.user.id or 0) if not task_id: return render( request, "error.html", {"error": "We don't have static extractor for this"}) details["task_ids"] += task_id elif task_category == "pcap": for content, path, sha256 in list_of_files: if path.lower().endswith(b".saz"): saz = saz_to_pcap(path) if saz: try: os.remove(path) except Exception as e: pass path = saz else: details["errors"].append({ os.path.basename(path): "Conversion from SAZ to PCAP failed." }) continue task_id = db.add_pcap(file_path=path, priority=priority, tlp=tlp, user_id=request.user.id or 0) if task_id: details["task_ids"].append(task_id) elif task_category == "url": for _, url, _ in list_of_files: if machine.lower() == "all": machines = [ vm.name for vm in db.list_machines(platform=platform) ] elif machine: machine_details = db.view_machine(machine) if platform and hasattr( machine_details, "platform" ) and not machine_details.platform == platform: details["errors"].append({ os.path.basename(url): f"Wrong platform, {machine_details.platform} VM selected for {platform} sample" }) continue else: machines = [machine] else: machines = [None] for entry in machines: task_id = db.add_url( url=url, package=package, timeout=timeout, priority=priority, options=options, machine=entry, platform=platform, tags=tags, custom=custom, memory=memory, enforce_timeout=enforce_timeout, clock=clock, shrike_url=shrike_url, shrike_msg=shrike_msg, shrike_sid=shrike_sid, shrike_refer=shrike_refer, route=route, cape=cape, tags_tasks=tags_tasks, user_id=request.user.id or 0, ) details["task_ids"].append(task_id) elif task_category == "dlnexec": for content, path, sha256 in list_of_files: details["path"] = path details["content"] = content details["service"] = "DLnExec" details["source_url"] = samples status, task_ids_tmp = download_file(**details) if status == "error": details["errors"].append( {os.path.basename(path): task_ids_tmp}) else: details["task_ids"] = task_ids_tmp elif task_category == "vtdl": if not settings.VTDL_KEY or not settings.VTDL_PATH: return render( request, "error.html", { "error": "You specified VirusTotal but must edit the file and specify your VTDL_KEY variable and VTDL_PATH base directory" }, ) else: if opt_apikey: details["apikey"] = opt_apikey details = download_from_vt(samples, details, opt_filename, settings) if details.get("task_ids"): tasks_count = len(details["task_ids"]) else: tasks_count = 0 if tasks_count > 0: data = { "tasks": details["task_ids"], "tasks_count": tasks_count, "errors": details["errors"], "existent_tasks": existent_tasks, "remote_console": remote_console, } return render(request, "submission/complete.html", data) else: return render( request, "error.html", { "error": "Error adding task(s) to CAPE's database.", "errors": details["errors"] }) else: enabledconf = {} enabledconf["vt"] = settings.VTDL_ENABLED enabledconf["kernel"] = settings.OPT_ZER0M0N enabledconf["memory"] = processing.memory.get("enabled") enabledconf["procmemory"] = processing.procmemory.get("enabled") enabledconf["dlnexec"] = settings.DLNEXEC enabledconf["url_analysis"] = settings.URL_ANALYSIS enabledconf["tags"] = False enabledconf[ "dist_master_storage_only"] = repconf.distributed.master_storage_only enabledconf["linux_on_gui"] = web_conf.linux.enabled enabledconf["tlp"] = web_conf.tlp.enabled enabledconf["timeout"] = cfg.timeouts.default enabledconf["amsidump"] = web_conf.amsidump.enabled enabledconf["pre_script"] = web_conf.pre_script.enabled enabledconf["during_script"] = web_conf.during_script.enabled if all_vms_tags: enabledconf["tags"] = True if not enabledconf["tags"]: # load multi machinery tags: # Get enabled machinery machinery = cfg.cuckoo.get("machinery") if machinery == "multi": for mmachinery in Config(machinery).multi.get( "machinery").split(","): vms = [ x.strip() for x in getattr(Config( mmachinery), mmachinery).get("machines").split(",") if x.strip() ] if any([ "tags" in list(getattr(Config(mmachinery), vmtag).keys()) for vmtag in vms ]): enabledconf["tags"] = True break else: # Get VM names for machinery config elements vms = [ x.strip() for x in str( getattr(Config(machinery), machinery).get( "machines")).split(",") if x.strip() ] # Check each VM config element for tags if any([ "tags" in list(getattr(Config(machinery), vmtag).keys()) for vmtag in vms ]): enabledconf["tags"] = True packages, machines = get_form_data("windows") socks5s = _load_socks5_operational() socks5s_random = "" vpn_random = "" if routing.socks5.random_socks5 and socks5s: socks5s_random = random.choice(socks5s.values()).get("name", False) if routing.vpn.random_vpn: vpn_random = random.choice(list(vpns.values())).get("name", False) if socks5s: socks5s_random = random.choice(list(socks5s.values())).get( "name", False) random_route = False if vpn_random and socks5s_random: random_route = random.choice((vpn_random, socks5s_random)) elif vpn_random: random_route = vpn_random elif socks5s_random: random_route = socks5s_random existent_tasks = {} if resubmit_hash: if web_conf.general.get("existent_tasks", False): records = perform_search("target_sha256", resubmit_hash, search_limit=5) for record in records: existent_tasks.setdefault( record["target"]["file"]["sha256"], list()) existent_tasks[record["target"]["file"]["sha256"]].append( record) return render( request, "submission/index.html", { "packages": sorted(packages), "machines": machines, "vpns": list(vpns.values()), "random_route": random_route, "socks5s": list(socks5s.values()), "route": routing.routing.route, "internet": routing.routing.internet, "inetsim": routing.inetsim.enabled, "tor": routing.tor.enabled, "config": enabledconf, "resubmit": resubmit_hash, "tags": sorted(list(set(all_vms_tags))), "existent_tasks": existent_tasks, "all_exitnodes": all_nodes_exits_list, }, )
def index(request, resubmit_hash=False): if request.method == "POST": package = request.POST.get("package", "") timeout = min(force_int(request.POST.get("timeout")), 60 * 60 * 24) options = request.POST.get("options", "") priority = force_int(request.POST.get("priority")) machine = request.POST.get("machine", "") gateway = request.POST.get("gateway", None) clock = request.POST.get("clock", None) custom = request.POST.get("custom", "") memory = bool(request.POST.get("memory", False)) enforce_timeout = bool(request.POST.get("enforce_timeout", False)) referrer = validate_referrer(request.POST.get("referrer", None)) tags = request.POST.get("tags", None) static = bool(request.POST.get("static", False)) opt_filename = "" for option in options.split(","): if option.startswith("filename="): opt_filename = option.split("filename=")[1] break task_gateways = [] ipaddy_re = re.compile( r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" ) if referrer: if options: options += "," options += "referrer=%s" % (referrer) if request.POST.get("free"): if options: options += "," options += "free=yes" if request.POST.get("nohuman"): if options: options += "," options += "nohuman=yes" if request.POST.get("tor"): if options: options += "," options += "tor=yes" if request.POST.get("route", None): if options: options += "," options += "route={0}".format(request.POST.get("route", None)) if request.POST.get("process_dump"): if options: options += "," options += "procdump=0" if request.POST.get("process_memory"): if options: options += "," options += "procmemdump=1" if request.POST.get("import_reconstruction"): if options: options += "," options += "import_reconstruction=1" if request.POST.get("disable_cape"): if options: options += "," options += "disable_cape=1" if request.POST.get("kernel_analysis"): if options: options += "," options += "kernel_analysis=yes" if request.POST.get("norefer"): if options: options += "," options += "norefer=1" if request.POST.get("oldloader"): if options: options += "," options += "loader=oldloader.exe,loader_64=oldloader_x64.exe" orig_options = options if gateway and gateway.lower() == "all": for e in settings.GATEWAYS: if ipaddy_re.match(settings.GATEWAYS[e]): task_gateways.append(settings.GATEWAYS[e]) elif gateway and gateway in settings.GATEWAYS: if "," in settings.GATEWAYS[gateway]: if request.POST.get("all_gw_in_group"): tgateway = settings.GATEWAYS[gateway].split(",") for e in tgateway: task_gateways.append(settings.GATEWAYS[e]) else: tgateway = random.choice( settings.GATEWAYS[gateway].split(",")) task_gateways.append(settings.GATEWAYS[tgateway]) else: task_gateways.append(settings.GATEWAYS[gateway]) if not task_gateways: # To reduce to the default case task_gateways = [None] all_tags = load_vms_tags() if tags and not all( [tag.strip() in all_tags for tag in tags.split(",")]): return render(request, "error.html", { "error": "Check Tags help, you have introduced incorrect tag(s)" }) db = Database() task_ids = [] task_machines = [] if machine.lower() == "all": for entry in db.list_machines(): task_machines.append(entry.label) else: task_machines.append(machine) failed_hashes = list() status = "ok" if "hash" in request.POST and request.POST.get( "hash", False) and request.POST.get("hash")[0] != '': resubmission_hash = request.POST.get("hash").strip() paths = db.sample_path_by_hash(resubmission_hash) if paths is not None and paths: paths = filter(None, [ path if os.path.exists(path) else False for path in paths ]) if not paths and FULL_DB: tasks = results_db.analysis.find( {"dropped.sha256": resubmission_hash}) if tasks: for task in tasks: # grab task id and replace in path aka distributed cuckoo hack path = os.path.join(settings.CUCKOO_PATH, "storage", "analyses", str(task["info"]["id"]), "files", resubmission_hash) if os.path.exists(path): paths = [path] break if paths is not None and paths: content = "" content = get_file_content(paths) if content is False: return render(request, "error.html", { "error": "Can't find {} on disk".format(resubmission_hash) }) base_dir = tempfile.mkdtemp(prefix='resubmit_', dir=settings.TEMP_PATH) if opt_filename: filename = base_dir + "/" + opt_filename else: filename = base_dir + "/" + resubmission_hash path = store_temp_file(content, filename) headers = {} url = 'local' params = {} status, task_ids = download_file( False, content, request, db, task_ids, url, params, headers, "Local", path, package, timeout, options, priority, machine, gateway, clock, custom, memory, enforce_timeout, referrer, tags, orig_options, task_gateways, task_machines, static) if status != "ok": failed_hashes.append(h) elif "sample" in request.FILES: samples = request.FILES.getlist("sample") for sample in samples: # Error if there was only one submitted sample and it's empty. # But if there are multiple and one was empty, just ignore it. if not sample.size: if len(samples) != 1: continue return render(request, "error.html", {"error": "You uploaded an empty file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a file that exceeds the maximum allowed upload size specified in web/web/local_settings.py." }) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), sample.name) for gw in task_gateways: options = update_options(gw, orig_options) for entry in task_machines: try: task_ids_new = db.demux_sample_and_add_to_db( file_path=path, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, enforce_timeout=enforce_timeout, tags=tags, clock=clock, static=static) task_ids.extend(task_ids_new) except CuckooDemuxError as err: return render(request, "error.html", {"error": err}) elif "static" in request.FILES: samples = request.FILES.getlist("static") for sample in samples: if not sample.size: if len(samples) != 1: continue return render(request, "error.html", {"error": "You uploaded an empty file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a file that exceeds the maximum allowed upload size specified in web/web/local_settings.py." }) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), sample.name) task_id = db.add_static(file_path=path, priority=priority) if not task_id: return render( request, "error.html", {"error": "We don't have static extractor for this"}) task_ids.append(task_id) elif "quarantine" in request.FILES: samples = request.FILES.getlist("quarantine") for sample in samples: # Error if there was only one submitted sample and it's empty. # But if there are multiple and one was empty, just ignore it. if not sample.size: if len(samples) != 1: continue return render( request, "error.html", {"error": "You uploaded an empty quarantine file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a quarantine file that exceeds the maximum allowed upload size specified in web/web/local_settings.py." }) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). tmp_path = store_temp_file(sample.read(), sample.name) path = unquarantine(tmp_path) try: os.remove(tmp_path) except: pass if not path: return render(request, "error.html", { "error": "You uploaded an unsupported quarantine file." }) for gw in task_gateways: options = update_options(gw, orig_options) for entry in task_machines: task_ids_new = db.demux_sample_and_add_to_db( file_path=path, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, enforce_timeout=enforce_timeout, tags=tags, clock=clock, static=static) task_ids.extend(task_ids_new) elif "pcap" in request.FILES: samples = request.FILES.getlist("pcap") for sample in samples: if not sample.size: if len(samples) != 1: continue return render( request, "error.html", {"error": "You uploaded an empty PCAP file."}) elif sample.size > settings.MAX_UPLOAD_SIZE: return render( request, "error.html", { "error": "You uploaded a PCAP file that exceeds the maximum allowed upload size specified in web/web/local_settings.py." }) # Moving sample from django temporary file to Cuckoo temporary storage to # let it persist between reboot (if user like to configure it in that way). path = store_temp_file(sample.read(), sample.name) if sample.name.lower().endswith(".saz"): saz = saz_to_pcap(path) if saz: try: os.remove(path) except: pass path = saz else: return render( request, "error.html", {"error": "Conversion from SAZ to PCAP failed."}) task_id = db.add_pcap(file_path=path, priority=priority) task_ids.append(task_id) elif "url" in request.POST and request.POST.get("url").strip(): url = request.POST.get("url").strip() if not url: return render(request, "error.html", {"error": "You specified an invalid URL!"}) url = url.replace("hxxps://", "https://").replace( "hxxp://", "http://").replace("[.]", ".") for gw in task_gateways: options = update_options(gw, orig_options) for entry in task_machines: task_id = db.add_url(url=url, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, enforce_timeout=enforce_timeout, tags=tags, clock=clock) if task_id: task_ids.append(task_id) elif "dlnexec" in request.POST and request.POST.get("dlnexec").strip(): url = request.POST.get("dlnexec").strip() if not url: return render(request, "error.html", {"error": "You specified an invalid URL!"}) url = url.replace("hxxps://", "https://").replace( "hxxp://", "http://").replace("[.]", ".") response = _download_file(request.POST.get("route", None), url, options) if not response: return render(request, "error.html", {"error": "Was impossible to retrieve url"}) name = os.path.basename(url) if not "." in name: name = get_user_filename(options, custom) or generate_fake_name() path = store_temp_file(response, name) for entry in task_machines: task_id = db.demux_sample_and_add_to_db( file_path=path, package=package, timeout=timeout, options=options, priority=priority, machine=entry, custom=custom, memory=memory, enforce_timeout=enforce_timeout, tags=tags, clock=clock) if task_id: task_ids += task_id elif settings.VTDL_ENABLED and "vtdl" in request.POST and request.POST.get( "vtdl", False) and request.POST.get("vtdl")[0] != '': vtdl = request.POST.get("vtdl") if (not settings.VTDL_PRIV_KEY and not settings.VTDL_INTEL_KEY) or not settings.VTDL_PATH: return render( request, "error.html", { "error": "You specified VirusTotal but must edit the file and specify your VTDL_PRIV_KEY or VTDL_INTEL_KEY variable and VTDL_PATH base directory" }) else: base_dir = tempfile.mkdtemp(prefix='cuckoovtdl', dir=settings.VTDL_PATH) hashlist = [] params = {} if "," in vtdl: hashlist = vtdl.replace(" ", "").strip().split(",") else: hashlist = vtdl.split() for h in hashlist: if opt_filename: filename = base_dir + "/" + opt_filename else: filename = base_dir + "/" + h paths = db.sample_path_by_hash(h) content = "" if paths is not None: content = get_file_content(paths) headers = {} params = {} url = "https://www.virustotal.com/api/v3/files/{id}/download".format( id=h) if settings.VTDL_PRIV_KEY: headers = {'x-apikey': settings.VTDL_PRIV_KEY} elif settings.VTDL_INTEL_KEY: headers = {'x-apikey': settings.VTDL_INTEL_KEY} if not content: status, task_ids = download_file( False, content, request, db, task_ids, url, params, headers, "VirusTotal", filename, package, timeout, options, priority, machine, gateway, clock, custom, memory, enforce_timeout, referrer, tags, orig_options, task_gateways, task_machines, static) else: status, task_ids = download_file( False, content, request, db, task_ids, url, params, headers, "Local", filename, package, timeout, options, priority, machine, gateway, clock, custom, memory, enforce_timeout, referrer, tags, orig_options, task_gateways, task_machines, static) if status != "ok": failed_hashes.append(h) if status == "error": # is render msg return task_ids if isinstance(task_ids, list): tasks_count = len(task_ids) else: # ToDo improve error msg tasks_count = 0 if tasks_count > 0: data = {"tasks": task_ids, "tasks_count": tasks_count} if failed_hashes: data["failed_hashes"] = failed_hashes return render(request, "submission/complete.html", data) else: return render(request, "error.html", {"error": "Error adding task to Cuckoo's database."}) else: enabledconf = dict() enabledconf["vt"] = settings.VTDL_ENABLED enabledconf["kernel"] = settings.OPT_ZER0M0N enabledconf["memory"] = processing_cfg.memory.get("enabled") enabledconf["procmemory"] = processing_cfg.procmemory.get("enabled") if aux_conf.gateways: enabledconf["gateways"] = True else: enabledconf["gateways"] = False enabledconf["tags"] = False enabledconf[ "dist_master_storage_only"] = repconf.distributed.master_storage_only enabledconf["dlnexec"] = settings.DLNEXEC all_tags = load_vms_tags() if all_tags: enabledconf["tags"] = True if not enabledconf["tags"]: # Get enabled machinery machinery = cfg.cuckoo.get("machinery") # load multi machinery tags: if machinery == "multi": for mmachinery in Config(machinery).multi.get( "machinery").split(","): vms = [ x.strip() for x in getattr(Config( mmachinery), mmachinery).get("machines").split(",") ] if any([ "tags" in getattr(Config(mmachinery), vmtag).keys() for vmtag in vms ]): enabledconf["tags"] = True break else: # Get VM names for machinery config elements vms = [ x.strip() for x in getattr(Config( machinery), machinery).get("machines").split(",") ] # Check each VM config element for tags if any([ "tags" in getattr(Config(machinery), vmtag).keys() for vmtag in vms ]): enabledconf["tags"] = True files = os.listdir( os.path.join(settings.CUCKOO_PATH, "analyzer", "windows", "modules", "packages")) packages = [] for name in files: name = os.path.splitext(name)[0] if name == "__init__": continue packages.append(name) # Prepare a list of VM names, description label based on tags. machines = [] for machine in Database().list_machines(): tags = [] for tag in machine.tags: tags.append(tag.name) if tags: all_tags += tags label = machine.label + ": " + ", ".join(tags) else: label = machine.label machines.append((machine.label, label)) # Prepend ALL/ANY options. machines.insert(0, ("", "First available")) machines.insert(1, ("all", "All")) socks5s = _load_socks5_operational() return render( request, "submission/index.html", { "packages": sorted(packages), "machines": machines, "vpns": vpns.values(), "socks5s": socks5s.values(), "route": cfg.routing.route, "internet": cfg.routing.internet, "inetsim": cfg.routing.inetsim, "tor": cfg.routing.tor, "gateways": settings.GATEWAYS, "config": enabledconf, "resubmit": resubmit_hash, "tags": sorted(list(set(all_tags))), })
def launch_analysis(self): """Start analysis.""" succeeded = False dead_machine = False self.socks5s = _load_socks5_operational() log.info( "Task #%s: Starting analysis of %s '%s'", self.task.id, self.task.category.upper(), convert_to_printable(self.task.target), ) # Initialize the analysis folders. if not self.init_storage(): log.debug("Failed to initialize the analysis folder") return False category_early_escape = self.category_checks() if isinstance(category_early_escape, bool): return category_early_escape # Acquire analysis machine. try: self.acquire_machine() self.db.set_task_vm(self.task.id, self.machine.label, self.machine.id) # At this point we can tell the ResultServer about it. except CuckooOperationalError as e: machine_lock.release() log.error("Task #%s: Cannot acquire machine: %s", self.task.id, e, exc_info=True) return False # Generate the analysis configuration file. options = self.build_options() try: ResultServer().add_task(self.task, self.machine) except Exception as e: machinery.release(self.machine.label) log.exception(e, exc_info=True) self.errors.put(e) aux = RunAuxiliary(task=self.task, machine=self.machine) try: unlocked = False # 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__) # Start the machine. machinery.start(self.machine.label) # 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 aux.start() # Initialize the guest manager. guest = GuestManager(self.machine.name, self.machine.ip, self.machine.platform, self.task.id, self) options["clock"] = self.db.update_clock(self.task.id) self.db.guest_set_status(self.task.id, "starting") # Start the analysis. guest.start_analysis(options) if self.db.guest_get_status(self.task.id) == "starting": self.db.guest_set_status(self.task.id, "running") guest.wait_for_completion() self.db.guest_set_status(self.task.id, "stopping") succeeded = True except (CuckooMachineError, CuckooNetworkError) as e: if not unlocked: machine_lock.release() log.error(str(e), extra={"task_id": self.task.id}, exc_info=True) dead_machine = True except CuckooGuestError as e: if not unlocked: machine_lock.release() log.error(str(e), extra={"task_id": self.task.id}, exc_info=True) finally: # Stop Auxiliary modules. aux.stop() # Take a memory dump of the machine before shutting it off. if self.cfg.cuckoo.memory_dump or self.task.memory: try: dump_path = get_memdump_path(self.task.id) need_space, space_available = free_space_monitor(os.path.dirname(dump_path), return_value=True) if need_space: log.error("Not enough free disk space! Could not dump ram (Only %d MB!)", space_available) else: machinery.dump_memory(self.machine.label, dump_path) except NotImplementedError: log.error("The memory dump functionality is not available for the current machine manager") except CuckooMachineError as e: log.error(e, exc_info=True) try: # Stop the analysis machine. machinery.stop(self.machine.label) except CuckooMachineError as e: log.warning("Task #%s: Unable to stop machine %s: %s", self.task.id, self.machine.label, e) # 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. self.unroute_network() if dead_machine: # Remove the guest from the database, so that we can assign a # new guest when the task is being analyzed with another # machine. self.db.guest_remove(guest_log) # Remove the analysis directory that has been created so # far, as launch_analysis() is going to be doing that again. shutil.rmtree(self.storage) # This machine has turned dead, so we throw an exception here # which informs the AnalysisManager that it should analyze # this task again with another available machine. raise CuckooDeadMachine() 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( "Task #%s: Unable to release machine %s, reason %s. You might need to restore it manually", self.task.id, self.machine.label, e, ) return succeeded