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

Added a Analyzer for Crowdstrikes Falcon X Sandbox #797

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
35 changes: 35 additions & 0 deletions analyzers/FalconSandbox/FalconSandbox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "FalconSandbox",
"version": "1.0",
"author": "Sebastian Schmerl - Computacenter",
"url": "https://www.crowdstrike.com/blog/tech-center/get-access-falcon-apis/",
"license": "AGPL-v3",
"description": "Submit observables to the Crowdstrike FalconX Sandbox",
"dataTypeList": ["file"],
"command": "FalconSandbox/FalconSandbox.py",
"baseConfig": "FalconSandbox",
"configurationItems": [
{
"name": "API_Base_Url",
"description": "Crowdstrike Api Base Url",
"type": "string",
"multi": false,
"required": true,
"default":"https://api.crowdstrike.com"
},
{
"name": "Client_ID",
"description": "Crowdstrike Api ClientID",
"type": "string",
"multi": false,
"required": true
},
{
"name": "Client_Secret",
"description": "Crowdstrike Api Client Secret",
"type": "string",
"multi": false,
"required": true
}
]
}
177 changes: 177 additions & 0 deletions analyzers/FalconSandbox/FalconSandbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#!/usr/bin/env python3
# encoding: utf-8


import hashlib
import json
import time
import traceback

from cortexutils.analyzer import Analyzer
from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session


class FalconSandbox(Analyzer):
def __init__(self):
Analyzer.__init__(self)
self.Client_ID = self.get_param(
'config.Client_ID', None, "Falcon ClientID missing")
self.Client_Secret = self.get_param(
'config.Client_Secret', None, "Falcon Client_Secret missing")
self.API_Base_Url = self.get_param(
'config.API_Base_Url', None, "Falcon API Base URl missing")
self.client = BackendApplicationClient(client_id=self.Client_ID)
self.oauth = OAuth2Session(client=self.client)
self.token = self.oauth.fetch_token(token_url=self.API_Base_Url + "/oauth2/token", client_id=self.Client_ID, client_secret=self.Client_Secret)

def summary(self, raw):
taxonomies = []
namespace = "CS-Sandbox"
predicate = "Threat-Score"
value = str(raw['resources'][0]['sandbox'][0]['threat_score'])
level = str(
raw['resources'][0]['sandbox'][0]['verdict']) # no verdict, No Specific Threat, suspicious, malicious

taxonomies.append(self.build_taxonomy(level, namespace, predicate, value))
return {"taxonomies": taxonomies}

def calc_sha256_hash(self, filepath):
buf_size = 65536 # lets read stuff in 64kb chunks!
l_sha256 = hashlib.sha256()
with open(filepath, 'rb') as f:
while True:
l_data = f.read(buf_size)
if not l_data:
break
l_sha256.update(l_data)

l_own_sha256 = l_sha256.hexdigest()
return l_own_sha256

def rest_api_request(self, method, url, headers, data):
# get the get, post or delete method from the OAuth2Session object
method_to_call = getattr(self.oauth, method)

response = method_to_call(url, data=data, headers=headers)
l_json_response = json.loads(response.text)

if l_json_response["errors"] and l_json_response["errors"][0]["code"] == 403:
# ok, we need a new token
self.client = BackendApplicationClient(client_id=self.Client_ID)
self.oauth = OAuth2Session(client=self.client)
self.token = self.oauth.fetch_token(token_url=self.API_Base_Url + "/oauth2/token",
client_id=self.Client_ID, client_secret=self.Client_Secret)
# lets do it again with new token
response = method_to_call(url, data=data, headers=headers)
l_json_response = json.loads(response.text)

return l_json_response

def run(self):
try:
Analyzer.run(self)

filename = self.get_param("filename", "")
filepath = self.get_param("file", "")

l_file_hash_sha256 = self.calc_sha256_hash(filepath)

# get the latest report for file with sha256
url = self.API_Base_Url + \
"/falconx/queries/submissions/v1?filter=sandbox.sha256:\"" + \
l_file_hash_sha256 + \
"\"&limit=1"
payload = {}
headers = {'Content-Type': 'application/json'}
json_response = self.rest_api_request(method="get", url=url, headers=headers, data=payload)

