Skip to content

Commit

Permalink
[crowdstrike] add call detects scope and refact
Browse files Browse the repository at this point in the history
  • Loading branch information
savacano28 committed Jun 27, 2024
1 parent 61bcd19 commit ca0ebd8
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 20 deletions.
59 changes: 56 additions & 3 deletions crowdstrike/src/collector/crowdstrike_api_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from falconpy import IOC as CsIOC
from falconpy import Alerts as CsAlerts
from falconpy import Incidents as CsIncidents
from falconpy import Detects as CsDetects

from .utils import Utils

Expand All @@ -18,13 +18,13 @@ def _init_service(self, service_class):
return service_class(
client_id=self.client_id,
client_secret=self.client_secret,
base_url=self.base_url
base_url=self.base_url,
)

def _auth(self):
self.ioc = self._init_service(CsIOC)
self.alerts = self._init_service(CsAlerts)
self.incidents = self._init_service(CsIncidents)
self.detections = self._init_service(CsDetects)

def extract_iocs(self):
response = self.ioc.indicator_combined_v1()
Expand All @@ -45,3 +45,56 @@ def extract_iocs(self):
extracted_iocs.append(Utils.format_ioc("Subdomains", ioc))

return extracted_iocs

def extract_alerts(self, start_time):
try:
parameters = {
"filter": f"timestamp:>'{start_time.isoformat()}'",
}
response = self.alerts.query_alerts(parameters=parameters)

alerts = []

if response["status_code"] == 200:
alert_ids = response["body"]["resources"]

# Get detailed information for each alert
for alert_id in alert_ids:
detail_response = self.alerts.get_details(ids=[alert_id])
if detail_response["status_code"] == 200:
alerts.append(detail_response["body"]["resources"][0])
else:
print(
f"Failed to get details for alert ID {alert_id}: {detail_response}"
)
else:
print("Failed to query alerts:", response)
return alerts
except Exception as e:
print(f"Error: {e}")

def extract_detects(self, start_time):
try:
parameters = {
"filter": f"created_timestamp:>'{start_time.isoformat()}'",
"limit": 10,
}

# Make the API call to retrieve detections
response = self.detections.query_detects(parameters=parameters)
detections = []

if response["status_code"] == 200:
detection_ids = response["body"]["resources"]
for detection_id in detection_ids:
detection_details = self.detections.get_details(ids=[detection_id])
if detection_details["status_code"] == 200:
detections.append(detection_details["body"]["resources"][0])
else:
print(f"Failed to get details for detection ID: {detection_id}")
else:
print("Failed to retrieve detections:", response)

return detections
except Exception as e:
print("Error:", e)
56 changes: 39 additions & 17 deletions crowdstrike/src/collector/openbas_crowdstrike.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from datetime import datetime
from datetime import datetime, timedelta

import pytz
from dateutil.parser import parse
from dateutil.relativedelta import relativedelta

from pyobas.helpers import OpenBASCollectorHelper, OpenBASConfigHelper

from .crowdstrike_api_handler import CrowdstrikeApiHandler


Expand Down Expand Up @@ -71,7 +70,7 @@ def __init__(self):
self.config.get_conf("crowdstrike_api_base_url"),
)

def _fetch_expectations(self):
def _fetch_expectations(self, start_time):
self.helper.collector_logger.info("Gathering expectations for executed injects")
expectations = (
self.helper.api.inject_expectation.expectations_assets_for_source(
Expand All @@ -81,7 +80,6 @@ def _fetch_expectations(self):
self.helper.collector_logger.info(
"Found " + str(len(expectations)) + " expectations waiting to be matched"
)
limit_date = datetime.now().astimezone(pytz.UTC) - relativedelta(days=45)

valid_expectations = []

Expand All @@ -91,8 +89,8 @@ def _fetch_expectations(self):
expectation["inject_expectation_created_at"]
).astimezone(pytz.UTC)

# Check if the expectation is expired
if expectation_date < limit_date:
# Check if the expectation is expired: created date is greater than request start time
if expectation_date < start_time:
self.helper.collector_logger.info(
f"Expectation expired, failing inject {expectation['inject_expectation_inject']} "
f"({expectation['inject_expectation_type']})"
Expand All @@ -115,23 +113,47 @@ def _fetch_expectations(self):

return valid_expectations

def _match_expectations(self, valid_expectations):
def _extract_ip_addresses(self, detections):
ips = []
for detection in detections:
if "device" in detection:
device = detection["device"]
if "local_ip" in device:
ips.append(device["local_ip"])
if "external_ip" in device:
ips.append(device["external_ip"])
return ips

def _match_expectations(self, valid_expectations, start_time):
try:
iocs = self.crowdstrike_api_handler.extract_iocs()
print(iocs)
# Process alerts and incidents if needed
print("iocs :", iocs)
alerts = self.crowdstrike_api_handler.extract_alerts(start_time)
print("alerts : ", alerts)
detections = self.crowdstrike_api_handler.extract_detects(start_time)
print("detections : ", detections)

ips = self._extract_ip_addresses(detections)
print(ips)

# Logic to match expectations
for expectation in valid_expectations:
self.helper.collector_logger.info(
f"Processing expectation: {expectation}"
)
# Match with detections from cs

except Exception as e:
print(f"Error matching expectations: {e}")

# Implement the logic to match expectations
for expectation in valid_expectations:
self.helper.collector_logger.info(f"Processing expectation: {expectation}")
# Add your processing logic here

def _process(self):
"""Fetch and match expectations with data from cs"""
valid_expectations = self._fetch_expectations()
self._match_expectations(valid_expectations)
# Calculate the time 45 minutes ago
now = datetime.now(pytz.UTC)
start_time = now - timedelta(minutes=15)

valid_expectations = self._fetch_expectations(start_time)
self._match_expectations(valid_expectations, start_time)

def start(self):
period = self.config.get_conf("collector_period", True, 60)
Expand Down

0 comments on commit ca0ebd8

Please sign in to comment.