Merge branch 'develop' into feature/manalyze

This commit is contained in:
Jérôme Leonard 2018-09-17 07:01:11 +02:00
commit 3a8ea1ca37
22 changed files with 641 additions and 13 deletions

View File

@ -0,0 +1,14 @@
{
"name": "DShield_lookup",
"version": "1.0",
"author": "Xavier Xavier, SANS ISC",
"url": "https://github.com/xme/thehive/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Query the SANS ISC DShield API to check for an IP address reputation.",
"dataTypeList": ["ip"],
"command": "DShield/DShield_lookup.py",
"baseConfig": "DShield",
"config": {
"service": "query"
}
}

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python3
# encoding: utf-8
import json
import requests
import datetime
import math
from cortexutils.analyzer import Analyzer
class DShieldAnalyzer(Analyzer):
def __init__(self):
Analyzer.__init__(self)
def dshield_checkip(self, data):
url = 'https://isc.sans.edu/api/ip/%s?json' % data
r = requests.get(url)
return json.loads(r.text)
def artifacts(self, raw):
artifacts = []
if 'as' in raw:
artifacts.append({'type':'autonomous-system','value':str(raw['as'])})
if 'asabusecontact' in raw:
artifacts.append({'type': 'email', 'value':str(raw['asabusecontact'])})
return artifacts
def summary(self, raw):
taxonomies = []
value = "-"
level = 'safe'
categories = raw.get("Categories", None)
blacklists = raw.get("Blacklists", None)
num_categories = raw.get("Categories Identifier", None)
if 'maxrisk' in raw:
if 'threatfeedscount' in raw:
r = int(raw['maxrisk']) + raw['threatfeedscount']
else:
r = int(raw['maxrisk'])
if r <= 1:
level = 'safe'
elif r <= 6:
level = 'suspicious'
else:
level = 'malicious'
value = "{} count(s) / {} attack(s) / {} threatfeed(s)".format(raw['count'], raw['attacks'], raw['threatfeedscount'])
taxonomies.append(self.build_taxonomy(level, "DShield", "Score", value))
return {"taxonomies": taxonomies}
def get_reputation(self, risk):
if risk <= 1:
return("Safe")
elif risk <= 6:
return("Suspicious")
else:
return("Malicious")
def run(self):
if self.data_type == 'ip':
data = self.get_param('data', None, 'Data is missing')
r = self.dshield_checkip(data)
# Do we get valid results
if self.data_type in r.keys():
info = r[self.data_type]
results = {}
results['ip'] = info['number']
results['count'] = info['count'] if isinstance(info['count'], int) else 0
results['attacks'] = info['attacks'] if isinstance(info['attacks'], int) else 0
results['lastseen'] = info['maxdate'] if isinstance(info['maxdate'], str) else 'None'
results['firstseen'] = info['mindate'] if isinstance(info['mindate'], str) else 'None'
results['updated'] = info['updated'] if isinstance(info['updated'], str) else 'None'
results['comment'] = info['comment'] if isinstance(info['comment'], str) else 'None'
results['asabusecontact'] = info['asabusecontact'] if isinstance(info['asabusecontact'], str) else 'Unknown'
results['as'] = info['as']
results['asname'] = info['asname']
results['ascountry'] = info['ascountry']
results['assize'] = info['assize']
results['network'] = info['network']
results['threatfeedscount'] = 0
if 'threatfeeds' not in info:
results['threatfeeds'] = ''
else:
results['threatfeedscount'] = len(json.loads(json.dumps(info['threatfeeds'])))
results['threatfeeds'] = info['threatfeeds']
# Compute a risk level based on collected information
results['maxrisk'] = 0
maxrisk = 10
if results['attacks'] > 0:
results['maxrisk'] = round(min(math.log10(results['attacks']) * 2, maxrisk))
# We add the number of threat feeds to the maxrisk to increase the detection rate
results['reputation'] = self.get_reputation(int(results['maxrisk']) + results['threatfeedscount'])
self.report(results)
else:
self.error('No data found')
else:
self.error('Invalid data type')
if __name__ == '__main__':
DShieldAnalyzer().run()

View File

@ -0,0 +1,2 @@
cortexutils
urllib2

