def execute(self, request: ServiceRequest): try: self.client = Client(apikey=self.config.get( "api_key", request.get_param("api_key")), proxy=self.config.get('proxy') or None) except Exception as e: self.log.error("No API key found for VirusTotal") raise e if request.task.metadata.get('submitted_url', None) and request.task.depth == 0: response = self.scan_url(request) else: response = self.scan_file(request) if response: result = self.parse_results(response) request.result = result else: request.result = Result()
def _download_from_vt(client: vt.Client, file_hash: str) -> bytes: """ Download file from VT. :param vt.Client client: the VT client :param str file_hash: the file hash :rtype: bytes :return: the downloaded data :raises ValueError: in case of any error """ try: buffer = io.BytesIO() client.download_file(file_hash, buffer) buffer.seek(0, 0) return buffer.read() except (IOError, vt.APIError) as e: raise ValueError(str(e)) finally: # vt.Client likes to free resources at shutdown, and it can be used as context to ease that # Since the structure of the module does not play well with how MISP modules are organized # let's play nice and close connections pro-actively (opened by "download_file") if client: client.close()
class VirusTotalStatic(ServiceBase): def __init__(self, config=None): super(VirusTotalStatic, self).__init__(config) self.client = None def start(self): self.log.debug("VirusTotalStatic service started") def execute(self, request: ServiceRequest): try: self.client = Client(apikey=self.config.get( "api_key", request.get_param("api_key")), proxy=self.config.get('proxy') or None) except Exception as e: self.log.error("No API key found for VirusTotal") raise e if request.task.metadata.get('submitted_url', None) and request.task.depth == 0: response = self.scan_url(request) else: response = self.scan_file(request) if response: result = self.parse_results(response) request.result = result else: request.result = Result() def common_scan(self, type: str, sample, retried: int = 0): json_response = None if retried < MAX_RETRY: try: json_response = self.client.get_json(f"/{type}s/{sample}") except APIError as e: if "NotFoundError" in e.code: self.log.warning(f"VirusTotal has nothing on this {type}.") elif "QuotaExceededError" in e.code: self.log.warning("Quota Exceeded. Trying again in 60s") time.sleep(60) retried += 1 return self.common_scan(type, sample, retried) else: self.log.error(e) return json_response def scan_file(self, request: ServiceRequest): return self.common_scan("file", request.sha256) def scan_url(self, request: ServiceRequest): url_id = base64.urlsafe_b64encode( request.task.metadata.get( 'submitted_url').encode()).decode().strip("=") return self.common_scan("url", url_id) @staticmethod def parse_results(response: Dict[str, Any]): res = Result() response = response['data'] url_section = ResultSection('VirusTotal report permalink', body_format=BODY_FORMAT.URL, body=json.dumps( {"url": response['links']['self']})) res.add_section(url_section) response = response['attributes'] scans = response['last_analysis_results'] av_hits = ResultSection('Anti-Virus Detections') av_hits.add_line( f'Found {response["last_analysis_stats"]["malicious"]} AV hit(s) from ' f'{len(response["last_analysis_results"].keys())}') for majorkey, subdict in sorted(scans.items()): if subdict['category'] == "malicious": virus_name = subdict['result'] av_hit_section = AvHitSection(majorkey, virus_name) av_hit_section.set_heuristic( 1, signature=f'{majorkey}.{virus_name}') av_hit_section.add_tag('av.virus_name', virus_name) av_hits.add_subsection(av_hit_section) res.add_section(av_hits) return res
def new_client(httpserver): return Client('dummy_api_key', host='http://' + httpserver.host + ':' + str(httpserver.port))