def run(self): """Run evented signatures.""" # This will contain all the matched signatures. matched = [] stats = { } complete_list = list_plugins(group="signatures") evented_list = [sig(self.results) for sig in complete_list if sig.enabled and sig.evented and self._check_signature_version(sig) and (not sig.filter_analysistypes or self.results["target"]["category"] in sig.filter_analysistypes)] overlay = self._load_overlay() log.debug("Applying signature overlays for signatures: %s", ", ".join(overlay.keys())) for signature in complete_list + evented_list: self._apply_overlay(signature, overlay) if evented_list and "behavior" in self.results: log.debug("Running %u evented signatures", len(evented_list)) for sig in evented_list: stats[sig.name] = timedelta() if sig == evented_list[-1]: log.debug("\t `-- %s", sig.name) else: log.debug("\t |-- %s", sig.name) # Iterate calls and tell interested signatures about them. for proc in self.results["behavior"]["processes"]: for call in proc["calls"]: # Loop through active evented signatures. for sig in evented_list: # Skip current call if it doesn't match the filters (if any). if sig.filter_processnames and not proc["process_name"] in sig.filter_processnames: continue if sig.filter_apinames and not call["api"] in sig.filter_apinames: continue if sig.filter_categories and not call["category"] in sig.filter_categories: continue result = None try: pretime = datetime.now() result = sig.on_call(call, proc) posttime = datetime.now() timediff = posttime - pretime stats[sig.name] += timediff except NotImplementedError: result = False except: log.exception("Failed to run signature \"%s\":", sig.name) result = False # If the signature returns None we can carry on, the # condition was not matched. if result is None: continue # On True, the signature is matched. if result is True: log.debug("Analysis matched signature \"%s\"", sig.name) matched.append(sig.as_result()) if sig in complete_list: complete_list.remove(sig) # Either True or False, we don't need to check this sig anymore. evented_list.remove(sig) del sig # Call the stop method on all remaining instances. for sig in evented_list: try: pretime = datetime.now() result = sig.on_complete() posttime = datetime.now() timediff = posttime - pretime stats[sig.name] += timediff except NotImplementedError: continue except: log.exception("Failed run on_complete() method for signature \"%s\":", sig.name) continue else: if result is True: log.debug("Analysis matched signature \"%s\"", sig.name) matched.append(sig.as_result()) if sig in complete_list: complete_list.remove(sig) # Link this into the results already at this point, so non-evented signatures can use it self.results["signatures"] = matched # Add in statistics for evented signatures that took at least some time for key, value in stats.iteritems(): if value: self.results["statistics"]["signatures"].append({ "name": key, "time": float("%d.%03d" % (value.seconds, value.microseconds / 1000)), }) # Compat loop for old-style (non evented) signatures. if complete_list: complete_list.sort(key=lambda sig: sig.order) log.debug("Running non-evented signatures") for signature in complete_list: if not signature.filter_analysistypes or self.results["target"]["category"] in signature.filter_analysistypes: match = self.process(signature) # If the signature is matched, add it to the list. if match: matched.append(match) # Reset the ParseProcessLog instances after each signature if "behavior" in self.results: for process in self.results["behavior"]["processes"]: process["calls"].reset() # Sort the matched signatures by their severity level. matched.sort(key=lambda key: key["severity"]) # Tweak later as needed malscore = 0.0 for match in matched: if match["severity"] == 1: malscore += match["weight"] * 0.5 * (match["confidence"] / 100.0) else: malscore += match["weight"] * (match["severity"] - 1) * (match["confidence"] / 100.0) if malscore > 10.0: malscore = 10.0 if malscore < 0.0: malscore = 0.0 self.results["malscore"] = malscore family = "" # Make a best effort detection of malware family name (can be updated later by re-processing the analysis) for match in matched: if "families" in match and match["families"]: family = match["families"][0].title() break if not family and self.results["info"]["category"] == "file" and "virustotal" in self.results and "results" in self.results["virustotal"] and self.results["virustotal"]["results"]: detectnames = [] for res in self.results["virustotal"]["results"]: if res["sig"]: # weight Microsoft's detection, they seem to be more accurate than the rest if res["vendor"] == "Microsoft": detectnames.append(res["sig"]) detectnames.append(res["sig"]) family = get_vt_consensus(detectnames) # add detection based on suricata here if not family and "suricata" in self.results and "alerts" in self.results["suricata"] and self.results["suricata"]["alerts"]: for alert in self.results["suricata"]["alerts"]: if "signature" in alert and alert["signature"]: if alert["signature"].startswith("ET TROJAN") or alert["signature"].startswith("ETPRO TROJAN"): words = re.findall(r"[A-Za-z0-9]+", alert["signature"]) famcheck = words[2] famchecklower = famcheck.lower() if famchecklower == "win32" or famchecklower == "w32": famcheck = words[3] famchecklower = famcheck.lower() blacklist = [ "upx", "executable", "potential", "likely", "rogue", "supicious", "generic", "possible", "known", "common", "troj", "trojan", "team", "probably", "w2km", "http", "abuse.ch", "win32", "unknown", "single", "exe", "filename", "js", ] isgood = True for black in blacklist: if black == famchecklower: isgood = False break if isgood: famcheck = famcheck.split(".")[0] family = famcheck.title() # fall back to ClamAV detection if not family and self.results["info"]["category"] == "file" and "clamav" in self.results["target"]["file"] and self.results["target"]["file"]["clamav"] and self.results["target"]["file"]["clamav"].startswith("Win.Trojan."): words = re.findall(r"[A-Za-z0-9]+", self.results["target"]["file"]["clamav"]) family = words[2] self.results["malfamily"] = family
def run(self): """Run all processing modules and all signatures. @return: processing results. """ # Order modules using the user-defined sequence number. # If none is specified for the modules, they are selected in # alphabetical order. processing_list = list_plugins(group="processing") # If no modules are loaded, return an empty dictionary. if processing_list: processing_list.sort(key=lambda module: module.order) # Run every loaded processing module. for module in processing_list: result = self.process(module) # If it provided some results, append it to the big results # container. if result: self.results.update(result) else: log.info("No processing modules loaded") # For correct error log on webgui logs = os.path.join(self.analysis_path, "logs") if os.path.exists(logs): for file_name in os.listdir(logs): file_path = os.path.join(logs, file_name) if os.path.isdir(file_path): continue # Skipping the current log file if it's too big. if os.stat( file_path ).st_size > self.cuckoo_cfg.processing.analysis_size_limit: if not hasattr(self.results, "debug"): self.results.setdefault("debug", dict()).setdefault( "errors", list()) self.results["debug"]["errors"].append( "Behavioral log {0} too big to be processed, skipped. Increase analysis_size_limit in cuckoo.conf" .format(file_name)) continue else: log.info( "Logs folder doesn't exist, maybe something with with analyzer folder, any change?" ) family = "" self.results["malfamily_tag"] = "" # add detection based on suricata here if not family and "suricata" in self.results and "alerts" in self.results[ "suricata"] and self.results["suricata"]["alerts"]: for alert in self.results["suricata"]["alerts"]: if "signature" in alert and alert["signature"]: if alert["signature"].startswith("ET TROJAN") or alert[ "signature"].startswith("ETPRO TROJAN"): words = re.findall(r"[A-Za-z0-9]+", alert["signature"]) famcheck = words[2] famchecklower = famcheck.lower() if famchecklower == "win32" or famchecklower == "w32" or famchecklower == "ransomware": famcheck = words[3] famchecklower = famcheck.lower() blacklist = [ "executable", "potential", "likely", "rogue", "supicious", "generic", "possible", "known", "common", "troj", "trojan", "team", "probably", "w2km", "http", "abuse", "win32", "unknown", "single", "filename", "worm", "fake", "malicious", ] isgood = True for black in blacklist: if black == famchecklower: isgood = False break if len(famcheck) < 4: isgood = False if isgood: family = famcheck.title() self.results["malfamily_tag"] = "Suricata" if not family and self.results["info"][ "category"] == "file" and "virustotal" in self.results and "results" in self.results[ "virustotal"] and self.results["virustotal"]["results"]: detectnames = [] for res in self.results["virustotal"]["results"]: if res["sig"] and "Trojan.Heur." not in res["sig"]: # weight Microsoft's detection, they seem to be more accurate than the rest if res["vendor"] == "Microsoft": detectnames.append(res["sig"]) detectnames.append(res["sig"]) family = get_vt_consensus(detectnames) self.results["malfamily_tag"] = "VirusTotal" # fall back to ClamAV detection if not family and self.results["info"][ "category"] == "file" and "clamav" in self.results["target"][ "file"] and self.results["target"]["file"][ "clamav"] and self.results["target"]["file"][ "clamav"].startswith("Win.Trojan."): words = re.findall(r"[A-Za-z0-9]+", self.results["target"]["file"]["clamav"]) family = words[2] self.results["malfamily_tag"] = "ClamAV" if self.results.get("cape", False): self.results["malfamily"] = self.results["cape"] self.results["malfamily_tag"] = "CAPE" else: self.results["malfamily"] = family return self.results
def run(self): """Run all processing modules and all signatures. @return: processing results. """ # Order modules using the user-defined sequence number. # If none is specified for the modules, they are selected in # alphabetical order. processing_list = list_plugins(group="processing") # If no modules are loaded, return an empty dictionary. if processing_list: processing_list.sort(key=lambda module: module.order) # Run every loaded processing module. for module in processing_list: result = self.process(module) # If it provided some results, append it to the big results # container. if result: self.results.update(result) else: log.info("No processing modules loaded") # For correct error log on webgui logs = os.path.join(self.analysis_path, "logs") if os.path.exists(logs): for file_name in os.listdir(logs): file_path = os.path.join(logs, file_name) if os.path.isdir(file_path): continue # Skipping the current log file if it's too big. if os.stat( file_path ).st_size > self.cuckoo_cfg.processing.analysis_size_limit: if not hasattr(self.results, "debug"): self.results.setdefault("debug", dict()).setdefault( "errors", list()) self.results["debug"]["errors"].append( "Behavioral log {0} too big to be processed, skipped. Increase analysis_size_limit in cuckoo.conf" .format(file_name)) continue else: log.info( "Logs folder doesn't exist, maybe something with with analyzer folder, any change?" ) family = "" self.results["malfamily_tag"] = "" if self.results.get("detections", False): family = self.results["detections"] self.results["malfamily_tag"] = "CAPE" # add detection based on suricata here elif not family and "suricata" in self.results and "alerts" in self.results[ "suricata"] and self.results["suricata"]["alerts"]: for alert in self.results["suricata"]["alerts"]: if alert.get("signature", "") and alert["signature"].startswith( ("ET TROJAN", "ETPRO TROJAN", "ET MALWARE", "ET CNC")): family = get_suricata_family(alert["signature"]) if family: self.results["malfamily_tag"] = "Suricata" self.results["detections"] = family elif not family and self.results["info"][ "category"] == "file" and "virustotal" in self.results and "results" in self.results[ "virustotal"] and self.results["virustotal"]["results"]: detectnames = [] for res in self.results["virustotal"]["results"]: if res["sig"] and "Trojan.Heur." not in res["sig"]: # weight Microsoft's detection, they seem to be more accurate than the rest if res["vendor"] == "Microsoft": detectnames.append(res["sig"]) detectnames.append(res["sig"]) family = get_vt_consensus(detectnames) self.results["malfamily_tag"] = "VirusTotal" # fall back to ClamAV detection elif not family and self.results["info"][ "category"] == "file" and "clamav" in self.results.get( "target", {}).get("file", {}) and self.results["target"]["file"]["clamav"]: for detection in self.results["target"]["file"]["clamav"]: if detection.startswith("Win.Trojan."): words = re.findall(r"[A-Za-z0-9]+", detection) family = words[2] self.results["malfamily_tag"] = "ClamAV" if family: self.results["malfamily"] = family return self.results
def run(self): """Run evented signatures.""" # This will contain all the matched signatures. matched = [] stats = {} complete_list = list_plugins(group="signatures") evented_list = [ sig(self.results) for sig in complete_list if sig.enabled and sig.evented and self._check_signature_version(sig) and (not sig.filter_analysistypes or self.results["target"]["category"] in sig.filter_analysistypes) ] overlay = self._load_overlay() log.debug("Applying signature overlays for signatures: %s", ", ".join(overlay.keys())) for signature in complete_list + evented_list: self._apply_overlay(signature, overlay) if evented_list and "behavior" in self.results: log.debug("Running %u evented signatures", len(evented_list)) for sig in evented_list: stats[sig.name] = timedelta() if sig == evented_list[-1]: log.debug("\t `-- %s", sig.name) else: log.debug("\t |-- %s", sig.name) # Iterate calls and tell interested signatures about them. for proc in self.results["behavior"]["processes"]: for call in proc["calls"]: # Loop through active evented signatures. for sig in evented_list: # Skip current call if it doesn't match the filters (if any). if sig.filter_processnames and not proc[ "process_name"] in sig.filter_processnames: continue if sig.filter_apinames and not call[ "api"] in sig.filter_apinames: continue if sig.filter_categories and not call[ "category"] in sig.filter_categories: continue result = None try: pretime = datetime.now() result = sig.on_call(call, proc) posttime = datetime.now() timediff = posttime - pretime stats[sig.name] += timediff except NotImplementedError: result = False except: log.exception("Failed to run signature \"%s\":", sig.name) result = False # If the signature returns None we can carry on, the # condition was not matched. if result is None: continue # On True, the signature is matched. if result is True: log.debug("Analysis matched signature \"%s\"", sig.name) matched.append(sig.as_result()) if sig in complete_list: complete_list.remove(sig) # Either True or False, we don't need to check this sig anymore. evented_list.remove(sig) del sig # Call the stop method on all remaining instances. for sig in evented_list: try: pretime = datetime.now() result = sig.on_complete() posttime = datetime.now() timediff = posttime - pretime stats[sig.name] += timediff except NotImplementedError: continue except: log.exception( "Failed run on_complete() method for signature \"%s\":", sig.name) continue else: if result is True: log.debug("Analysis matched signature \"%s\"", sig.name) matched.append(sig.as_result()) if sig in complete_list: complete_list.remove(sig) # Link this into the results already at this point, so non-evented signatures can use it self.results["signatures"] = matched # Add in statistics for evented signatures that took at least some time for key, value in stats.iteritems(): if value: self.results["statistics"]["signatures"].append({ "name": key, "time": float("%d.%03d" % (value.seconds, value.microseconds / 1000)), }) # Compat loop for old-style (non evented) signatures. if complete_list: complete_list.sort(key=lambda sig: sig.order) log.debug("Running non-evented signatures") for signature in complete_list: if not signature.filter_analysistypes or self.results[ "target"]["category"] in signature.filter_analysistypes: match = self.process(signature) # If the signature is matched, add it to the list. if match: matched.append(match) # Reset the ParseProcessLog instances after each signature if "behavior" in self.results: for process in self.results["behavior"]["processes"]: process["calls"].reset() # Sort the matched signatures by their severity level. matched.sort(key=lambda key: key["severity"]) # Tweak later as needed malscore = 0.0 for match in matched: if match["severity"] == 1: malscore += match["weight"] * 0.5 * (match["confidence"] / 100.0) else: malscore += match["weight"] * (match["severity"] - 1) * ( match["confidence"] / 100.0) if malscore > 10.0: malscore = 10.0 if malscore < 0.0: malscore = 0.0 self.results["malscore"] = malscore family = "" # Make a best effort detection of malware family name (can be updated later by re-processing the analysis) for match in matched: if "families" in match and match["families"]: family = match["families"][0].title() break if not family and self.results["info"][ "category"] == "file" and "virustotal" in self.results and "results" in self.results[ "virustotal"] and self.results["virustotal"]["results"]: detectnames = [] for res in self.results["virustotal"]["results"]: if res["sig"]: # weight Microsoft's detection, they seem to be more accurate than the rest if res["vendor"] == "Microsoft": detectnames.append(res["sig"]) detectnames.append(res["sig"]) family = get_vt_consensus(detectnames) # add detection based on suricata here if not family and "suricata" in self.results and "alerts" in self.results[ "suricata"] and self.results["suricata"]["alerts"]: for alert in self.results["suricata"]["alerts"]: if "signature" in alert and alert["signature"]: if alert["signature"].startswith("ET TROJAN") or alert[ "signature"].startswith("ETPRO TROJAN"): words = re.findall(r"[A-Za-z0-9\.]+", alert["signature"]) famcheck = words[2] famchecklower = famcheck.lower() if famchecklower == "win32": famcheck = words[3] famchecklower = famcheck.lower() blacklist = [ "upx", "executable", "potential", "likely", "rogue", "supicious", "generic", "possible", "known", "common", "troj", "team", "probably", "w2km", "http", ] isgood = True for black in blacklist: if black == famchecklower: isgood = False break if isgood: famcheck = famcheck.split(".")[0] family = famcheck.title() self.results["malfamily"] = family
def run(self): """Run evented signatures.""" # This will contain all the matched signatures. matched = [] Database().set_statistics_time(self.task_id, SIGNATURES_STARTED) complete_list = list_plugins(group="signatures") evented_list = [sig(self.results) for sig in complete_list if sig.enabled and sig.evented and self._check_signature_version(sig) and (not sig.filter_analysistypes or self.results["target"]["category"] in sig.filter_analysistypes)] overlay = self._load_overlay() log.debug("Applying signature overlays for signatures: %s", ", ".join(overlay.keys())) for signature in complete_list + evented_list: self._apply_overlay(signature, overlay) if evented_list: log.debug("Running %u evented signatures", len(evented_list)) for sig in evented_list: if sig == evented_list[-1]: log.debug("\t `-- %s", sig.name) else: log.debug("\t |-- %s", sig.name) # Iterate calls and tell interested signatures about them. for proc in self.results["behavior"]["processes"]: for call in proc["calls"]: # Loop through active evented signatures. for sig in evented_list: # Skip current call if it doesn't match the filters (if any). if sig.filter_processnames and not proc["process_name"] in sig.filter_processnames: continue if sig.filter_apinames and not call["api"] in sig.filter_apinames: continue if sig.filter_categories and not call["category"] in sig.filter_categories: continue result = None try: result = sig.on_call(call, proc) except NotImplementedError: result = False except: log.exception("Failed to run signature \"%s\":", sig.name) result = False # If the signature returns None we can carry on, the # condition was not matched. if result is None: continue # On True, the signature is matched. if result is True: log.debug("Analysis matched signature \"%s\"", sig.name) matched.append(sig.as_result()) if sig in complete_list: complete_list.remove(sig) # Either True or False, we don't need to check this sig anymore. evented_list.remove(sig) del sig # Call the stop method on all remaining instances. for sig in evented_list: try: result = sig.on_complete() except NotImplementedError: continue except: log.exception("Failed run on_complete() method for signature \"%s\":", sig.name) continue else: if result is True: log.debug("Analysis matched signature \"%s\"", sig.name) matched.append(sig.as_result()) if sig in complete_list: complete_list.remove(sig) # Link this into the results already at this point, so non-evented signatures can use it self.results["signatures"] = matched # Compat loop for old-style (non evented) signatures. if complete_list: complete_list.sort(key=lambda sig: sig.order) log.debug("Running non-evented signatures") for signature in complete_list: if not signature.filter_analysistypes or self.results["target"]["category"] in signature.filter_analysistypes: match = self.process(signature) # If the signature is matched, add it to the list. if match: matched.append(match) # Reset the ParseProcessLog instances after each signature if "behavior" in self.results: for process in self.results["behavior"]["processes"]: process["calls"].reset() # Sort the matched signatures by their severity level. matched.sort(key=lambda key: key["severity"]) # Tweak later as needed malscore = 0.0 for match in matched: if match["severity"] == 1: malscore += match["weight"] * 0.5 * (match["confidence"] / 100.0) else: malscore += match["weight"] * (match["severity"] - 1) * (match["confidence"] / 100.0) if malscore > 10.0: malscore = 10.0 if malscore < 0.0: malscore = 0.0 self.results["malscore"] = malscore family = "" # Make a best effort detection of malware family name (can be updated later by re-processing the analysis) for match in matched: if "families" in match and match["families"]: family = match["families"][0].title() break if not family and "virustotal" in self.results and "results" in self.results["virustotal"] and self.results["virustotal"]["results"]: detectnames = [] for res in self.results["virustotal"]["results"]: if res["sig"]: # weight Microsoft's detection, they seem to be more accurate than the rest if res["vendor"] == "Microsoft": detectnames.append(res["sig"]) detectnames.append(res["sig"]) family = get_vt_consensus(detectnames) # add detection based on suricata here if not family and "suricata" in self.results and "alerts" in self.results["suricata"] and self.results["suricata"]["alerts"]: for alert in self.results["suricata"]["alerts"]: if "signature" in alert and alert["signature"]: if alert["signature"].startswith("ET TROJAN") or alert["signature"].startswith("ETPRO TROJAN"): words = re.findall(r"[A-Za-z0-9\./]+", alert["signature"]) famcheck = words[2] famchecklower = words[2].lower() blacklist = [ "upx", "executable", "potential", "likely", "rogue", "supicious", "generic", "possible", "known", "common", "troj", "team", "probably", ] isgood = True for black in blacklist: if black == famchecklower: isgood = False break if isgood: if famchecklower.startswith("win32"): famcheck = famcheck[6:] famcheck = famcheck.split(".")[0] family = famcheck.title() self.results["malfamily"] = family # Doing signature statistics alert = 0 normal = 0 crash = 0 anti = 0 for sig in self.results["signatures"]: if sig["alert"]: alert += 1 if sig["name"] in ["exec_crash"]: crash += 1 if sig["name"] in ["antidbg_devices", "antidbg_windows", "antiemu_wine", "antisandbox_mouse_hook", "antivm_generic_bios", "antivm_generic_disk", "antivm_generic_ide", "antivm_generic_scsi", "antivm_vbox_acpi", "antivm_vbox_devices", "antivm_vbox_files", "antivm_vbox_keys", "antivm_vbox_libs"]: anti += 1 normal += 1 Database().set_statistics_counter(self.task_id, SIGNATURES_TOTAL, normal) Database().set_statistics_counter(self.task_id, SIGNATURES_ALERT, alert) Database().set_statistics_counter(self.task_id, TASK_ISSUE_CRASH, crash) Database().set_statistics_counter(self.task_id, TASK_ISSUE_ANTI, anti) if "info" in self.results and "timeout" in self.results["info"]: Database().set_statistics_counter(self.task_id, TASK_TIMEDOUT, self.results["info"]["timeout"]) Database().set_statistics_time(self.task_id, SIGNATURES_FINISHED)
def run(self): """Run all processing modules and all signatures. @return: processing results. """ # Order modules using the user-defined sequence number. # If none is specified for the modules, they are selected in # alphabetical order. processing_list = list_plugins(group="processing") # If no modules are loaded, return an empty dictionary. if processing_list: processing_list.sort(key=lambda module: module.order) # Run every loaded processing module. for module in processing_list: result = self.process(module) # If it provided some results, append it to the big results # container. if result: self.results.update(result) else: log.info("No processing modules loaded") family = "" # add detection based on suricata here if not family and "suricata" in self.results and "alerts" in self.results[ "suricata"] and self.results["suricata"]["alerts"]: for alert in self.results["suricata"]["alerts"]: if "signature" in alert and alert["signature"]: if alert["signature"].startswith("ET TROJAN") or alert[ "signature"].startswith("ETPRO TROJAN"): words = re.findall(r"[A-Za-z0-9]+", alert["signature"]) famcheck = words[2] famchecklower = famcheck.lower() if famchecklower == "win32" or famchecklower == "w32" or famchecklower == "ransomware": famcheck = words[3] famchecklower = famcheck.lower() blacklist = [ "executable", "potential", "likely", "rogue", "supicious", "generic", "possible", "known", "common", "troj", "trojan", "team", "probably", "w2km", "http", "abuse", "win32", "unknown", "single", "filename", "worm", "fake", "malicious", ] isgood = True for black in blacklist: if black == famchecklower: isgood = False break if len(famcheck) < 4: isgood = False if isgood: family = famcheck.title() if not family and self.results["info"][ "category"] == "file" and "virustotal" in self.results and "results" in self.results[ "virustotal"] and self.results["virustotal"]["results"]: detectnames = [] for res in self.results["virustotal"]["results"]: if res["sig"] and "Trojan.Heur." not in res["sig"]: # weight Microsoft's detection, they seem to be more accurate than the rest if res["vendor"] == "Microsoft": detectnames.append(res["sig"]) detectnames.append(res["sig"]) family = get_vt_consensus(detectnames) # fall back to ClamAV detection if not family and self.results["info"][ "category"] == "file" and "clamav" in self.results["target"][ "file"] and self.results["target"]["file"][ "clamav"] and self.results["target"]["file"][ "clamav"].startswith("Win.Trojan."): words = re.findall(r"[A-Za-z0-9]+", self.results["target"]["file"]["clamav"]) family = words[2] if self.results.get("cape", False): self.results["malfamily"] = self.results["cape"] else: self.results["malfamily"] = family return self.results
def vt_lookup(category, target, on_demand=False): if processing_conf.virustotal.enabled and (processing_conf.virustotal.get( "on_demand", False) is False or on_demand is True): if category not in ("file", "url"): return {"error": True, "msg": "VT category isn't supported"} if category == "file": if not do_file_lookup: return { "error": True, "msg": "VT File lookup disabled in processing.conf" } if not os.path.exists(target): return {"error": True, "msg": "File doesn't exist"} sha256 = File(target).get_sha256() url = VIRUSTOTAL_FILE_URL.format(id=sha256) elif category == "url": if not do_url_lookup: return { "error": True, "msg": "VT URL lookup disabled in processing.conf" } if urlscrub: urlscrub_compiled_re = None try: urlscrub_compiled_re = re.compile(urlscrub) except Exception as e: raise CuckooProcessingError( "Failed to compile urlscrub regex" % (e)) try: target = re.sub(urlscrub_compiled_re, "", target) except Exception as e: return {"error": True, "msg": "Failed to scrub url" % (e)} # normalize the URL the way VT appears to if not target.lower().startswith( "http://") and not target.lower().startswith("https://"): target = "http://" + target slashsplit = target.split("/") slashsplit[0] = slashsplit[0].lower() slashsplit[2] = slashsplit[2].lower() if len(slashsplit) == 3: slashsplit.append("") target = "/".join(slashsplit) sha256 = hashlib.sha256(target.encode("utf-8")).hexdigest() url = VIRUSTOTAL_URL_URL.format(id=target) try: r = requests.get(url, headers=headers, verify=True, timeout=timeout) if r.ok: vt_response = r.json() engines = vt_response.get("data", {}).get("attributes", {}).get( "last_analysis_results", {}) if engines: virustotal = {} virustotal["names"] = vt_response.get("data", {}).get( "attributes", {}).get("names") virustotal["scan_id"] = vt_response.get("data", {}).get("id") virustotal["md5"] = vt_response.get("data", {}).get( "attributes", {}).get("md5") virustotal["sha1"] = vt_response.get("data", {}).get( "attributes", {}).get("sha1") virustotal["sha256"] = vt_response.get("data", {}).get( "attributes", {}).get("sha256") virustotal["tlsh"] = vt_response.get("data", {}).get( "attributes", {}).get("tlsh") virustotal["positive"] = (vt_response.get("data", {}).get( "attributes", {}).get("last_analysis_stats", {}).get("malicious")) virustotal["total"] = len(engines.keys()) virustotal["permalink"] = vt_response.get("data", {}).get( "links", {}).get("self") virustotal["scans"] = dict( (engine.replace(".", "_"), block) for engine, block in engines.items() if remove_empty and block["result"]) virustotal["resource"] = sha256 virustotal["results"] = list() detectnames = list() for engine, block in engines.items(): virustotal["results"] += [{ "vendor": engine.replace(".", "_"), "sig": block["result"] }] if block["result"] and "Trojan.Heur." not in block[ "result"]: # weight Microsoft's detection, they seem to be more accurate than the rest if engine == "Microsoft": detectnames.append(block["result"]) detectnames.append(block["result"]) virustotal["detection"] = get_vt_consensus(detectnames) return virustotal else: return dict() else: return { "error": True, "msg": "Unable to complete connection to VirusTotal. Status code: {}" .format(r.status_code) } except requests.exceptions.RequestException as e: return { "error": True, "msg": "Unable to complete connection to VirusTotal: {0}".format(e) } else: return dict()