View File

@ -78,6 +78,9 @@ class DomainToolsAnalyzer(Analyzer):
"domain_count": sum(d["domain_count"] for d in raw["ip_addresses"])
}
if "record_count" in raw:
r["record_count"] = raw.get('record_count')
if "domain_count" in raw:
r["domain_count"] = {
"current": raw["domain_count"]["current"],
@ -122,7 +125,7 @@ class DomainToolsAnalyzer(Analyzer):
if r["service"] == "whois/history":
taxonomies.append(self.build_taxonomy("info", "DT", "Whois_History",
"{}, {} domains ".format(r["name_server"], r["domain_count"])))
"{} {}".format(r["record_count"], "records" if r["record_count"] > 1 else "record")))
if r["service"] == "whois/parsed" or r['service'] == "whois":
if r["registrar"]:
@ -170,9 +173,6 @@ class DomainToolsAnalyzer(Analyzer):
except Exception as e:
self.unexpectedError(e)
else:
self.error('Unknown DomainTools service or invalid data type')
if __name__ == '__main__':
DomainToolsAnalyzer().run()

View File

@ -1,11 +1,29 @@
{
"name": "Fortiguard_URLCategory",
"version": "2.0",
"version": "2.1",
"author": "Eric Capuano",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "AGPL-V3",
"dataTypeList": ["domain", "url"],
"description": "Check the Fortiguard category of a URL or a domain.",
"description": "Check the Fortiguard category of a URL or a domain. Check the full available list at https://fortiguard.com/webfilter/categories",
"baseConfig": "Fortiguard",
"command": "Fortiguard/urlcategory.py"
"command": "Fortiguard/urlcategory.py",
"configurationItems": [
{
"name": "malicious_categories",
"description": "List of FortiGuard categories to be considered as malicious",
"type": "string",
"multi": true,
"required": true,
"defaultValue": ["Malicious Websites", "Phishing", "Spam URLs"]
},
{
"name": "suspicious_categories",
"description": "List of FortiGuard categories to be considered as suspicious",
"type": "string",
"multi": true,
"required": true,
"defaultValue": ["Newly Observed Domain", "Newly Registered Domain", "Dynamic DNS", "Proxy Avoidance", "Hacking"]
}
]
}

View File

@ -16,14 +16,14 @@ class URLCategoryAnalyzer(Analyzer):
if 'category' in raw:
r = raw.get('category')
value = "{}".format(r)
if r == "Malicious Websites":
if r in self.get_param('config.malicious_categories', []):
level = "malicious"
elif r == "Suspicious Websites":
elif r in self.get_param('config.suspicious_categories', []):
level = "suspicious"
elif r == "Not Rated":
level = "info"
else:
level = "safe"
else:
level = "info"
taxonomies.append(self.build_taxonomy(level, "Fortiguard", "URLCat", value))

View File

@ -0,0 +1,22 @@
{
"name": "Hunterio_DomainSearch",
"author": "Rémi Allain, Cyberprotect",
"license": "AGPL-V3",
"url": "https://github.com/Cyberprotect/Cortex-Analyzers",
"version": "1.0",
"description": "hunter.io is a service to find email addresses from a domain.",
"dataTypeList": ["domain", "fqdn"],
"command": "Hunterio/hunterio_analyzer.py",
"baseConfig": "Hunterio",
"config": {
"service": "domainsearch",
"check_tlp": false
},
"configurationItems": [{
"name": "key",
"description": "api key of hunter.io",
"type": "string",
"multi": false,
"required": true
}]
}

View File

