Skip to content

Commit

Permalink
Added Malpedia Analyzer (#168), fixes #166
Browse files Browse the repository at this point in the history
  • Loading branch information
garanews authored and 3c7 committed Jan 18, 2018
1 parent 55af297 commit fe5a847
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 0 deletions.
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>

0 comments on commit fe5a847

Please sign in to comment.