if json_response["errors"]:
self.error(str(json_response["errors"]))
return
else:
if not json_response["resources"]:
# no scan reports exists for this file -> submit the file for analysis
payload = open(filepath, "rb")
headers = {'Content-Type': 'application/octet-stream'}
url = self.API_Base_Url + \
"/samples/entities/samples/v2?file_name=" + \
filename + \
"&comment=" + \
"added by TheHive:FalconSandbox-Analyzer"
json_response_submit = self.rest_api_request(method="post", url=url, data=payload, headers=headers)
if json_response_submit["errors"]:
self.error(str(json_response_submit["errors"]))
return
else:
# start the analysis of the submitted file
url = self.API_Base_Url + \
"/falconx/entities/submissions/v1"
headers = {'Content-Type': 'application/json'}
payload = "{\"sandbox\": " \
"[{\"sha256\": \"" + \
l_file_hash_sha256 + \
"\",\"environment_id\": 110 }] }"
json_response_start_analysis = self.rest_api_request(method="post", url=url, data=payload,
headers=headers)
if json_response_start_analysis["errors"]:
self.error(str(json_response_start_analysis["errors"]))
return
else:
# now the file is submitted and analysis is started, let's get the report_id now
url = self.API_Base_Url + \
"/falconx/queries/submissions/v1?filter=sandbox.sha256:\"" \
+ l_file_hash_sha256 + \
"\"&limit=1"
payload = {}
headers = {'Content-Type': 'application/json'}
report_found = 0
while report_found == 0:
json_response = self.rest_api_request(method="get", url=url, data=payload,
headers=headers)
if json_response["errors"]:
self.error(str(json_response["errors"]))
return
if not json_response["resources"]:
# still waiting for the report ID
time.sleep(60.0)
report_found = 0
else:
# report_id is found
report_id = json_response["resources"][0]
report_found = 1
else:
report_id = json_response["resources"][0]

analyzeinprogress = 1
while analyzeinprogress == 1:
url = self.API_Base_Url + \
"/falconx/entities/report-summaries/v1?ids=" + report_id
payload = {}
headers = {'Content-Type': 'application/json'}
json_response_report = self.rest_api_request(method="get", url=url, data=payload, headers=headers)
if json_response_report["errors"]:
self.error(str(json_response_report["errors"]))
return

if not json_response_report["resources"] and \
json_response_report["meta"]["quota"]["in_progress"] != 0:
# still waiting for the analysis results
time.sleep(60.0)
analyzeinprogress = 1
else:
# report is ready
analyzeinprogress = 0

if json_response_report["errors"]:
self.error(str(json_response_report["errors"]))
return
else:
self.report(json_response_report)

except Exception as ex:
self.error(traceback.format_exc())


if __name__ == '__main__':
FalconSandbox().run()
42 changes: 42 additions & 0 deletions analyzers/FalconSandbox/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!-- General error -->
<div class="panel panel-danger" ng-if="!success">
<div class="panel-heading">
<strong>Crowdstrike FalconX Sandbox report for {{(artifact.data || artifact.attachment.name) | fang}}</strong>
</div>
<div class="panel-body">
{{results.errorMessage}}
</div>
</div>

<!-- Success -->
<div class="panel panel-info" ng-if="success">
<div class="panel-heading">
<strong>CrowdStrike Falcon X Sandbox Report for {{(artifact.data || artifact.attachment.name) | fang}}</strong>
</div>
<div class="panel-body">
<a target="_blank" href="https://falcon.crowdstrike.com/intelligence/sandbox/report/{{content.resources[0].id}}/report-summary">Open the Crowdstrike Sandbox Report here</a>
</div>
<div class="panel-body" ng-repeat="sample in content.resources">

<div class="panel-body" ng-repeat="sandbox in sample.sandbox">
<div ng-repeat="(key, value) in sandbox">
<dl class="dl-horizontal">
<dt>{{key}}</dt>
<dd>
<div ng-switch on="key">
<div ng-switch-when="verdict">
<span class="label" ng-class="{'label-info':value === 'no specific threat',
'label-warning': value==='suspicious',
'label-danger': value==='malicious',
'label-success': value==='no verdict'}">
{{value}}
</span>
</div>
<div ng-switch-default>{{value}}</div>
</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
4 changes: 4 additions & 0 deletions analyzers/FalconSandbox/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
requests~=2.24.0
cortexutils~=2.0.0
urllib3~=1.25.9
oauthlib~=3.1.0
3 changes: 3 additions & 0 deletions analyzers/FalconSandbox/short.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<span class="label" ng-repeat="t in content.taxonomies" ng-class="{'No Specific Threat': 'label-info', 'no verdict': 'label-success', 'suspicious': 'label-warning', 'malicious':'label-danger'}[t.level]">
{{t.namespace}}:{{t.predicate}}="{{t.value}}"
</span>