-
Notifications
You must be signed in to change notification settings - Fork 379
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 #1297 from TheHive-Project/csfalcon-integration-1
Multiple Analyzers & Responders for CrowdstrikeFalcon
- Loading branch information
Showing
62 changed files
with
4,064 additions
and
0 deletions.
There are no files selected for viewing
73 changes: 73 additions & 0 deletions
73
analyzers/CrowdstrikeFalcon/CrowdstrikeFalcon_GetDeviceVulnerabilities.json
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,73 @@ | ||
{ | ||
"name": "CrowdstrikeFalcon_GetDeviceVulnerabilities", | ||
"version": "1.0", | ||
"author": "nusantara-self, StrangeBee", | ||
"url": "https://github.com/TheHive-Project/Cortex-Analyzers", | ||
"license": "AGPL-V3", | ||
"baseConfig": "CrowdstrikeFalcon", | ||
"config": { | ||
"check_tlp": false, | ||
"max_tlp": 3, | ||
"service": "" | ||
}, | ||
"description": "Get device vulnerabilities from hostname", | ||
"dataTypeList": [ | ||
"hostname" | ||
], | ||
"command": "CrowdstrikeFalcon/CrowdstrikeFalcon_getDeviceVulnerabilities.py", | ||
"configurationItems": [ | ||
{ | ||
"name": "client_id", | ||
"description": "Crowdstrike client ID key", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "" | ||
}, | ||
{ | ||
"name": "client_secret", | ||
"description": "Crowdstrike client secret key", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "" | ||
}, | ||
{ | ||
"name": "vuln_fields", | ||
"description": "Specific field values to keep in resulting payload for vulnerabilities", | ||
"type": "string", | ||
"multi": true, | ||
"required": true, | ||
"defaultValue": [ | ||
"vulnerability_id", | ||
"status", | ||
"created_timestamp", | ||
"updated_timestamp", | ||
"apps.product_name_version", | ||
"confidence", | ||
"cve", | ||
"host_info.asset_criticality", | ||
"host_info.internet_exposure", | ||
"remediation.entities.action" | ||
] | ||
} | ||
], | ||
"registration_required": true, | ||
"subscription_required": true, | ||
"free_subscription": false, | ||
"service_homepage": "https://www.crowdstrike.com", | ||
"service_logo": { | ||
"path": "assets/crowdstrike.png", | ||
"caption": "Crowdstrike logo" | ||
}, | ||
"screenshots": [ | ||
{ | ||
"path": "assets/short-report-vulns.png", | ||
"caption": "Crowdstrike: Short report template" | ||
}, | ||
{ | ||
"path": "assets/long-report-vulns.png", | ||
"caption": "Crowdstrike: Long report template" | ||
} | ||
] | ||
} |
114 changes: 114 additions & 0 deletions
114
analyzers/CrowdstrikeFalcon/CrowdstrikeFalcon_GetDeviceVulnerabilities.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,114 @@ | ||
#!/usr/bin/env python3 | ||
# encoding: utf-8 | ||
from cortexutils.analyzer import Analyzer | ||
from falconpy import OAuth2 | ||
from falconpy import Hosts | ||
from falconpy import SpotlightVulnerabilities | ||
|
||
|
||
class CrowdstrikeFalcon_GetDeviceVulnerabilities(Analyzer): | ||
def __init__(self): | ||
Analyzer.__init__(self) | ||
self.client_id = self.get_param("config.client_id") | ||
self.client_secret = self.get_param("config.client_secret") | ||
self.vuln_fields = self.get_param("config.vuln_fields", []) | ||
|
||
|
||
|
||
def run(self): | ||
Analyzer.run(self) | ||
if self.data_type == 'hostname': | ||
try: | ||
auth = OAuth2(client_id=self.client_id, client_secret=self.client_secret) | ||
hosts = Hosts(auth_object=auth) | ||
hostname = self.get_data() | ||
|
||
# Search for the device ID using the hostname | ||
response = hosts.query_devices_by_filter(filter=f"hostname:'{hostname}'") | ||
device_ids = response["body"]["resources"] | ||
|
||
# Check the response | ||
status_code = response["status_code"] | ||
if status_code != 200 : | ||
self.error(f"No devices found with hostname: {hostname} -- {status_code}") | ||
|
||
if device_ids: | ||
device_id = device_ids[0] | ||
# Get detailed asset information using the device ID | ||
spotlight = SpotlightVulnerabilities(auth_object=auth) | ||
host_vulns = spotlight.query_vulnerabilities_combined(parameters={"filter": f"aid:'{device_id}'+status:!'closed'"}) | ||
host_vulns = host_vulns["body"]["resources"] | ||
#print(host_vulns) | ||
vuln_details = [] | ||
products_with_vulns = {} | ||
for vuln in host_vulns: | ||
product_name = vuln["apps"][0]["product_name_normalized"] | ||
vuln_id = vuln["id"] | ||
|
||
if product_name not in products_with_vulns: | ||
products_with_vulns[product_name] = [] | ||
|
||
products_with_vulns[product_name].append(vuln_id) | ||
for key, vuln_ids in products_with_vulns.items(): | ||
for vuln_id in vuln_ids: | ||
request = spotlight.get_vulnerabilities(vuln_id) | ||
data = request["body"]["resources"][0] | ||
# Filter the dictionary | ||
#filtered_data = {key: data[key] for key in top_10_keys if key in data} | ||
filtered_data = self.filter_dict(data, self.vuln_fields) | ||
vuln_details.append(filtered_data) | ||
self.report({"message": vuln_details}) | ||
except Exception as e: | ||
self.unexpectedError(e) | ||
else: | ||
self.notSupported() | ||
|
||
def filter_dict(self, d, keys): | ||
filtered = {} | ||
for key in keys: | ||
parts = key.split(".") | ||
if len(parts) == 3: | ||
main_key, sub_key, sub_sub_key = parts | ||
if main_key in d and sub_key in d[main_key]: | ||
filtered.setdefault(main_key, {}).setdefault(sub_key, []) | ||
for entity in d[main_key][sub_key]: | ||
filtered[main_key][sub_key].append({sub_sub_key: entity[sub_sub_key]}) | ||
elif len(parts) == 2: | ||
main_key, sub_key = parts | ||
if main_key in d and sub_key in d[main_key]: | ||
filtered.setdefault(main_key, {})[sub_key] = d[main_key][sub_key] | ||
elif len(parts) == 1: | ||
main_key = parts[0] | ||
if main_key in d: | ||
filtered[main_key] = d[main_key] | ||
return filtered | ||
|
||
def summary(self, raw): | ||
taxonomies = [] | ||
level = "safe" | ||
namespace = "CSFalcon" | ||
predicate = "VulnDetails" | ||
|
||
count_vulns = len(raw["message"]) | ||
if count_vulns > 0: | ||
level = "suspicious" | ||
for vuln in raw["message"]: | ||
if vuln["cve"]["base_score"] >= 7: | ||
level = "malicious" | ||
|
||
# Build summary | ||
taxonomies.append( | ||
self.build_taxonomy( | ||
level, namespace, predicate, str(count_vulns) | ||
) | ||
) | ||
return {"taxonomies": taxonomies} | ||
|
||
def artifacts(self, raw): | ||
artifacts = [] | ||
#artifacts.append(self.build_artifact("ip",raw["external_ip"],tags=["hostname=" + raw["hostname"], "external_ip"])) | ||
return artifacts | ||
|
||
|
||
if __name__ == "__main__": | ||
CrowdstrikeFalcon_GetDeviceVulnerabilities().run() |
121 changes: 121 additions & 0 deletions
121
analyzers/CrowdstrikeFalcon/CrowdstrikeFalcon_Sandbox.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,121 @@ | ||
#!/usr/bin/env python3 | ||
# encoding: utf-8 | ||
from os.path import basename | ||
from cortexutils.analyzer import Analyzer | ||
from falconpy import FalconXSandbox, SampleUploads, OAuth2 | ||
import time | ||
|
||
|
||
|
||
class CrowdstrikeFalcon_Sandbox(Analyzer): | ||
def __init__(self): | ||
Analyzer.__init__(self) | ||
# filename of the observable | ||
self.filename = self.getParam("attachment.name", "noname.ext") | ||
self.filepath = self.getParam("file", None, "File is missing") | ||
self.client_id = self.get_param("config.client_id") | ||
self.client_secret = self.get_param("config.client_secret") | ||
self.environment = self.get_param("config.service", 160) | ||
self.network_settings = self.get_param("config.network_settings", "default") | ||
self.action_script = self.get_param("config.action_script", "default") | ||
|
||
def run(self): | ||
Analyzer.run(self) | ||
|
||
# file analysis | ||
if self.data_type == 'file': | ||
filepath = self.get_param('file', None, 'File is missing') | ||
filename = self.get_param('filename', basename(filepath)) | ||
comment = f"Submitted from TheHive" | ||
# additional_params = { | ||
# "action_script": "default", | ||
# "command_line": "", | ||
# "document_password": "", | ||
# "environment_id": 160, | ||
# "network_settings": "default", | ||
# "send_email_notifications": False, | ||
# "submit_name": filename, | ||
# "submit_date": "2024-08-01", | ||
# "submit_time": "12:00:00", | ||
# "user_tags": ["test", "sample"] | ||
# } | ||
|
||
additional_params = { | ||
"environment_id": self.environment, | ||
"submit_name": filename, | ||
"network_settings": self.network_settings, | ||
"action_script": self.action_script | ||
} | ||
|
||
with open(filepath, "rb") as sample: | ||
auth = OAuth2(client_id=self.client_id, client_secret=self.client_secret) | ||
samples = SampleUploads(auth_object=auth) | ||
sandbox = FalconXSandbox(auth_object=auth) | ||
response = samples.upload_sample(file_data=sample.read(), | ||
file_name=filename, | ||
comment=comment, | ||
is_confidential=True | ||
) | ||
#response = falconx.query_sample() | ||
|
||
#response = falconx.submit(file_name=filename, file_data=sample, **additional_params) | ||
|
||
# Check the response | ||
if response["status_code"] in [200, 201] : | ||
#message = f"File uploaded successfully! Submission ID : {response['body']["resources"]}" | ||
sha256 = response['body']["resources"][0]["sha256"] | ||
submit_response = sandbox.submit(body={ | ||
"sandbox": [{ | ||
"sha256": sha256, | ||
**additional_params | ||
}] | ||
}) | ||
|
||
message = f"File submitted successfully for ! Submission ID : {submit_response}" | ||
|
||
## Check status of on-going scan | ||
status = "running" | ||
while status == "running": | ||
submit_id = submit_response["body"]["resources"][0]["id"] | ||
scan_status = sandbox.get_submissions(ids=submit_id) | ||
if scan_status["body"]["resources"]: | ||
status = scan_status["body"]["resources"][0]["state"] | ||
|
||
analysis_result = sandbox.get_reports(ids=submit_id) | ||
message = analysis_result['body'] | ||
else: | ||
self.error(f"Error uploading file: {response} and {filepath} and {sample} and {filename}") | ||
self.report(message) | ||
else: | ||
self.error("Datatype is not file") | ||
|
||
def summary(self, raw): | ||
taxonomies = [] | ||
|
||
level = "info" | ||
namespace = "CSFalcon" | ||
predicate = "Sandbox" | ||
|
||
value = raw["resources"][0]["verdict"] | ||
|
||
if value == "suspicious": | ||
level = "suspicious" | ||
elif value == "malicious": | ||
level = "malicious" | ||
elif value == "no specific threat": | ||
level = "safe" | ||
|
||
# Build summary | ||
taxonomies.append( | ||
self.build_taxonomy( | ||
level, namespace, predicate, value | ||
) | ||
) | ||
return {"taxonomies": taxonomies} | ||
|
||
def artifacts(self, raw): | ||
artifacts = [] | ||
return artifacts | ||
|
||
if __name__ == "__main__": | ||
CrowdstrikeFalcon_Sandbox().run() |
70 changes: 70 additions & 0 deletions
70
analyzers/CrowdstrikeFalcon/CrowdstrikeFalcon_Sandbox_Android.json
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,70 @@ | ||
{ | ||
"name": "CrowdstrikeFalcon_Sandbox_Android", | ||
"version": "1.0", | ||
"author": "nusantara-self, StrangeBee", | ||
"url": "https://github.com/TheHive-Project/Cortex-Analyzers", | ||
"license": "AGPL-V3", | ||
"baseConfig": "CrowdstrikeFalcon", | ||
"config": { | ||
"check_tlp": false, | ||
"max_tlp": 3, | ||
"service": 200 | ||
}, | ||
"description": "Send a file to CrowdstrikeFalcon Sandbox", | ||
"dataTypeList": [ | ||
"file" | ||
], | ||
"command": "CrowdstrikeFalcon/CrowdstrikeFalcon_Sandbox.py", | ||
"configurationItems": [ | ||
{ | ||
"name": "client_id", | ||
"description": "Crowdstrike client ID key", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "" | ||
}, | ||
{ | ||
"name": "client_secret", | ||
"description": "Crowdstrike client secret key", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "" | ||
}, | ||
{ | ||
"name": "network_settings", | ||
"description": "Specifies the sandbox network_settings used for analysis : default, tor, simulated, offline", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "default" | ||
}, | ||
{ | ||
"name": "action_script", | ||
"description": "Runtime script for sandbox analysis : default, default_randomtheme, default_maxantievasion, default_openie, default_randomfiles", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "default" | ||
} | ||
], | ||
"registration_required": true, | ||
"subscription_required": true, | ||
"free_subscription": false, | ||
"service_homepage": "https://www.crowdstrike.com", | ||
"service_logo": { | ||
"path": "assets/crowdstrike.png", | ||
"caption": "Crowdstrike logo" | ||
}, | ||
"screenshots": [ | ||
{ | ||
"path": "assets/short-report-sandbox.png", | ||
"caption": "Crowdstrike: Short report template" | ||
}, | ||
{ | ||
"path": "assets/long-report-sandbox.png", | ||
"caption": "Crowdstrike: Long report template" | ||
} | ||
] | ||
} |
Oops, something went wrong.