CrowdStrike Indicator Feed
Retrieves indicators from the CrowdStrike Falcon Intel Feed.
Data Enrichment & Threat Intelligence · Crowdstrike Falcon Intel Feed · Feed
Details
| ID | CrowdStrike Indicator Feed |
|---|---|
| Provider | CrowdStrike |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.5.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
README
CrowdStrike Falcon Intel Indicator Feed
Configure CrowdStrike Indicator Feed in Cortex
| Parameter | Description | Required |
|---|---|---|
| Fetch indicators | False | |
| CrowdStrike Base URL | True | |
| CrowdStrike API Client ID | For non 6.1 - enter your CrowdStrike API Client Secret in the password field. | True |
| Type | The indicator types to fetch. Out-of-the-box indicator types supported in XSOAR are: “Account”, “Domain”, “Email”, “File MD5”, “File SHA256”, “IP”, “Registry Key”, and “URL”. The default is “ALL”. | False |
| First fetch time | The time range to consider for the initial data fetch. Leave empty to fetch from the first available indicator. | False |
| Max. indicators per fetch | Maximum number of indicators per fetch. Value should be between 1 - 10000. A large value may result in a timeout. | False |
| Malicious confidence | Malicious confidence level to filter by. | False |
| Include deleted indicators | False | |
| Filter | Advanced: FQL query. For more information visit the CrowdStrike documentation. For example: published_date:>”now-3d” can be used to only pull indicators published in the last 3 days. | False |
| Generic phrase match | Generic phrase match search across all indicator fields. | 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 |
| Traffic Light Protocol Color | The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. | False |
| Indicator Expiration Method | The feed’s expiration method. | False |
| Feed Fetch Interval | The interval after which the feed expires. | False |
| Tags | Supports CSV values. | False |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | 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 |
Note: To change the fetch start time , use the crowdstrike-reset-fetch-indicators command after setting the desired time in First Fetch Time parameter.
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.
crowdstrike-indicators-list
Gets indicators from the CrowdStrike Falcon Intel Feed.
Base Command
crowdstrike-indicators-list
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of indicators to return. Default is 50. | Optional |
| offset | The index of the first indicator to fetch. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| CrowdStrikeFalconIntel.Indicators.id | String | Indicator ID. |
| CrowdStrikeFalconIntel.Indicators.value | String | Indicator value. |
| CrowdStrikeFalconIntel.Indicators.type | String | Indicator type. |
| CrowdStrikeFalconIntel.Indicators.fields.reports | Unknown | Indicator reports. |
| CrowdStrikeFalconIntel.Indicators.fields.actors | Unknown | Actors related to the indicator. |
| CrowdStrikeFalconIntel.Indicators.fields.malwarefamily | Unknown | Indicator malware families. |
| CrowdStrikeFalconIntel.Indicators.fields.stixkillchainphases | Unknown | Indicator kill chains. |
| CrowdStrikeFalconIntel.Indicators.fields.maliciousconfidence | String | Indicator malicious confidence. |
| CrowdStrikeFalconIntel.Indicators.fields.tags | Unknown | Indicator labels. |
| CrowdStrikeFalconIntel.Indicators.fields.targets | Unknown | Targets of the indicator. |
| CrowdStrikeFalconIntel.Indicators.fields.threattypes | Unknown | Indicator threat types. |
| CrowdStrikeFalconIntel.Indicators.fields.vulnerabilities | Unknown | Indicator vulnerabilities. |
| CrowdStrikeFalconIntel.Indicators.fields.ipaddress | Unknown | Indicator related IP address. |
| CrowdStrikeFalconIntel.Indicators.fields.domainname | Unknown | Indicator related domains. |
| CrowdStrikeFalconIntel.Indicators.fields.updateddate | Date | Indicator update date. |
| CrowdStrikeFalconIntel.Indicators.fields.creationdate | Unknown | Indicator creation date. |
| CrowdStrikeFalconIntel.Indicators.rawJSON | Unknown | Raw response. |
Command Example
!crowdstrike-indicators-list limit=3
Context Example
{
"CrowdStrikeFalconIntel": {
"Indicators": [
{
"fields": {
"actor": [],
"creationdate": 1600080520,
"domainname": [],
"ipaddress": [],
"confidence": "low",
"malwarefamily": [
"Remcos"
],
"reports": [],
"stixkillchainphases": [
"C2"
],
"threattypes": [
{
"threatcategory": "Criminal"
}
],
"tags": [
"MaliciousConfidence/Low",
"KillChain/C2",
"ThreatType/Commodity",
"ThreatType/Criminal",
"ThreatType/CredentialHarvesting",
"Malware/Remcos"
],
"targets": [],
"trafficlightprotocol": "AMBER",
"updateddate": 1608207378,
"vulnerabilities": []
},
"id": "ip_address_1.1.1.1",
"rawJSON": {
"_marker": "1608207378159fc77935511a2f0c9541511bd936f8",
"actors": [],
"deleted": false,
"domain_types": [],
"id": "ip_address_1.1.1.1",
"indicator": "1.1.1.1",
"ip_address_types": [],
"kill_chains": [
"C2"
],
"labels": [
{
"created_on": 1600080520,
"last_valid_on": 1608207377,
"name": "MaliciousConfidence/Low"
},
{
"created_on": 1600080520,
"last_valid_on": 1608207377,
"name": "KillChain/C2"
}
],
"last_updated": 1608207378,
"malicious_confidence": "low",
"malware_families": [
"Remcos"
],
"published_date": 1600080520,
"relations": [
{
"created_date": 1608207377,
"id": "hash_sha256_9bb12d611cb19e84f2f22791cb86a43841e95020b1e113469e5cad95b97a8d42",
"indicator": "9bb12d611cb19e84f2f22791cb86a43841e95020b1e113469e5cad95b97a8d42",
"last_valid_date": 1608207377,
"type": "hash_sha256"
},
{
"created_date": 1608207377,
"id": "hash_sha256_58a3e65de35d8da1f7955680e07a82ede43a1e677e0abc200923b484a7615494",
"indicator": "58a3e65de35d8da1f7955680e07a82ede43a1e677e0abc200923b484a7615494",
"last_valid_date": 1608207377,
"type": "hash_sha256"
}
],
"reports": [],
"targets": [],
"threat_types": [
"Criminal"
],
"type": "ip_address",
"vulnerabilities": []
},
"type": "IP",
"value": "1.1.1.1"
},
{
"fields": {
"actor": [],
"creationdate": 1608208087,
"domainname": [],
"ipaddress": [],
"confidence": "low",
"malwarefamily": [
"Remcos"
],
"reports": [],
"stixkillchainphases": [
"C2"
],
"tags": [
"MaliciousConfidence/Low",
"KillChain/C2",
"Malware/Remcos",
"ThreatType/Commodity",
"ThreatType/Criminal",
"ThreatType/CredentialHarvesting"
],
"threattypes": [
{
"threatcategory": "Criminal"
}
],
"targets": [],
"trafficlightprotocol": "AMBER",
"updateddate": 1608208109,
"vulnerabilities": []
},
"id": "ip_address_2.2.2.2",
"rawJSON": {
"_marker": "16082081092644654ac0f7738b7086d25532d38ec1",
"actors": [],
"deleted": false,
"domain_types": [],
"id": "ip_address_2.2.2.2",
"indicator": "2.2.2.2",
"ip_address_types": [],
"kill_chains": [
"C2"
],
"labels": [
{
"created_on": 1608208087,
"last_valid_on": 1608208108,
"name": "MaliciousConfidence/Low"
},
{
"created_on": 1608208087,
"last_valid_on": 1608208108,
"name": "KillChain/C2"
}
],
"last_updated": 1608208109,
"malicious_confidence": "low",
"malware_families": [
"Remcos"
],
"published_date": 1608208087,
"relations": [
{
"created_date": 1608208090,
"id": "hash_sha256_b90713f3b31f29ceb64355b3c016aa0a74e1ce90dca5570db04aff27e12b343c",
"indicator": "b90713f3b31f29ceb64355b3c016aa0a74e1ce90dca5570db04aff27e12b343c",
"last_valid_date": 1608208090,
"type": "hash_sha256"
},
{
"created_date": 1483468884,
"id": "domain_holmann02.ddns.net",
"indicator": "holmann02.ddns.net",
"last_valid_date": 1483468884,
"type": "domain"
}
],
"reports": [],
"targets": [],
"threat_types": [
"Criminal"
],
"type": "ip_address",
"vulnerabilities": []
},
"type": "IP",
"value": "1.2.3.4"
},
{
"fields": {
"actor": [
"MUMMYSPIDER"
],
"creationdate": 1592473928,
"domainname": [],
"ipaddress": [],
"confidence": "low",
"malwarefamily": [],
"reports": [],
"stixkillchainphases": [
"C2"
],
"threattypes": [],
"tags": [
"KillChain/C2",
"MaliciousConfidence/Low",
"Actor/MUMMYSPIDER"
],
"targets": [],
"trafficlightprotocol": "AMBER",
"updateddate": 1608208626,
"vulnerabilities": []
},
"id": "ip_address_1.2.3.4",
"rawJSON": {
"_marker": "1608208626d02e40678e554f71fd6c3c33cc71c5c0",
"actors": [
"MUMMYSPIDER"
],
"deleted": false,
"domain_types": [],
"id": "ip_address_1.2.3.4",
"indicator": "1.2.3.4",
"ip_address_types": [],
"kill_chains": [
"C2"
],
"labels": [
{
"created_on": 1592473928,
"last_valid_on": 1592473930,
"name": "KillChain/C2"
},
{
"created_on": 1592473928,
"last_valid_on": 1592473930,
"name": "MaliciousConfidence/Low"
},
{
"created_on": 1592473930,
"last_valid_on": 1592473930,
"name": "Actor/MUMMYSPIDER"
}
],
"last_updated": 1608208626,
"malicious_confidence": "low",
"malware_families": [],
"published_date": 1592473928,
"relations": [
{
"created_date": 1597858281,
"id": "url_http://1.1.1.1:80",
"indicator": "http://1.1.1.1:80",
"last_valid_date": 1597858281,
"type": "url"
},
{
"created_date": 1592473931,
"id": "hash_md5_6d795170965336a9006f059dd444fc8f",
"indicator": "6d795170965336a9006f059dd444fc8f",
"last_valid_date": 1592473931,
"type": "hash_md5"
}
],
"reports": [],
"targets": [],
"threat_types": [],
"type": "ip_address",
"vulnerabilities": []
},
"type": "IP",
"value": "1.2.3.4"
}
]
}
}
Human Readable Output
Indicators from CrowdStrike Falcon Intel
Type Value Id IP 1.1.1.1 ip_address_1.1.1.1 IP 2.2.2.2 ip_address_2.2.2.2 IP 1.2.3.4 ip_address_1.2.3.4
crowdstrike-reset-fetch-indicators
Resets the retrieving start time according to the First Fetch Time parameter, WARNING: This command will reset your fetch history.
Base Command
crowdstrike-reset-fetch-indicators
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
Command Example
!crowdstrike-reset-fetch-indicators
Human Readable Output
Fetch history deleted successfully
Configuration parameters
feed— Fetch indicatorsbase_url— CrowdStrike Base URL (required)credentials— CrowdStrike API Client ID (required)timeout— The amount of time (in seconds) that a request will wait for a client to establish a connection to a remote machine before a timeout occurs.type— Typefirst_fetch— First fetch time (required)max_indicator_to_fetch— Max. indicators per fetchmalicious_confidence— Malicious confidenceinclude_deleted— Include deleted indicatorsfilter— Filtergeneric_phrase— Generic phrase matchfeedReputation— Indicator ReputationfeedReliability— Source Reliability (required)tlp_color— Traffic Light Protocol ColorfeedExpirationPolicy—feedExpirationInterval—feedFetchInterval— Feed Fetch IntervalfeedTags— Tagscreate_relationships— Create relationshipsinsecure— Trust any certificate (not secure)proxy— Use system proxy settingsfeedBypassExclusionList— Bypass exclusion listfeedIncremental— Incremental Feed
Commands (2)
-
crowdstrike-indicators-listGets indicators from the CrowdStrike Falcon Intel Feed.
-
crowdstrike-reset-fetch-indicatorsResets the retrieving start time according to the `First Fetch Time` parameter, WARNING: This command will reset your fetch history.
import copy import demistomock as demisto # noqa: F401 # IMPORTS import urllib3 from CommonServerPython import * # noqa: F401 from CrowdStrikeApiModule import * # noqa: E402 from CommonServerUserPython import * # noqa urllib3.disable_warnings() XSOAR_TYPES_TO_CROWDSTRIKE = { "account": "username", "domain": "domain", "email": "email_address", "file md5": "hash_md5", "file sha-256": "hash_sha256", "ip": "ip_address", "registry key": "registry", "url": "url", } CROWDSTRIKE_TO_XSOAR_TYPES = { "username": FeedIndicatorType.Account, "domain": FeedIndicatorType.Domain, "email_address": FeedIndicatorType.Email, "hash_md5": FeedIndicatorType.File, "hash_sha1": FeedIndicatorType.File, "hash_sha256": FeedIndicatorType.File, "registry": FeedIndicatorType.Registry, "url": FeedIndicatorType.URL, "ip_address": FeedIndicatorType.IP, "reports": ThreatIntel.ObjectsNames.REPORT, "actors": ThreatIntel.ObjectsNames.THREAT_ACTOR, "malware_families": ThreatIntel.ObjectsNames.MALWARE, "vulnerabilities": FeedIndicatorType.CVE, } INDICATOR_TO_CROWDSTRIKE_RELATION_DICT: Dict[str, Any] = { ThreatIntel.ObjectsNames.REPORT: { FeedIndicatorType.File: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.IP: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Domain: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.URL: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Email: EntityRelationship.Relationships.RELATED_TO, FeedIndicatorType.Registry: EntityRelationship.Relationships.RELATED_TO, FeedIndicatorType.Account: EntityRelationship.Relationships.RELATED_TO, }, ThreatIntel.ObjectsNames.THREAT_ACTOR: { FeedIndicatorType.File: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.IP: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Domain: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.URL: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Email: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Registry: EntityRelationship.Relationships.RELATED_TO, FeedIndicatorType.Account: EntityRelationship.Relationships.RELATED_TO, }, ThreatIntel.ObjectsNames.MALWARE: { FeedIndicatorType.File: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.IP: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Domain: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.URL: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Email: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Registry: EntityRelationship.Relationships.RELATED_TO, FeedIndicatorType.Account: EntityRelationship.Relationships.RELATED_TO, }, FeedIndicatorType.CVE: { FeedIndicatorType.File: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.IP: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Domain: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.URL: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Email: EntityRelationship.Relationships.INDICATOR_OF, FeedIndicatorType.Registry: EntityRelationship.Relationships.RELATED_TO, FeedIndicatorType.Account: EntityRelationship.Relationships.RELATED_TO, }, } CROWDSTRIKE_INDICATOR_RELATION_FIELDS = ["reports", "actors", "malware_families", "vulnerabilities", "relations"] def kill_chain_standard_values(phases: list | None): """ Will convert crowdstrike's return values for kill chain to our standard kill chain syntax. Args: phase: the raw inout from the api Returns: the standardized value, or the given value if not found """ if not phases: return phases return [ { "reconnaissance": "Reconnaissance", "weaponization": "Weaponization", "installation": "Installation", "exploitation": "Exploitation", "delivery": "Delivery", "c2": "Command & Control", "actionOnObjectives": "Actions on Objectives", }.get(phase.lower(), phase) for phase in phases ] class Client(CrowdStrikeClient): def __init__( self, credentials, base_url, include_deleted, type, limit, tlp_color=None, feed_tags=None, malicious_confidence=None, filter_string=None, generic_phrase=None, insecure=True, proxy=False, first_fetch=None, create_relationships=True, timeout="10", ): params = assign_params( credentials=credentials, server_url=base_url, insecure=insecure, ok_codes=(), proxy=proxy, timeout=timeout ) super().__init__(params) self.type = type self.malicious_confidence = malicious_confidence self.filter_string = filter_string self.generic_phrase = generic_phrase self.include_deleted = include_deleted self.tlp_color = tlp_color self.feed_tags = feed_tags self.limit = limit self.first_fetch = first_fetch self.create_relationships = create_relationships def get_indicators(self, params): response = super().http_request( method="GET", params=params, url_suffix="intel/combined/indicators/v1", timeout=30, ok_codes=(200, 401) ) if response.get("errors") and response["errors"][0].get("code") == 401: demisto.info(f'request failed with status code is 401, error: {str(response["errors"][0])}, regenerate token') self._token = self._get_token() self._headers = {"Authorization": "bearer " + self._token} response = super().http_request(method="GET", params=params, url_suffix="intel/combined/indicators/v1", timeout=30) return response def get_actors_names_request(self, params_string): response = self._http_request( method="GET", url_suffix=f"intel/entities/actors/v1?{params_string}", timeout=30, ok_codes=(200, 401) ) if response.get("errors") and response["errors"][0].get("code") == 401: demisto.info(f'request failed with status code is 401, error: {str(response["errors"][0])}, regenerate token') self._token = self._get_token() self._headers = {"Authorization": "bearer " + self._token} response = self._http_request(method="GET", url_suffix=f"intel/entities/actors/v1?{params_string}", timeout=30) if "resources" not in response: raise DemistoException("Get actors request completed. Parse error: could not find resources in response.") return response["resources"] def fetch_indicators(self, limit: int, offset: int = 0, fetch_command: bool = False, manual_last_run: int = 0) -> list: """Get indicators from CrowdStrike API Args: limit(int): number of indicators to return offset: indicators offset fetch_command: In order not to update last_run time if it is not fetch command manual_last_run: The minimum timestamp to fetch indicators by Returns: (list): parsed indicators """ indicators: list[dict] = [] filter_string = f"({self.filter_string})" if self.filter_string else "" if self.type: type_fql = self.build_type_fql(self.type) filter_string = f"({type_fql})+{filter_string}" if filter_string else f"({type_fql})" if self.malicious_confidence: malicious_confidence_fql = ",".join([f"malicious_confidence:'{item}'" for item in self.malicious_confidence]) filter_string = f"{filter_string}+({malicious_confidence_fql})" if filter_string else f"({malicious_confidence_fql})" if manual_last_run: filter_string = ( f"{filter_string}+(last_updated:>={manual_last_run})" if filter_string else f"(last_updated:>={manual_last_run})" ) if fetch_command: if last_run := self.get_last_run(): filter_string = f"{filter_string}+({last_run})" if filter_string else f"({last_run})" else: filter_string, indicators = self.handle_first_fetch_context_or_pre_2_1_0(filter_string) if indicators: limit = limit - len(indicators) if filter_string or not fetch_command: demisto.debug(f"{filter_string=}") params = assign_params( include_deleted=self.include_deleted, limit=limit, offset=offset, q=self.generic_phrase, filter=filter_string, sort="_marker|asc", ) response = self.get_indicators(params=params) # need to fetch all indicators after the limit if resources := response.get("resources", []): new_last_marker_time = resources[-1].get("_marker") else: new_last_marker_time = demisto.getIntegrationContext().get("last_marker_time") last_marker_time_for_debug = new_last_marker_time or "No data yet" demisto.debug( f"There are no indicators, using last_marker_time={last_marker_time_for_debug} from Integration Context" ) if fetch_command: context = demisto.getIntegrationContext() demisto.info(f"last_marker_time before updating: {context.get('last_marker_time')}") context.update({"last_marker_time": new_last_marker_time}) demisto.setIntegrationContext(context) demisto.info(f"set last_run to: {new_last_marker_time}") indicators.extend( self.create_indicators_from_response( response, self.get_actors_names_request, self.tlp_color, self.feed_tags, self.create_relationships, ) ) return indicators def handle_first_fetch_context_or_pre_2_1_0(self, filter_string: str) -> tuple[str, list[dict]]: """ Checks whether the context integration uses the format used up to version 2_1_0 (when the `last_update` parameter was removed), or whether this is the first time of the fetch, If so, the function imports one indicator and extracts the `_marker` from it to import the following indicators. The function is only called in the following two cases: 1. At the first run of v2.1.0 or newer. 2. In order to transfer the context integration to the new implementation Returns: Tuple: 1. filter_string with the _marker key - str. 2. parse indicator that retrieved - list[dict]. """ filter_for_first_fetch = filter_string if last_run := demisto.getIntegrationContext().get("last_updated") or self.first_fetch: last_run = f"last_updated:>={int(last_run)}" filter_for_first_fetch = f"{filter_string}+({last_run})" if filter_string else f"({last_run})" params = assign_params( include_deleted=self.include_deleted, limit=1, q=self.generic_phrase, filter=filter_for_first_fetch, sort="last_updated|asc", ) response = self.get_indicators(params=params) # In case there is an indicator for extracting the `_marker` # it allows fetching following indicators better. if resources := response.get("resources", []): _marker = resources[-1].get("_marker") demisto.debug(f"Importing the indicator marker in first time: {_marker=}") last_run = f"_marker:>'{_marker}'" parse_indicator = self.create_indicators_from_response( response, self.get_actors_names_request, self.tlp_color, self.feed_tags, self.create_relationships ) filter_string = f"{filter_string}+({last_run})" if filter_string else f"({last_run})" return filter_string, parse_indicator # In case no indicator is returned demisto.debug("No indicator returned") return "", [] @staticmethod def get_last_run() -> str: """Gets last run time in timestamp Returns: last run in timestamp, or '' if no last run. Taken from Integration Context key last_marker_time. """ if last_run := demisto.getIntegrationContext().get("last_marker_time"): demisto.info(f"get last_run: {last_run}") params = f"_marker:>'{last_run}'" else: demisto.debug("There is no last_run (last_marker_time in Integration Context)") params = "" return params @staticmethod def create_indicators_from_response( raw_response, get_actors_names_request_func, tlp_color=None, feed_tags=None, create_relationships=True ) -> list: """Builds indicators from API raw response Args: raw_response: response from crowdstrike API tlp_color: tlp color chosen by customer feed_tags: Feed tags to filter by create_relationships: Whether to create relationships. Returns: (list): list of indicators """ parsed_indicators: list = [] indicator: dict = {} for resource in raw_response["resources"]: if not (type_ := auto_detect_indicator_type_from_cs(resource["indicator"], resource["type"])): demisto.debug(f"Indicator {resource['indicator']} of type {resource['type']} is not supported in XSOAR, skipping") continue indicator = { "type": type_, "value": resource.get("indicator"), "rawJSON": resource, "fields": { "actor": resource.get("actors"), "reports": resource.get("reports"), "malwarefamily": resource.get("malware_families"), "stixkillchainphases": kill_chain_standard_values(resource.get("kill_chains")), "ipaddress": resource.get("ip_address_types"), "domainname": resource.get("domain_types"), "targets": resource.get("targets"), "threattypes": [{"threatcategory": threat} for threat in resource.get("threat_types", [])], "vulnerabilities": resource.get("vulnerabilities"), "confidence": resource.get("malicious_confidence"), "updateddate": resource.get("last_updated"), "creationdate": resource.get("published_date"), "tags": [label.get("name") for label in resource.get("labels")], # type: ignore }, } if tlp_color: indicator["fields"]["trafficlightprotocol"] = tlp_color if feed_tags: indicator["fields"]["tags"].extend(feed_tags) if create_relationships: relationships = create_and_add_relationships(indicator, resource, get_actors_names_request_func) indicator["relationships"] = relationships parsed_indicators.append(indicator) return parsed_indicators @staticmethod def build_type_fql(types_list: list) -> str: """Builds an indicator type query for the filter parameter Args: types_list(list): indicator types that was chosen by user Returns: (str): FQL query containing the relevant indicator types we want to fetch from Crowdstrike """ if "ALL" in types_list: # Replaces "ALL" for all types supported on XSOAR. crowdstrike_types = [f"type:'{type}'" for type in CROWDSTRIKE_TO_XSOAR_TYPES] else: crowdstrike_types = [ f"type:'{XSOAR_TYPES_TO_CROWDSTRIKE.get(type.lower())}'" for type in types_list if type.lower() in XSOAR_TYPES_TO_CROWDSTRIKE ] result = ",".join(crowdstrike_types) return result def create_and_add_relationships(indicator: dict, resource: dict, get_actors_names_request_func) -> list: """ Creates and adds relationships to indicators for each CrowdStrike relationships type. Args: indicator(dict): The indicator in XSOAR format. resource(dict): The indicator from the response. Returns: List of relationships objects. """ relationships = [] for field in CROWDSTRIKE_INDICATOR_RELATION_FIELDS: if resource.get(field): relationships.extend(create_relationships(field, indicator, resource, get_actors_names_request_func)) return relationships def create_relationships(field: str, indicator: dict, resource: dict, get_actors_names_request_func) -> List: """ Creates indicator relationships. Args: field(str): A CrowdStrike indicator field which contains relationships. indicator(dict): The indicator in XSOAR format. resource(dict): The indicator from the response. Returns: List of relationships objects. """ relationships = [] if field == "actors" and resource["actors"]: resource["actors"] = change_actors_from_id_to_name(resource["actors"], get_actors_names_request_func) for relation in resource[field]: if field == "relations" and not CROWDSTRIKE_TO_XSOAR_TYPES.get(relation.get("type")): demisto.debug(f"The related indicator type {relation.get('type')} is not supported in XSOAR.") continue if field == "relations": related_indicator_type = auto_detect_indicator_type_from_cs(relation["indicator"], relation["type"]) relation_name = EntityRelationship.Relationships.RELATED_TO else: related_indicator_type = CROWDSTRIKE_TO_XSOAR_TYPES[field] relation_name = INDICATOR_TO_CROWDSTRIKE_RELATION_DICT[related_indicator_type].get( indicator["type"], indicator["type"] ) indicator_relation = EntityRelationship( name=relation_name, entity_a=indicator["value"], entity_a_type=indicator["type"], entity_b=relation["indicator"] if field == "relations" else relation, entity_b_type=related_indicator_type, reverse_name=EntityRelationship.Relationships.RELATIONSHIPS_NAMES.get(relation_name, ""), ).to_indicator() relationships.append(indicator_relation) return relationships def change_actors_from_id_to_name(indicator_actors_array: List[str], get_name_of_actors__func): integration_context = get_integration_context() actors_to_convert = [] converted_actors_array = [] for actor in indicator_actors_array: if converted_actor := integration_context.get(actor, None): converted_actors_array.append(converted_actor) else: actors_to_convert.append(actor) if actors_to_convert: actor_ids_params = "ids=" + "&ids=".join(actors_to_convert) + "&fields=name" actors_response = get_name_of_actors__func(actor_ids_params) converted_actors_from_request = [] for actor_dict in actors_response: converted_actors_from_request.append(actor_dict.get("name")) zipped_actors_list_to_context = dict(zip(actors_to_convert, converted_actors_from_request)) update_integration_context(zipped_actors_list_to_context) converted_actors_array += converted_actors_from_request return converted_actors_array def auto_detect_indicator_type_from_cs(value: str, crowdstrike_resource_type: str) -> str | None: """ The function determines the type of indicator according to two cases:: 1. In case the type is ip_address then the type is detected by auto_detect_indicator_type function (CSP). 2. In any other case, the type is converted by the table CROWDSTRIKE_TO_XSOAR_TYPES to a type of XSOAR. """ if crowdstrike_resource_type == "ip_address": return auto_detect_indicator_type(value) return CROWDSTRIKE_TO_XSOAR_TYPES.get(crowdstrike_resource_type) def fetch_indicators_command(client: Client): """fetch indicators from the Crowdstrike Intel Args: client: Client object Returns: list of indicators(list) """ parsed_indicators = client.fetch_indicators(fetch_command=True, limit=client.limit) # we submit the indicators in batches for b in batch(parsed_indicators, batch_size=2000): demisto.createIndicators(b) return parsed_indicators def crowdstrike_indicators_list_command(client: Client, args: dict) -> CommandResults: """Gets indicator from Crowdstrike Intel to readable output Args: client: Client object args: demisto.args() Returns: readable_output, raw_response """ offset = arg_to_number(args.get("offset", 0)) or 0 limit = arg_to_number(args.get("limit", 50)) or 50 last_run = arg_to_number(args.get("last_run", 0)) or 0 parsed_indicators = client.fetch_indicators(limit=limit, offset=offset, fetch_command=False, manual_last_run=last_run) if outputs := copy.deepcopy(parsed_indicators): for indicator in outputs: indicator["id"] = indicator.get("rawJSON", {}).get("id") readable_output = tableToMarkdown( name="Indicators from CrowdStrike Falcon Intel", t=outputs, headers=["type", "value", "id"], headerTransform=pascalToSpace, ) return CommandResults( outputs=outputs, outputs_prefix="CrowdStrikeFalconIntel.Indicators", outputs_key_field="id", readable_output=readable_output, raw_response=parsed_indicators, ) else: return CommandResults(readable_output="No Indicators.") def test_module(client: Client, args: dict) -> str: try: client.fetch_indicators(limit=client.limit, fetch_command=False) except Exception: raise Exception("Could not fetch CrowdStrike Indicator Feed\n\nCheck your API key and your connection to CrowdStrike.") return "ok" def reset_last_run(): """ Reset the last run from the integration context """ demisto.setIntegrationContext({}) return CommandResults(readable_output="Fetch history deleted successfully") """ MAIN FUNCTION """ def main() -> None: params = demisto.params() credentials = params.get("credentials") proxy = params.get("proxy", False) insecure = params.get("insecure", False) first_fetch_param = params.get("first_fetch") first_fetch_datetime = arg_to_datetime(first_fetch_param) if first_fetch_param else None first_fetch = first_fetch_datetime.timestamp() if first_fetch_datetime else None base_url = params.get("base_url") tlp_color = params.get("tlp_color") include_deleted = params.get("include_deleted", False) type = argToList(params.get("type"), "ALL") malicious_confidence = argToList(params.get("malicious_confidence")) filter_string = params.get("filter") generic_phrase = params.get("generic_phrase") max_fetch = arg_to_number(params.get("max_indicator_to_fetch")) if params.get("max_indicator_to_fetch") else 10000 max_fetch = min(max_fetch, 10000) # type: ignore feed_tags = argToList(params.get("feedTags")) create_relationships = params.get("create_relationships", True) timeout = params.get("timeout") args = demisto.args() try: command = demisto.command() demisto.info(f"Command being called is {demisto.command()}") client = Client( credentials=credentials, base_url=base_url, insecure=insecure, proxy=proxy, tlp_color=tlp_color, feed_tags=feed_tags, include_deleted=include_deleted, type=type, malicious_confidence=malicious_confidence, filter_string=filter_string, generic_phrase=generic_phrase, limit=max_fetch, first_fetch=first_fetch, create_relationships=create_relationships, timeout=timeout, ) if command == "test-module": # This is the call made when pressing the integration Test button. result = test_module(client, args) return_results(result) elif command == "fetch-indicators": fetch_indicators_command(client=client) elif command == "crowdstrike-indicators-list": return_results(crowdstrike_indicators_list_command(client, args)) elif command == "crowdstrike-reset-fetch-indicators": return_results(reset_last_run()) # Log exceptions and return errors except Exception as e: return_error(f"Failed to execute {demisto.command()} command.\nError:\n{e!s}") """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): main()