Skip to content

Commit

Permalink
Merge pull request #448 from ottimo/develop
Browse files Browse the repository at this point in the history
Maltiverse Analyzer
  • Loading branch information
3c7 authored Dec 17, 2019
2 parents 8ec3b3b + 7dc22e4 commit 4400e2a
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 0 deletions.
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>

0 comments on commit 4400e2a

Please sign in to comment.