VirusTotal Retrohunt Feed
Use this feed integration to fetch VirusTotal Retrohunt matches.
Data Enrichment & Threat Intelligence · VirusTotal · Feed
Details
| ID | VirusTotal Retrohunt Feed |
|---|---|
| Provider | |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.5.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM EDR Cortex Cloud Cloud Runtime Security |
README
Use this feed integration to fetch VirusTotal Retrohunt matches. It processes the latest finished job retrieving its matches based on the limit parameter (40 by default) in every fetch until there are no more matches for that job.
Configure VirusTotal Retrohunt Feed in Cortex
| Parameter | Description | Required |
|---|---|---|
| API Key (leave empty. Fill in the API key in the password field.) | True | |
| API Key | True | |
| Limit | Limit of indicators to fetch from retrohunt job results. | False |
| Fetch indicators | False | |
| Indicator Reputation | Indicators from this integration instance will be marked with this reputation. | False |
| Source Reliability | Reliability of the source providing the intelligence data. | True |
| False | ||
| False | ||
| Feed Fetch Interval | False | |
| Bypass exclusion list | When selected, the exclusion list is ignored for indicators from this feed. This means that if an indicator from this feed is on the exclusion list, the indicator might still be added to the system. | False |
| Tags | Supports CSV values. | False |
| Traffic Light Protocol Color | The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
vt-retrohunt-get-indicators
Gets the matches from a given retrohunt job’s id or the latest finished by default.
vt-retrohunt-reset-fetch-indicators
Reset the last processed job’s id
Base Command
vt-retrohunt-get-indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of results to return. Default is 40. | Optional |
| job_id | VT Retrohunt job’s ID. | Optional |
Context Output
There is no context output for this command.
Command Example
!vt-retrohunt-get-indicators
!vt-retrohunt-get-indicators limit=10
!vt-retrohunt-get-indicators limit=10 job_id="RETROHUNT-JOB-ID"
Human Readable Output
Indicators from VirusTotal Retrohunt Feed
| Sha256 | Detections | Filetype |
|---|---|---|
| 80db033dfe2b4e966d46a4ceed36e20b98a13891ce364a1308b90da7ad694cf3 | 1/59 | ELF |
| 6717c568e623551e600d315c7d1d634824a6f4b16e8aedfa298aefe7155313ff | 1/59 | ELF |
| 2c02a593ac714f9bac876d0a3c056384e0038505515d0c8472aa00ea36a6abb2 | 1/59 | ELF |
| e658b64650153c2207a76b2ee390b0fef04712d0da1d75a9eae25e4be596071a | 3/59 | ELF |
| 5ec2e17f25e800825ec5ed592c73303f840fa33cce2c8c4a4e7b6556798ffda0 | 1/55 | ELF |
| 771ba05ca9321dc723fc66b995c1d79a969330fc4242da6737cff1b364f978c8 | 2/59 | ELF |
| 4e3fac63a8b027788a10fd0191adf3ad59b2111324e1aa4eb4441723793c1b11 | 33/60 | ELF |
| ff1bdaf789643c6b934c9a9593fea82912d5974ba6ca0fd8dbf42db09ba82925 | 0/60 | ELF |
| 4371874f35538dc7d3b1d50df8cd0e8ad0744441ed487deb0d7a18a4a4373fea | 1/60 | ELF |
Configuration parameters
credentials— API Key (leave empty. Fill in the API key in the password field.) (required)limit— Limitfeed— Fetch indicatorsfeedReputation— Indicator ReputationfeedReliability— Source Reliability (required)feedExpirationPolicy—feedExpirationInterval—feedFetchInterval— Feed Fetch IntervalfeedBypassExclusionList— Bypass exclusion listfeedTags— Tagstlp_color— Traffic Light Protocol Color
Commands (2)
-
vt-retrohunt-get-indicatorsGets the matches from a given retrohunt job's id or the latest by default.
-
vt-retrohunt-reset-fetch-indicatorsThis command will reset your fetch history.
import demistomock as demisto # noqa: F401 import urllib3 from CommonServerPython import * # noqa: F401 # Disable insecure warnings. urllib3.disable_warnings() class Client(BaseClient): def get_detections_str(self, last_analysis_stats: dict): if not last_analysis_stats: return "0/0" malicious = last_analysis_stats["malicious"] total = ( last_analysis_stats["harmless"] + last_analysis_stats["suspicious"] + last_analysis_stats["undetected"] + last_analysis_stats["malicious"] ) return f"{malicious}/{total}" def build_iterator(self, limit: int = 40, job_id: str = "") -> List: """Retrieves all entries from the feed. Returns: A list of objects, containing the indicators. """ result = [] jobs = self.list_job_matches(limit, job_id) try: for job in jobs: result.append({"data": job, "type": "file", "FeedURL": self._base_url}) except ValueError as err: demisto.debug(str(err)) raise ValueError(f"Could not parse returned data as indicator. \n\nError message: {err}") return result def fetch_jobs(self): """Retrieve finished retrohunt jobs.""" return self._http_request("GET", "intelligence/retrohunt_jobs?filter=status:finished") def fetch_job_matches(self, job_id: str, limit: int = 40, cursor: str = None) -> dict: return self._http_request( "GET", f"intelligence/retrohunt_jobs/{job_id}/matching_files", params=assign_params(limit=min(limit, 40), cursor=cursor), ) def list_job_matches(self, limit: int = 40, job_id: str = "") -> list: """Retrieve matches for a given retrohunt job (latest by default).""" last_job_id = demisto.getIntegrationContext().get("last_retrohunt_job_id") last_job_cursor = demisto.getIntegrationContext().get("last_retrohunt_job_matches_cursor") # There is a pending job to be completely processed if not job_id and last_job_id and last_job_cursor: job_id = last_job_id # Look for new jobs and get the latest finished one if not job_id: jobs = self.fetch_jobs().get("data") if len(jobs) == 0: return [] job_id = jobs[0].get("id") last_job_id = demisto.getIntegrationContext().get("last_retrohunt_job_id") # Ignore already processed job if last_job_id == job_id: return [] demisto.setIntegrationContext({"last_retrohunt_job_id": job_id}) cursor = last_job_cursor matches = [] # Retrieve matches for the latest job until the limit is reached or there are no more matches while True: response = self.fetch_job_matches(job_id, limit, cursor) cursor = response.get("meta", {}).get("cursor") matches.extend(response.get("data", [])) demisto.setIntegrationContext({"last_retrohunt_job_matches_cursor": cursor}) if len(matches) >= limit or not cursor: break return matches def test_module(client: Client, args: dict) -> str: client.list_job_matches() return "ok" def fetch_indicators_command( client: Client, tlp_color: Optional[str] = None, feed_tags: List = [], limit: int = 40, job_id: str = "" ) -> List[Dict]: """Retrieves indicators from the feed Args: client (Client): Client object with request tlp_color (str): Traffic Light Protocol color feed_tags (list): tags to assign fetched indicators limit (int): limit the results Returns: Indicators. """ iterator = client.build_iterator(limit, job_id) indicators = [] if limit > 0: iterator = iterator[:limit] # extract values from iterator for item in iterator: value_ = item.get("data") type_ = FeedIndicatorType.File attributes = value_.get("attributes", {}) raw_data = { "value": value_, "type": type_, } # Create indicator object for each value. # The object consists of a dictionary with required and optional keys and values, as described blow. indicator_obj = { # The indicator value. "value": attributes["sha256"], # The indicator type as defined in Cortex XSOAR. # One can use the FeedIndicatorType class under CommonServerPython to populate this field. "type": type_, # The name of the service supplying this feed. "service": "VirusTotal", # A dictionary that maps values to existing indicator fields defined in Cortex XSOAR. # One can use this section in order to map custom indicator fields previously defined # in Cortex XSOAR to their values. "fields": { "md5": attributes.get("md5"), "sha1": attributes.get("sha1"), "sha256": attributes.get("sha256"), "ssdeep": attributes.get("ssdeep"), }, # A dictionary of the raw data returned from the feed source about the indicator. "rawJSON": raw_data, "sha256": attributes["sha256"], "detections": client.get_detections_str(attributes.get("last_analysis_stats")), "fileType": attributes.get("type_description"), } if feed_tags: indicator_obj["fields"]["tags"] = feed_tags if tlp_color: indicator_obj["fields"]["trafficlightprotocol"] = tlp_color indicators.append(indicator_obj) return indicators def get_indicators_command(client: Client, params: Dict[str, str], args: Dict[str, str]) -> CommandResults: """Wrapper for retrieving indicators from the feed to the war-room. Args: client: Client object with request params: demisto.params() args: demisto.args() Returns: Outputs. """ limit = int(args.get("limit", params.get("limit", 40))) job_id = args.get("job_id", "") tlp_color = params.get("tlp_color") feed_tags = argToList(params.get("feedTags", "")) indicators = fetch_indicators_command(client, tlp_color, feed_tags, limit, job_id) human_readable = tableToMarkdown( "Indicators from VirusTotal Retrohunt Feed:", indicators, headers=["sha256", "detections", "fileType"], headerTransform=string_to_table_header, removeNull=True, ) return CommandResults( readable_output=human_readable, outputs_prefix="", outputs_key_field="", raw_response=indicators, outputs={}, ) def reset_last_job_id(): """ Reset last job ib from the integration context """ demisto.setIntegrationContext({}) return CommandResults(readable_output="Fetch history deleted successfully") def main(): """ main function, parses params and runs command functions """ params = demisto.params() # If your Client class inherits from BaseClient, SSL verification is # handled out of the box by it, just pass ``verify_certificate`` to # the Client constructor insecure = not params.get("insecure", False) # If your Client class inherits from BaseClient, system proxy is handled # out of the box by it, just pass ``proxy`` to the Client constructor proxy = params.get("proxy", False) command = demisto.command() args = demisto.args() demisto.debug(f"Command being called is {command}") try: client = Client( base_url="https://www.virustotal.com/api/v3/", verify=insecure, proxy=proxy, headers={ "x-apikey": params["credentials"]["password"], "x-tool": "CortexVirusTotalRetrohuntFeed", }, ) if command == "test-module": # This is the call made when pressing the integration Test button. return_results(test_module(client, {})) elif command == "vt-retrohunt-get-indicators": # This is the command that fetches a limited number of indicators # from the feed source and displays them in the war room. return_results(get_indicators_command(client, params, args)) elif command == "vt-retrohunt-reset-fetch-indicators": return_results(reset_last_job_id()) elif command == "fetch-indicators": # This is the command that initiates a request to the feed endpoint # and create new indicators objects from the data fetched. If the # integration instance is configured to fetch indicators, then this # is the commandthat will be executed at the specified feed fetch # interval. indicators = fetch_indicators_command(client, params) for iter_ in batch(indicators, batch_size=2000): demisto.createIndicators(iter_) else: raise NotImplementedError(f"Command {command} is not implemented.") # Log exceptions and return errors except Exception as e: demisto.error(traceback.format_exc()) # Print the traceback return_error(f"Failed to execute {command} command.\nError:\n{e!s}") if __name__ in ["__main__", "builtin", "builtins"]: main()