-
Notifications
You must be signed in to change notification settings - Fork 385
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 #943 from Neo23x0/master
- Loading branch information
Showing
19 changed files
with
502 additions
and
0 deletions.
There are no files selected for viewing
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,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/). |
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": "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.
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,3 @@ | ||
cortexutils | ||
requests | ||
thunderstormAPI |
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,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() |
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,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. |
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,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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,3 @@ | ||
cortexutils | ||
requests | ||
valhallaAPI |
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,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() |
Oops, something went wrong.