Skip to content

Commit

Permalink
Merge branch 'Crowdstrike-Sandbox-Analyzer' of https://github.com/foe…
Browse files Browse the repository at this point in the history
…nyxxyneof/Cortex-Analyzers into foenyxxyneof-Crowdstrike-Sandbox-Analyzer
  • Loading branch information
jeromeleonard committed Jan 24, 2022
2 parents e508235 + a31e105 commit ed10c92
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 0 deletions.
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>

0 comments on commit ed10c92

Please sign in to comment.