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

Maltiverse Analyzer #448

Merged
merged 13 commits into from
Dec 17, 2019
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ thehive-templates/*.sh

.idea
.DS_Store
.vscode

Cortex-analyzers.iml

Expand Down
24 changes: 24 additions & 0 deletions analyzers/Maltiverse/Maltiverse_Report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "Maltiverse_Report",
"version": "1.0",
"author": "ottimo",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Get the latest Maltiverse report for an hash, domain or an IP address.",
"dataTypeList": ["hash", "domain", "ip", "url"],
"command": "Maltiverse/maltiverse-client.py",
"baseConfig": "Maltiverse",
"config": {
"service": "get"
},
"configurationItems": [
{
"name": "polling_interval",
"description": "Define time interval between two requests attempts for the report",
"type": "number",
"multi": false,
"required": false,
"defaultValue": 60
}
]
}
150 changes: 150 additions & 0 deletions analyzers/Maltiverse/maltiverse-client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#!/usr/bin/env python
# encoding: utf-8
import sys
import time
import hashlib
import urllib

from cortexutils.analyzer import Analyzer
from maltiverse import Maltiverse

class MaltiverseAnalyzer(Analyzer):

def __init__(self):
Analyzer.__init__(self)
self.service = self.get_param('config.service', None, 'Service parameter is missing')
# self.username = self.get_param('config.username', None, 'Missing Maltiverse API Username')
# self.password = self.get_param('config.password', None, 'Missing Maltiverse API Password')
self.polling_interval = self.get_param('config.polling_interval', 60)
self.proxies = self.get_param('config.proxy', None)
self.m = Maltiverse()

def maltiverse_query_ip(self, data):
try:
result = self.m.ip_get(data)
self.report({
'registrant_name': result.get("registrant_name","-"),
'last_updated': result.get("last_updated","-"),
'asn_registry': result.get("asn_registry","-"),
'classification': result.get("classification","-"),
'asn_country_code': result.get("asn_country_code","-"),
'creation_time': result.get("creation_time","-"),
'visits': result.get("visits","-"),
'blacklist': result.get("blacklist","-"),
'asn_date': result.get("asn_date","-"),
'modification_time': result.get("modification_time","-"),
'asn_cidr': result.get("asn_cidr","-"),
'location': result.get("location","-"),
'country_code': result.get("country_code","-"),
'address': result.get("address","-"),
'ip_addr': result.get("ip_addr","-"),
'cidr': result.get("cidr","-"),
'tag': result.get("tag","-"),
'type': result.get("type","-"),
'email': result.get("email","-")
})
except Exception:
self.error('API Error! Please verify data type is correct.')

def maltiverse_query_domain(self, data):
try:
result = self.m.hostname_get(data)
self.report({
'domain': result.get("domain","-"),
'classification': result.get("classification","-"),
'hostname': result.get("hostname","-"),
'creation_time': result.get("creation_time","-"),
'domain_lenght': result.get("domain_lenght","-"),
'resolved_ip': result.get("resolved_ip","-"),
'modification_time': result.get("modification_time","-"),
'domain_consonants': result.get("domain_consonants","-"),
'visits': result.get("visits","-"),
'tld': result.get("tld","-"),
'entropy': result.get("entropy","-"),
'type': result.get("type","-"),
'as_name': result.get("as_name","-")
})
except Exception:
self.error('API Error! Please verify data type is correct.')

def maltiverse_query_file(self, data):
try:
result = self.m.sample_get(data)
self.report(result)
except Exception:
self.error('API Error! Please verify data type is correct.')

def maltiverse_query_url(self, data):
# urlencode the URL that we are searching for
#data = urllib.quote_plus(data)
try:
result = self.m.url_get(data)
self.report({
'original': data,
'hash': hash,
'url': result.get("url","-"),
'type': result.get("type","-"),
'classification': result.get("classification","-"),
'tag': result.get("tag","-"),
'blacklist': result.get("blacklist","-"),
'creation_time': result.get("creation_time","-"),
'modification_time': result.get("modification_time","-")
})
except:
self.error('API Error! Please verify data type is correct.')

def summary(self, raw):
taxonomies = []
level = "info"
namespace = "Maltiverse"
predicate = "Report"
value = "{}".format("n/a")
if "classification" in raw:
if raw["classification"] == "malicious":
level = "malicious"
elif raw["classification"] == "suspicious":
level = "suspicious"
else:
level = "safe"
value = "{}".format(raw["classification"])


taxonomies.append(self.build_taxonomy(level, namespace, predicate, value))

return {"taxonomies": taxonomies}

def run(self):
Analyzer.run(self)
if self.data_type == 'file':
hashes = self.get_param('attachment.hashes', None)
if hashes is None:
filepath = self.get_param('file', None, 'File is missing')
sha256 = hashlib.sha256()
with io.open(filepath, 'rb') as fh:
while True:
data = fh.read(4096)
if not data:
break
sha256.update(data)
hash = sha256.hexdigest()
else:
# find SHA256 hash
hash = next(h for h in hashes if len(h) == 64)
self.maltiverse_query_file(hash)
elif self.data_type == 'url':
data = self.get_param('data', None, 'Data is missing')
self.maltiverse_query_url(data)
elif self.data_type == 'domain':
data = self.get_param('data', None, 'Data is missing')
self.maltiverse_query_domain(data)
elif self.data_type == 'ip':
data = self.get_param('data', None, 'Data is missing')
self.maltiverse_query_ip(data)
elif self.data_type == 'hash':
data = self.get_param('data', None, 'Data is missing')
self.maltiverse_query_file(data)
else:
self.error('Invalid data type')

if __name__ == '__main__':
MaltiverseAnalyzer().run()
5 changes: 5 additions & 0 deletions analyzers/Maltiverse/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cortexutils
future
requests
git+https://github.com/maltiverse/python-maltiverse
PyJWT
111 changes: 111 additions & 0 deletions thehive-templates/Maltiverse_Report_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<div class="panel panel-danger" ng-if="!success">
<div class="panel-heading">
<strong>{{(artifact.data || artifact.attachment.name) | fang}}</strong>
</div>
<div class="panel-body">
{{content.errorMessage}}
</div>
</div>

<div ng-if="success">
<div class="panel panel-info">
<div class="panel-heading">
Maltiverse record for "{{artifact.data}}"</a>
<br/>
<a target="_blank" href="https://www.maltiverse.com/{{content.type}}/{{artifact.data}}">view more on www.maltiverse.com</a>
</div>
<div class="panel-body">
<dl class="dl-horizontal" ng-if="content.classification">
<dt>Classification</dt>
<dd class="wrap">{{content.classification}}</dd>
</dl>
<dl class="dl-horizontal" ng-if="content.type">
<dt>Type</dt>
<dd class="wrap">{{content.type}}</dd>
</dl>
<dl class="dl-horizontal" ng-if="content.tag">
<dt>Tag</dt>
<dd class="wrap">
<span class="label label-default" style="margin-right:1px" ng-repeat="tag in ::content.tag">
{{tag}}
</span>
</dd>
</dl>
<dl class="dl-horizontal" ng-if="content.creation_time">
<dt>Creation Time</dt>
<dd class="wrap">{{content.creation_time}}</dd>
</dl>
<dl class="dl-horizontal" ng-if="content.modification_time">
<dt>Modification Time</dt>
<dd class="wrap">{{content.modification_time}}</dd>
</dl>
<dl class="dl-horizontal" ng-if="content.subdomains">
<dt>Sub domains</dt>
<dd class="wrap">{{content.subdomains.join(', ')}}</dd>
</dl>

<dl class="dl-horizontal" ng-if="content.filetype">
<dt>File Type</dt>
<dd class="wrap">{{content.filetype}}</dd>
</dl>

<dl class="dl-horizontal" ng-if="content.score">
<dt>Score</dt>
<dd class="wrap">{{content.score}}</dd>
</dl>
<dl class="dl-horizontal" ng-if="content.resolved_ip && content.resolved_ip.length > 0">
<dt>Resolutions</dt>
<dd>
<div ng-if="::artifact.dataType === 'domain'">
<strong>This domain has been seen to resolve to the following IP addresses.</strong>
</div>
<div ng-repeat="resolution in ::content.resolved_ip | limitTo:20">
<p class="text-danger wrap">{{(resolution.ip_addr | fang)}}</p>
</div>
</dd>
</dl>
<dl class="dl-horizontal" ng-if="content.filename && content.filename.length > 0">
<dt>File names</dt>
<dd>
<div ng-if="::artifact.dataType === 'domain'">
<strong>This sample has been seen with the following filename.</strong>
</div>
<div ng-repeat="name in ::content.filename | limitTo:20">
<p class="text-danger wrap">{{(name | fang)}}</p>
</div>
</dd>
</dl>
</div>
</div>



<div class="panel panel-info" ng-if="::content.blacklist" ng-init="blacklists_limit = 5">
<div class="panel-heading">
<strong>Blacklists</strong>
<span class="pull-right" ng-show="::content.blacklist.length > 5">
<a href ng-show="blacklists_limit===5" ng-click="blacklists_limit = undefined">Show All ({{::content.blacklist.length}})</a>
<a href ng-show="!blacklists_limit"ng-click="blacklists_limit = 5">Show less</a>
</span>
</div>

<div class="panel-body">
<p>The observable is present in the following blacklists:
</p>
<table class="table table-hover">
<tr>
<th>Source</th>
<th>Description</th>
<th>First seen</th>
<th>Last seen</th>
</tr>
<tr ng-repeat="bl in content.blacklist | limitTo:blacklists_limit">
<td>{{bl.source}}</td>
<td>{{bl.description}}</td>
<td>{{bl.first_seen}}</td>
<td>{{bl.last_seen}}</td>
</tr>
</table>
</div>
</div>
</div>
3 changes: 3 additions & 0 deletions thehive-templates/Maltiverse_Report_1_0/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="{'info': 'label-info', 'safe': 'label-success', 'suspicious': 'label-warning', 'malicious':'label-danger'}[t.level]">
{{t.namespace}}:{{t.predicate}}="{{t.value}}"
</span>