-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1188 from nicoctn/falcon_responder_v2
FalconCrowstrikeCustomIOC Responder v2
- Loading branch information
Showing
4 changed files
with
257 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
FROM python:2 | ||
FROM python:3 | ||
|
||
WORKDIR /worker | ||
COPY . FalconCustomIOC | ||
RUN pip install --no-cache-dir -r FalconCustomIOC/requirements.txt | ||
ENTRYPOINT FalconCustomIOC/FalconCustomIOC.py | ||
ENTRYPOINT FalconCustomIOC/FalconCustomIOCv2.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
{ | ||
"name": "Crowdstrike_Falcon_Custom_IOC", | ||
"version": "2.0", | ||
"author": "Nicolas Criton", | ||
"url": "https://www.crowdstrike.com/blog/tech-center/consume-ioc-and-threat-feeds/", | ||
"license": "AGPL-v3", | ||
"description": "Submit observables to the Crowdstrike Falcon Custom IOC API", | ||
"dataTypeList": ["thehive:alert","thehive:case_artifact"], | ||
"command": "FalconCustomIOC/FalconCustomIOCv2.py", | ||
"baseConfig": "FalconCustomIOCv2", | ||
"configurationItems": [ | ||
{ | ||
"name": "falconapi_endpoint", | ||
"description": "CrowdStrike API endpoints: US-1 | US-2 | US-GOV-1 | EU-1", | ||
"type": "string", | ||
"multi": false, | ||
"required": true | ||
}, | ||
{ | ||
"name": "falconapi_clientid", | ||
"description": "Crowdstrike Falcon Client ID Oauth2 API client", | ||
"type": "string", | ||
"multi": false, | ||
"required": true | ||
}, | ||
{ | ||
"name": "falconapi_key", | ||
"description": "Crowdstrike Falcon Oauth2 API Key", | ||
"type": "string", | ||
"multi": false, | ||
"required": true | ||
}, | ||
{ | ||
"name": "domain_block_expiration_days", | ||
"description": "How many days should we block the domain IOCs sent? Default: 30", | ||
"type": "number", | ||
"multi": false, | ||
"required": false, | ||
"defaultValue": 30 | ||
}, | ||
{ | ||
"name": "ip_block_expiration_days", | ||
"description": "How many days should we block the ip IOCs sent? Default: 30", | ||
"type": "number", | ||
"multi": false, | ||
"required": false, | ||
"defaultValue": 30 | ||
}, | ||
{ | ||
"name": "hash_block_expiration_days", | ||
"description": "How many days should we block the hash IOCs sent? Default: 30", | ||
"type": "number", | ||
"multi": false, | ||
"required": false, | ||
"defaultValue": 30 | ||
}, | ||
{ | ||
"name": "action_to_take", | ||
"description": "How the IOCs should be handled by Falcon ? Choose between 'no_action' or 'detect' -> no_action: Save the indicator for future use, but take no action / detect: Enable detections for the indicator at the selected severity (Default: detect)", | ||
"type": "string", | ||
"multi": false, | ||
"required": false, | ||
"defaultValue": "detect" | ||
}, | ||
{ | ||
"name": "severity_level", | ||
"description": "Severity level when IOCs are ingested by Falcon CustomIOC: informational / low / medium / high / critical - Default: high", | ||
"type": "string", | ||
"multi": false, | ||
"required": false, | ||
"defaultValue": "high" | ||
}, | ||
{ | ||
"name": "tag_added_to_cs", | ||
"description": "Tag added to the IOC in Falcon platform - Default: Cortex Incident - FalconCustomIOC", | ||
"type": "string", | ||
"multi": false, | ||
"required": false, | ||
"defaultValue": "Cortex Incident - FalconCustomIOC" | ||
}, | ||
{ | ||
"name": "tag_added_to_thehive", | ||
"description": "Tag added to the IOC in TheHive platform - Default: Falcon:Custom IOC Uploaded", | ||
"type": "string", | ||
"multi": false, | ||
"required": false, | ||
"defaultValue": "Falcon:Custom IOC Uploaded" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
|
||
import requests | ||
import re | ||
import json | ||
import ipaddress | ||
|
||
from cortexutils.responder import Responder | ||
from cortexutils.extractor import Extractor | ||
from falconpy import OAuth2, IOC | ||
from dateutil.relativedelta import relativedelta | ||
from datetime import datetime | ||
|
||
|
||
class FalconCustomIOC(Responder): | ||
def __init__(self): | ||
Responder.__init__(self) | ||
self.falconapi_endpoint = self.get_param( | ||
"config.falconapi_endpoint", None, "Falcon API Endpoint: US-1 | US-2 | US-GOV-1 | EU-1", | ||
) | ||
self.falconapi_clientid = self.get_param( | ||
"config.falconapi_clientid", None, "Falcon clientid missing" | ||
) | ||
self.falconapi_key = self.get_param( | ||
"config.falconapi_key", None, "Falcon api key missing" | ||
) | ||
self.domain_block_expiration_days = self.get_param( | ||
"config.domain_block_expiration_days", 30 | ||
) | ||
self.ip_block_expiration_days = self.get_param( | ||
"config.ip_block_expiration_days", 30 | ||
) | ||
self.hash_block_expiration_days = self.get_param( | ||
"config.hash_block_expiration_days", 30 | ||
) | ||
self.action_to_take = self.get_param( | ||
"config.action_to_take", "detect" | ||
) | ||
self.severity_level = self.get_param( | ||
"config.severity_level", "high" | ||
) | ||
self.tag_added_to_cs = self.get_param( | ||
"config.tag_added_to_cs", "Cortex Incident - FalconCustomIOC" | ||
) | ||
self.tag_added_to_thehive = self.get_param( | ||
"config.tag_added_to_thehive", "CrowdStrike:Custom IOC Uploaded" | ||
) | ||
|
||
def run(self): | ||
try: | ||
Responder.run(self) | ||
ioctypes = { | ||
"hash": "sha256", | ||
"sha256": "sha256", | ||
"md5": "md5", | ||
"ip": "ipv4", | ||
"ipv4": "ipv4", | ||
"ip6": "ipv6", | ||
"ipv6": "ipv6", | ||
"domain": "domain", | ||
"url": "domain", | ||
} | ||
|
||
data_type = self.get_param("data.dataType") | ||
if not data_type in ioctypes: | ||
self.error("Unsupported IOC type") | ||
return False | ||
ioc = self.get_param("data.data", None, "No IOC provided") | ||
|
||
if data_type == "url": | ||
match = re.match(r"(http:\/\/|https:\/\/)?([\w\-\.]{0,256}).*", ioc) | ||
if match is None or match.group(2) is None: | ||
self.error("Could not parse iocs from URL") | ||
return False | ||
else: | ||
ioc = match.group(2) | ||
data_type = Extractor().check_string(ioc) | ||
|
||
if data_type == "ip": | ||
try: | ||
ip_check = ipaddress.ip_address(ioc) | ||
except Exception as e: | ||
self.error(f"Could not check IP type from IOC : {e}") | ||
return False | ||
if isinstance(ip_check, ipaddress.IPv6Address): | ||
data_type = "ipv6" | ||
elif isinstance(ip_check, ipaddress.IPv4Address): | ||
data_type = "ipv4" | ||
else: | ||
self.error("Could not determine IP type from IOC") | ||
return False | ||
|
||
if data_type == "hash": | ||
if len(ioc) == 32: | ||
data_type = "md5" | ||
elif len(ioc) == 40: | ||
self.error("Unsupported IOC type") | ||
return False | ||
elif len(ioc) == 64: | ||
data_type = "sha256" | ||
|
||
if data_type in ("fqdn", "domain"): | ||
expiration_date = datetime.today() + relativedelta(days=self.domain_block_expiration_days) | ||
elif data_type in ("ip", "ipv4", "ipv6", "ip6"): | ||
expiration_date = datetime.today() + relativedelta(days=self.ip_block_expiration_days) | ||
elif data_type in ("hash", "sha256", "md5"): | ||
expiration_date = datetime.today() + relativedelta(days=self.hash_block_expiration_days) | ||
expiration = expiration_date.strftime("%Y-%m-%dT%H:%M:%SZ") | ||
|
||
incident_title = self.get_param("data.case.title", None, "Can't get case title").encode("utf-8")[:128] | ||
|
||
auth = OAuth2( | ||
client_id=self.falconapi_clientid, | ||
client_secret=self.falconapi_key, | ||
base_url=self.falconapi_endpoint | ||
) | ||
|
||
falcon_api = IOC(auth_object=auth) | ||
response = falcon_api.indicator_create(action=self.action_to_take, | ||
applied_globally=True, | ||
comment="TheHive IOC incident", | ||
description=incident_title.decode("utf-8"), | ||
expiration=expiration, | ||
filename="", | ||
ignore_warnings=False, | ||
platforms='mac,windows,linux', | ||
severity=self.severity_level, | ||
source="Cortex - FalconCustomIOC [" + incident_title.decode("utf-8") + "]", | ||
tags=self.tag_added_to_cs, | ||
type=ioctypes[data_type], | ||
value=ioc.strip() | ||
) | ||
|
||
response_error = str(response['body']['errors']) | ||
response_ressources = str(response['body']['resources']) | ||
|
||
if response['body']['errors'] is None: | ||
self.report( | ||
{"message": f"{ioc} successuflly submitted to Crowdstrike Falcon custom IOC api - status code: {response['status_code']}"} | ||
) | ||
elif 'Duplicate type' in response_ressources: | ||
self.error(f"Not submitted because of duplicated entry - {ioc} already found on your Falcon CustomIOC database") | ||
return False | ||
else: | ||
self.error(f"Error: unable to complete action - received {response['status_code']} status code from FalconIOC API with the following message: {response_error}") | ||
return False | ||
|
||
except Exception as ex: | ||
self.error(f"Unable to send IOC to FalconCustomIOC API: {ex}") | ||
return False | ||
return True | ||
|
||
def operations(self, raw): | ||
return [ | ||
self.build_operation( | ||
"AddTagToArtifact", tag=self.tag_added_to_thehive | ||
) | ||
] | ||
|
||
if __name__ == "__main__": | ||
FalconCustomIOC().run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
cortexutils | ||
requests | ||
crowdstrike-falconpy | ||
datetime | ||
python-dateutil |