@ -0,0 +1,65 @@
#!/usr/bin/env python
# encoding: utf-8
import requests
from cortexutils.analyzer import Analyzer
class Hunterio(Analyzer):
URI = "https://api.hunter.io/v2/"
def __init__(self):
Analyzer.__init__(self)
self.service = self.get_param('config.service', None, 'Service parameter is missing')
self.key = self.get_param('config.key', None, 'Missing hunter.io API key')
def summary(self, raw):
taxonomies = []
namespace = "Hunter.io"
if self.service == 'domainsearch':
found = 0
if(raw.get('meta') and raw['meta'].get('results')):
found = raw['meta'].get('results')
taxonomies.append(self.build_taxonomy('info', namespace, "Emails found", str(found)))
return {"taxonomies": taxonomies}
def artifacts(self, raw):
artifacts = []
if(raw.get('meta') and raw['meta'].get('results') > 0 ):
for email in raw.get('data').get('emails'):
artifacts.append({'type':'email', 'value':email.get('value')})
return artifacts
def run(self):
Analyzer.run(self)
if self.service == 'domainsearch' and (self.data_type == 'domain' or self.data_type == 'fqdn'):
try:
offset = 0
firstResponse = requests.get("{}domain-search?domain={}&api_key={}&limit=100&offset={}".format(self.URI, self.get_data(), self.key, offset))
firstResponse = firstResponse.json()
if firstResponse.get('meta'):
meta = firstResponse.get('meta')
while meta.get('results') > offset:
offset = meta.get('limit') + meta.get('offset')
additionalResponse = requests.get("{}domain-search?domain={}&api_key={}&limit=100&offset={}".format(
self.URI, self.get_data(), self.key, offset))
additionalResponse = additionalResponse.json()
meta = additionalResponse.get('meta')
firstResponse['data']['emails'] += additionalResponse['data']['emails']
self.report(firstResponse)
except Exception as e:
self.unexpectedError(e)
else:
self.notSupported()
if __name__ == '__main__':
Hunterio().run()

View File

@ -0,0 +1,2 @@
cortexutils
requests

View File

@ -10,7 +10,9 @@ class MISPAnalyzer(Analyzer):
Analyzer.__init__(self)
# Fixes #94. Instead of None, the string Unnamed should be passed to MISPClient constructor
name = self.get_param('config.name', 'Unnamed')
name = self.get_param('config.name', None)
if not name or len(name) == 0:
name = 'Unnamed'
if self.get_param('config.cert_check', True):
ssl_path = self.get_param('config.cert_path', None)
if not ssl_path or ssl_path == '':

View File

@ -0,0 +1,20 @@
{
"name": "Pulsedive_GetIndicator",
"version": "1.0",
"author": "Nils Kuhnert",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Search Pulsedive for a given indicator",
"dataTypeList": ["url", "domain", "ip", "hash"],
"baseConfig": "Pulsedive",
"command": "Pulsedive/pulsedive.py",
"configurationItems": [
{
"name": "key",
"description": "Define the API Key",
"type": "string",
"multi": false,
"required": true
}
]
}

View File

@ -0,0 +1,55 @@
#!/usr/bin/env python
import requests
from cortexutils.analyzer import Analyzer
class PulsediveAnalyzer(Analyzer):
def __init__(self):
Analyzer.__init__(self)
self.url = 'https://pulsedive.com/api/'
self.key = self.get_param('config.key', None, 'API-Key not given.')
self.mapping = {
'high': 'malicious',
'medium': 'suspicious',
'low': 'info'
}
def _query(self, observable):
request = self.url + 'info.php'
result = requests.get(request, {
'indicator': observable,
'key': self.key
}).json()
if result.get('error', None) and result.get('error') != 'Indicator not found.':
self.error(result.get('error'))
return result
def run(self):
self.report(self._query(self.get_data()))
def summary(self, raw):
taxonomies = []
for threat in raw.get('threats', []):
taxonomies.append(self.build_taxonomy(
'malicious' if threat.get('risk', '') == 'high' else 'suspicious',
'Pulsedive',
'Threat',
threat.get('name')
))
if raw.get('risk', None):
taxonomies.append(self.build_taxonomy(
self.mapping[raw['risk']],
'Pulsedive',
'Risk',
raw['risk']
))
return {'taxonomies': taxonomies}
if __name__ == '__main__':
PulsediveAnalyzer().run()

View File

@ -0,0 +1,2 @@
cortexutils
requests

View File

