Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FalconCrowstrikeCustomIOC Responder v2 #1188

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions responders/FalconCustomIOC/Dockerfile
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
90 changes: 90 additions & 0 deletions responders/FalconCustomIOC/FalconCustomIOCv2.json
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"
}
]
}
162 changes: 162 additions & 0 deletions responders/FalconCustomIOC/FalconCustomIOCv2.py
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()
4 changes: 3 additions & 1 deletion responders/FalconCustomIOC/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
cortexutils
requests
crowdstrike-falconpy
datetime
python-dateutil