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

added Malpedia Analyzer #168

Merged
merged 3 commits into from
Jan 18, 2018
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
12 changes: 12 additions & 0 deletions analyzers/Malpedia/Malpedia.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "Malpedia",
"author": "Davide Arcuri, Andrea Garavaglia - LDO-CERT",
"license": "AGPL-V3",
"url": "https://github.com/LDO-CERT/cortex-analyzers",
"version": "1.0",
"baseConfig": "Malpedia",
"config": {},
"description": "Check files against Malpedia YARA rules.",
"dataTypeList": ["file"],
"command": "Malpedia/malpedia_analyzer.py"
}
116 changes: 116 additions & 0 deletions analyzers/Malpedia/malpedia_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
import os
import io
import sys
import json
import yara
import requests
import datetime

from cortexutils.analyzer import Analyzer
from requests.auth import HTTPBasicAuth
from stat import ST_MTIME


class MalpediaAnalyzer(Analyzer):
"""Checking binaries through yara rules. This analyzer requires a list of yara rule paths in the cortex
configuration. If a path is given, an index file is expected."""

def __init__(self):
Analyzer.__init__(self)

self.baseurl = "https://malpedia.caad.fkie.fraunhofer.de/api/get"
self.rulepaths = self.get_param('config.path', None, 'No rulepath provided.')
self.user = self.get_param('config.username', None, 'No username provided.')
self.pwd = self.get_param('config.password', None, 'No password provided.')
self.update_hours = int(self.get_param('config.update_hours', 10))

if not os.path.exists(self.rulepaths):
os.makedirs(self.rulepaths)

timestamps = []
try:
for fn in os.listdir(self.rulepaths):
for path in os.path.join(self.rulepaths, fn):
if os.path.isfile(path) and path.endswith('.yar'):
timestamps.append(datetime.datetime.fromtimestamp(os.stat(path)[ST_MTIME]))
newest = max(timestamps)
hours = (datetime.datetime.now() - newest).seconds / 3600
except ValueError:
hours = self.update_hours + 1

if hours > self.update_hours or len(timestamps) == 0:
try:
req = requests.get('{}/yara/after/2010-01-01?format=json'.format(self.baseurl),
auth=HTTPBasicAuth(self.user, self.pwd))
if req.status_code == requests.codes.ok:
rules_json = json.loads(req.text)
for color, color_data in rules_json.items():
for rule_name, rule_text in color_data.items():
with io.open(os.path.join(self.rulepaths, rule_name), 'w', encoding='utf-8') as f:
f.write(rule_text)
else:
self.error('Could not download new rules due tue HTTP {}: {}'.format(req.status_code, req.text))
except Exception as e:
with io.open('%s' % os.path.join(self.rulepaths, "error.txt"), 'w') as f:
f.write('Error: {}\n'.format(e))

def check(self, file):
"""
Checks a given file against all available yara rules
:param file: Path to file
:type file:str
:returns: Python list with matched rules info
:rtype: list
"""
result = []
all_matches = []
for filerules in os.listdir(self.rulepaths):
try:
rule = yara.compile(os.path.join(self.rulepaths, filerules))
except yara.SyntaxError:
continue
matches = rule.match(file)
if len(matches) > 0:
for rulem in matches:
rule_family = "_".join([x for x in rulem.rule.replace("_", ".", 1).split("_")[:-1]])
if rule_family not in all_matches:
all_matches.append(rule_family)
for rule_family in all_matches:
rules_info_txt = requests.get('{}/family/{}'.format(self.baseurl, rule_family),
auth=HTTPBasicAuth(self.user, self.pwd))
rules_info_json = json.loads(rules_info_txt.text)
result.append({
'family': rule_family,
'common_name': rules_info_json['common_name'],
'description': rules_info_json['description'],
'attribution': rules_info_json['attribution'],
'alt_names': rules_info_json['alt_names'],
'urls': rules_info_json['urls']
})

return result

def summary(self, raw):
taxonomies = []
namespace = "Malpedia"
predicate = "Match"

value = "\"{} rule(s)\"".format(len(raw["results"]))
if len(raw["results"]) == 0:
level = "safe"
else:
level = "malicious"

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

def run(self):
if self.data_type == 'file':
self.report({'results': self.check(self.get_param('file', None, 'No file given.'))})
else:
self.error('Wrong data type.')


if __name__ == '__main__':
MalpediaAnalyzer().run()
3 changes: 3 additions & 0 deletions analyzers/Malpedia/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
yara-python
cortexutils
requests
56 changes: 56 additions & 0 deletions thehive-templates/Malpedia_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<div class="panel panel-danger" ng-if="success && content.results.length > 0">
<div class="panel-heading">
Malpedia Report
</div>
<div class="panel-body">
<div ng-repeat="m in content.results">
<h4>{{m.common_name}} <span style="font-size: small">[{{m.family}}]</span></h4>
<dl class="dl-horizontal">
<dt>Description</dt>
<dd>{{m.description || "No description given."}}</dd>
<dt>Attributions</dt>
<dd>
<span ng-if="m.attribution.length == 0">No attributions given.</span>
<ul ng-if="m.attribution.length > 0">
<li ng-repeat="x in m.attribution">{{x}}</li>
</ul>
</dd>
<dt>Alternative Names</dt>
<dd>
<span ng-if="m.alt_names.length == 0">No alternative names given.</span>
<ul ng-if="m.alt_names.length > 0">
<li ng-repeat="x in m.alt_names">{{x}}</li>
</ul>
</dd>
<dt>Urls</dt>
<dd>
<span ng-if="m.urls.length == 0">No Urls given.</span>
<ul ng-if="m.urls.length > 0">
<li ng-repeat="x in m.urls"><a href="{{x}}" target="_blank">{{x}}</a></li>
</ul>
</dd>
</dl>
</div>
</div>
</div>
<div class="panel panel-success" ng-if="success && content.results.length == 0">
<div class="panel-heading">
Malpedia Report
</div>
<div class="panel-body">
<span>No matches.</span>
</div>
</div>

<!-- General error -->
<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">
<dl class="dl-horizontal" ng-if="content.errorMessage">
<dt><i class="fa fa-warning"></i> Yara: </dt>
<dd class="wrap">{{content.errorMessage}}</dd>
</dl>
</div>
</div>
3 changes: 3 additions & 0 deletions thehive-templates/Malpedia_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>