@ -1 +1,3 @@
<span class="label label-info">Basic: {{content.count || 0}} record(s)</span>`
<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>

View File

@ -0,0 +1,104 @@
<div class="report-FILEInfo" ng-if="success">
<style>
.report-FILEInfo dl {
margin-bottom: 2px;
}
</style>
<div class="panel panel-info">
<div class="panel-heading">
<strong>DShield IP Reputation Summary</strong>
</div>
<div class="panel-body">
<div ng-if="content[artifact.data].length === 0">
No records found
</div>
<div>
<dl class="dl-horizontal">
<dt>IP: </dt>
<dd class="wrap">{{content.ip|| "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Reputation: </dt>
<dd class="wrap">{{content.reputation || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Network: </dt>
<dd class="wrap">{{content.network || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>AS: </dt>
<dd class="wrap">{{content.as || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>AS Name: </dt>
<dd class="wrap">{{content.asname || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>AS Country: </dt>
<dd class="wrap">{{content.ascountry || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>AS Abuse Contact: </dt>
<dd class="wrap">{{content.asabusecontact || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Number of Attacks: </dt>
<dd class="wrap">{{content.count || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Unique Attacked Hosts: </dt>
<dd class="wrap">{{content.attacks || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>First Reported Attack: </dt>
<dd class="wrap">{{content.firstseen || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Last Reported Attacks: </dt>
<dd class="wrap">{{content.lastseen || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Risk Level: </dt>
<dd class="wrap">{{content.maxrisk || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Comment: </dt>
<dd class="wrap">{{content.comment || "-"}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Threat Feeds: </dt>
<dd class="wrap">{{content.threatfeedscount || "-"}}</dd>
</dl>
</div>
</div>
</div>
<div class="panel panel-info" ng-if="success">
<div class="panel-heading">
<strong>Threat Feeds</strong>
</div>
<div class="panel-body">
<div ng-if="content[artifact.threatfeeds].length === 0">
No threat feed found
</div>
<div>
<dl class="dl-horizontal" ng-repeat="(key, value) in content.threatfeeds">
<dt>{{key}}</dt>
<dd class="wrap">First Seen: {{value.firstseen || "-"}}</dd>
<dd class="wrap">Last Seen: {{value.lastseen || "-"}}</dd>
</dl>
</div>
</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">
{{content.errorMessage}}
</div>
</div>

View File

@ -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>

View File

@ -0,0 +1,66 @@
<div class="panel panel-info" ng-if="success">
<div class="panel-heading">
<a href="https://hunter.io" target="_blank">hunter.io</a> domain search to find email addresses
<br/> Report for
<strong>{{artifact.data}}</strong>
</div>
<div class="panel-body" ng-if="content.meta">
<h4 class="dl-horizontal">{{content.meta.results}} addresses found.</h4>
<div ng-if="content.data && content.data.emails.length > 0">
<h5>
Pattern : {{content.data.pattern}}
</h5>
<h5>
Organization: {{content.data.organization}}
</h5>
<table class="table table-bordered">
<tr>
<th>Email</th>
<th>Name</th>
<th>Position</th>
<th>Type</th>
<th>Twitter</th>
<th>LinkedIn</th>
<th>Phone</th>
<th>Confidence</th>
<th>Sources</th>
</tr>
<tr ng-repeat="email in ::content.data.emails">
<td class="text-info">{{email.value}}</td>
<td>{{email.fisrtname}} {{email.lastname}}</td>
<td>{{email.position}}</td>
<td>{{email.type}}</td>
<td><a ng-if="email.twitter" href="https://twitter.com/{{email.twitter}}" target="_blank">{{email.twitter}}</a></td>
<td><a ng-if="email.linkedin" href="{{email.linkedin}}" target="_blank">{{email.linkedin}}</a></td>
<td>{{email.phone_number}}</td>
<td>
<span class="label label-default">{{email.confidence}}</span>
</td>
<td>
<ul>
<li ng-repeat="src in ::email.sources">{{src.domain}}</li>
</ul>
</td>
</tr>
</table>
</div>
<div class="panel-body" ng-if="!content.meta">
No results found
</div>
</div>
<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>

View File

@ -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>

View File

@ -0,0 +1,141 @@
<div class="panel panel-info" ng-if="success && !content.error">
<div class="panel-heading">
Pulsedive Report for <strong>{{artifact.data}}</strong>
</div>
<div class="panel-body">
<!-- THREATS -->
<div class="panel" ng-repeat="threat in content.threats" ng-class="{'panel-warning': threat.risk !== 'high', 'panel-danger': threat.risk === 'high'}">
<div class="panel-heading">
Threat
</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Name</dt>
<dd>{{threat.name}}</dd>
<dt>Risk</dt>
<dd><span class="label"
ng-class="{'label-warning': threat.risk !== 'high', 'label-danger': threat.risk === 'high'}">
{{threat.risk}}
</span>
</dd>
<dt>Category</dt>
<dd>{{threat.category}}</dd>
</dl>
</div>
</div>
<!-- //THREATS -->
<!-- RISKFACTORS -->
<div class="panel panel-warning" ng-if="content.riskfactors">
<div class="panel-heading">Risk factors</div>
<div class="panel-body">
<ul style="list-style: none" ng-repeat="(k, v) in content.riskfactors">
<li>
<span class="label"
ng-class="{'label-success': v.risk === 'none', 'label-primary': v.risk === 'low', 'label-warning': v.risk === 'medium', 'label-danger': v.risk === 'high'}">
{{v.risk}}
</span>&nbsp;
{{v.description}}
</li>
</ul>
</div>
</div>
<!-- //RISKFACTORS -->
<!-- GENERAL -->
<div class="panel panel-primary">
<div class="panel-heading">General information</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Indicator</dt>
<dd>{{content.indicator}}</dd>
<dt>Risk</dt>
<dd>
<span class="label"
ng-class="{'label-warning': content.risk !== 'high', 'label-danger': content.risk === 'high'}">
{{content.risk}}
</span>
</dd>
<dt>Added</dt>
<dd>{{content.stamp_added | date}}</dd>
<dt>Seen</dt>
<dd>{{content.stamp_seen | date}}</dd>
<dt>Probed</dt>
<dd>{{content.stamp_probed | date}}</dd>
<dt>Updated</dt>
<dd>{{content.stamp_updated | date}}</dd>
<dt>Retired</dt>
<dd><span class="label label-secondary" ng-if="content.retired">Retired on {{content.stamp_retired | date}}</span></dd>
</dl>
</div>
</div>
<uib-accordion>
<div uib-accordion-group class="panel panel-info" heading="Attributes" ng-if="content.attributes" is-open="true">
<div class="panel-body">
<dl class="dl-horizontal" ng-repeat="(a, c) in content.attributes">
<dt>{{a}}</dt>
<dd><ul style="list-style: none"><li ng-repeat="item in c">{{item}}</li></ul></dd>
</dl>
</div>
</div>
<div uib-accordion-group class="panel panel-info" heading="Feeds" ng-if="content.feeds">
<div class="panel-body">
<table class="table">
<thead>
<tr>
<td>Feed name</td>
<td>Feed organization</td>
<td>Feed category</td>
<td>Feed linked</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="(key, feed) in content.feeds">
<td>{{feed.name}}</td>
<td>{{feed.organization}}</td>
<td>{{feed.category}}</td>
<td>{{feed.stamp_linked | date}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div uib-accordion-group class="panel panel-info" heading="Properties">
<div class="panel-body">
<table class="table">
<thead>
<tr>
<td>Property</td>
<td>Property content</td>
</tr>
</thead>
<tr ng-repeat="(property, value) in content.properties">
<td><b>{{property}}</b></td>
<td>
<dl class="dl-horizontal" ng-repeat="(k, v) in value">
<dt>{{k}}</dt>
<dd>{{v}}</dd>
</dl>
</td>
</tr>
</table>
</div>
</div>
<div uib-accordion-group class="panel panel-info" heading="Raw">
<div class="panel-body"><pre><code>{{content | json}}</code></pre></div>
</div>
</uib-accordion>
</div>
</div>
<div class="panel panel-warning" ng-if="success && content.error">
<div class="panel-heading"><strong>{{artifact.data | fang}}</strong></div>
<div class="panel-body">{{content.error}}</div>
</div>
<div class="panel panel-danger" ng-if="!success">
<div class="panel-heading">
<strong>{{artifact.data | fang}}</strong>
</div>
<div class="panel-body">
{{content.errorMessage}}
</div>
</div>

View File

@ -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>