Skip to content

Commit

Permalink
Merge pull request #943 from Neo23x0/master
Browse files Browse the repository at this point in the history
  • Loading branch information
dadokkio authored Feb 26, 2021
2 parents fa0f72f + 77cf56a commit de934a2
Show file tree
Hide file tree
Showing 19 changed files with 502 additions and 0 deletions.
23 changes: 23 additions & 0 deletions analyzers/Thunderstorm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
### Thunderstorm

The Thunderstorm analyzer submits a file sample to a local or public THOR Thunderstorm service and processes the scan result

#### Requirements

- [ThunderstormAPI](https://github.com/NextronSystems/thunderstormAPI)

#### Scope

[THOR Thunderstorm](https://www.nextron-systems.com/thor-thunderstorm/) is a web service version of the well-known scanner THOR. THOR focuses on APTs, hacking activity, traces of hacking activity and file anomalies like obfuscation techniques, suspicious PE packers or PE header anomalies.

#### Matches

The reports contain useful meta data and a list of matching rules. Each rule links to a related public report or states that the rules was based on internal research.

The reports include a total score and sub scores defined in the matching YARA rules.

The score and level indicate the criticality of the finding.

#### Access to Thunderstorm

THOR Thunderstorm is a high-speed, multi-threaded, caching scan service that is licensed and installed on-premise on the Linux system of your choice. Nextron systems offers access to test systems with the FQDN thunderstorm.nextron-systems.com [on request](https://www.nextron-systems.com/get-started/).
70 changes: 70 additions & 0 deletions analyzers/Thunderstorm/Thunderstorm_ScanSample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"name": "THOR_Thunderstorm_ScanSample",
"version": "0.3.1",
"author": "Florian Roth",
"url": "https://github.com/NextronSystems/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Submits sample to an on-premise THOR Thunderstorm web service and processes the scan result",
"dataTypeList": ["file"],
"command": "Thunderstorm/thunderstorm.py",
"baseConfig": "Thunderstorm",
"configurationItems": [
{
"name": "thunderstorm_server",
"description": "Thunderstorm Server",
"type": "string",
"multi": false,
"required": true,
"defaultValue": "thunderstorm.nextron-systems.com"
},
{
"name": "thunderstorm_port",
"description": "Thunderstorm Port",
"type": "number",
"multi": false,
"required": true,
"defaultValue": 8080
},
{
"name": "thunderstorm_source",
"description": "Source System",
"type": "string",
"multi": false,
"required": false,
"defaultValue": "cortex-analyzer"
},
{
"name": "thunderstorm_ssl",
"description": "Use an SSL encrypted HTTP connection",
"type": "boolean",
"multi": false,
"required": false,
"defaultValue": false
},
{
"name": "thunderstorm_ssl_verify",
"description": "Verify the SSL certificate of the remote service",
"type": "boolean",
"multi": false,
"required": false,
"defaultValue": false
}
],
"registration_required": true,
"subscription_required": true,
"free_subscription": false,
"service_homepage": "https://www.nextron-systems.com/thor-thunderstorm/",
"service_logo": {"path":"assets/thor_thunderstorm_logo.png", "caption": "logo"},
"screenshots": [
{"path":"assets/THOR_Thunderstorm_ScanSample_long.png",
"caption":"THOR Thunderstorm long report sample"
},
{
"path": "assets/THOR_Thunderstorm_ScanSample_short.png",
"caption:":"THOR Thunderstorm short report sample"
},
{"path":"assets/THOR_Thunderstorm_ScanSample_raw.png",
"caption":"THOR Thunderstorm raw JSON"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions analyzers/Thunderstorm/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cortexutils
requests
thunderstormAPI
99 changes: 99 additions & 0 deletions analyzers/Thunderstorm/thunderstorm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
# encoding: utf-8

import os

from thunderstormAPI.thunderstorm import ThunderstormAPI
from cortexutils.analyzer import Analyzer


class ThunderstormAnalyzer(Analyzer):

def __init__(self):
Analyzer.__init__(self)
self.thunderstorm_server = self.get_param('config.thunderstorm_server', None, 'THOR Thunderstorm server has not been configured')
self.thunderstorm_port = self.get_param('config.thunderstorm_port', 8080)
self.thunderstorm_source = self.get_param('config.thunderstorm_source', 'cortex-analyzer')
self.thunderstorm_ssl = self.get_param('config.thunderstorm_ssl', False)
self.thunderstorm_verify_ssl = self.get_param('config.thunderstorm_ssl_verify', False)

self.thorapi = ThunderstormAPI(
host=self.thunderstorm_server,
port=int(self.thunderstorm_port),
source=self.thunderstorm_source,
use_ssl=self.thunderstorm_ssl,
verify_ssl=self.thunderstorm_verify_ssl)

def check_response(self, response):
if len(response) > 0:
if type(response) is not list:
self.error('Bad response : ' + str(response))
results = response[0]
else:
results = {}
return results

def summary(self, raw):
taxonomies = []
level = "info"
namespace = "THUNDERSTORM"
predicate = "GetScanResult"
value = "no matches"

result = raw
if len(result) > 0:
# A single match automatically makes it suspicious
level = "suspicious"
# Get THOR's level
thor_level = result['level']
# If that is 'Alert', then increase the level to malicious
if thor_level == "Alert":
level = "malicious"

# Get all matches that add a sub score to the total score
match_reasons = []
yara_matches = 0
other_matches = 0
total_score = 0
for match in result['matches']:
# Fix /tmp/ folder finding caused by Cortex file upload
if "suspicious apt directory" in match['reason'].lower():
# ignore this match
continue
# Add sub score to total score
if 'subscore' in match:
total_score += int(match['subscore'])
# YARA rule match
if 'rulename' in match:
match_reasons.append(match['rulename'])
yara_matches += 1
else:
other_matches += 1

# Combine all rule names to a value
if len(match_reasons) > 0:
if len(match_reasons) < 4:
value = ", ".join(match_reasons)
else:
value = "[%d of different rule matches]" % len(match_reasons)

# Add match type to the result set
result['yara_matches'] = yara_matches
result['other_matches'] = other_matches

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

def run(self):
if self.data_type == 'file':
data = self.get_param('file', None, 'File is missing')
if os.path.exists(data):
self.report(self.check_response(self.thorapi.scan(data)))
else:
self.error("File '%s' not found" % data)
else:
self.error('Invalid data type')


if __name__ == '__main__':
ThunderstormAnalyzer().run()
28 changes: 28 additions & 0 deletions analyzers/Valhalla/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
### Valhalla

The Valhalla analyzer queries the Valhalla YARA rule databased and retrieves the matching YARA rules.

#### Requirements

- [ValhallaAPI](https://github.com/NextronSystems/valhallaAPI)

#### Scope

The result contains all matching YARA rules including

- Nextron's rules in the [public rule repository](https://github.com/Neo23x0/signature-base/)
- Nextron's rules sold in the form of the [YARA rule feed](https://www.nextron-systems.com/valhalla/)

The result does not contain matches with YARA rules

- submitted by 3rd parties into the [public rule repository](https://github.com/Neo23x0/signature-base/) due to legal restrictions
- rules that are tagged as confidential and can therefore only be used in Nextron's scanner [THOR](https://www.nextron-systems.com/thor/)
- rules that require external variables and can therefore only be used in Nextron's scanner [THOR](https://www.nextron-systems.com/thor/)

The database contains YARA rule matches on samples submitted to Virustotal and Nextron's internal sample matching, which accounts for less than 1% of the matches within that database. The database does not contain information on samples that have not been transmitted to Virustotal.

#### Matches

The matches in the long report link to rule info pages that contain more information, like other matching samples, a report or public source in which the sample from which that rule was derived has been mentioned.

They also include the Antivirus detection rate at the moment of the first submission to Virustotal, which gives a good indication of the overall coverage.
34 changes: 34 additions & 0 deletions analyzers/Valhalla/Valhalla_GetMatches.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "Valhalla_GetRuleMatches",
"version": "0.3.1",
"author": "Florian Roth",
"url": "https://github.com/NextronSystems/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Gets matching YARA rules for a given sample SHA256 hash",
"dataTypeList": ["hash"],
"command": "Valhalla/valhalla.py",
"baseConfig": "Valhalla",
"configurationItems": [
{
"name": "key",
"description": "API key for Valhalla",
"type": "string",
"multi": false,
"required": true,
"defaultValue": "1111111111111111111111111111111111111111111111111111111111111111"
}
],
"registration_required": false,
"subscription_required": false,
"free_subscription": false,
"service_homepage": "https://valhalla.nextron-systems.com",
"service_logo": {"path":"assets/Valhalla_logo.png", "caption": "logo"},
"screenshots": [
{"path":"assets/Valhalla_GetMatches_short.png",
"caption":"Valhalla Get Hashes short report sample"
},
{
"path": "assets/Valhalla_GetMatches_long.png",
"caption:":"Valhalla Get Hashes long report sample"
}]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added analyzers/Valhalla/assets/Valhalla_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions analyzers/Valhalla/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cortexutils
requests
valhallaAPI
84 changes: 84 additions & 0 deletions analyzers/Valhalla/valhalla.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
# encoding: utf-8

from valhallaAPI.valhalla import ValhallaAPI
from cortexutils.analyzer import Analyzer


class ValhallaAnalyzer(Analyzer):

def __init__(self):
Analyzer.__init__(self)
self.valhalla_key = self.get_param('config.key', None, 'Missing Valhalla API key')
self.polling_interval = self.get_param('config.polling_interval', 60)
self.v = ValhallaAPI(api_key=self.valhalla_key)

def check_response(self, response):
if type(response) is not dict:
self.error('Bad response : ' + str(response))
status = response.get('status', 'not set')
if status == 'error':
self.error('Query failed: %s Message: %s' % (str(status), response.get('message', 'not set')))
results = response
return results

def summary(self, raw):
taxonomies = []
level = "info"
namespace = "VALHALLA"
predicate = "GetMatches"
value = "not set"

# Get status and result set
status = raw.get('status', 'not set')
results = raw.get('results', [])

# Status handling
if status == "error":
status = raw.get('message', 'not set')
value = status
if status == "empty":
value = "no matches found"

# If a single matching YARA rule could be found, then set suspicious
if len(results) > 0:
level = "suspicious"

# Match handling
av_matches = []
avg_av_matches = 0
matching_rules = []
for match in results:
# Add rule to list
matching_rules.append(match["rulename"])
# Sum up all AV matches
if 'positives' in match:
if isinstance(match['positives'], int):
av_matches.append(match['positives'])
# Calculate average AV detection rate
if len(av_matches) > 0:
avg_av_matches = sum(av_matches)/len(av_matches)
# If AV engines also came to the conclusion that this is malicious, then mark it as malicious
if avg_av_matches > 10:
level = "malicious"

# Compose a list of all matching YARA rules for the value field
if len(matching_rules) > 0:
value = ", ".join(matching_rules)

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

def run(self):
if self.data_type == 'hash':
data = self.get_param('data', None, 'Data is missing')
if len(data) == 64:
self.report(self.check_response(self.v.get_hash_info(data)))
else:
self.report({'status': 'error', 'message': 'hash is not SHA256', 'results': []})
else:
self.error('Invalid data type')


if __name__ == '__main__':
ValhallaAnalyzer().run()
Loading

0 comments on commit de934a2

Please sign in to comment.