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

feat: Adds SEKOIA analyzers #780

Merged
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
50 changes: 50 additions & 0 deletions analyzers/SEKOIAIntelligenceCenter/IntelligenceCenter_Context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "SEKOIAIntelligenceCenter_Context",
"version": "1.0",
"author": "SEKOIA",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Query the Intelligence Center to retrieve the context of an observable",
"dataTypeList": [
"domain",
"fqdn",
"url",
"hash",
"ip"
],
"command": "SEKOIAIntelligenceCenter/sekoia_intelligence_center_analyzer.py",
"baseConfig": "SEKOIAIntelligenceCenter",
"config": {
"service": "context"
},
"configurationItems": [
{
"name": "api_key",
"description": "Intelligence center API key",
"type": "string",
"multi": false,
"required": true
},
{
"name": "url",
"description": "Intelligence center URL",
"type": "string",
"multi": false,
"required": false
}
],
"registration_required": true,
"subscription_required": true,
"free_subscription": false,
"service_homepage": "https://sekoia.io/",
"service_logo": {
"path": "assets/sekoia_logo.png",
"caption": "logo"
},
"screenshots": [
{
"path": "assets/SEKOIAIntelligenceCenter_Context_long.png",
"caption": "SEKOIAIntelligenceCenter_Context long report sample"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "SEKOIAIntelligenceCenter_Indicators",
"version": "1.0",
"author": "SEKOIA",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Query the Intelligence Center to retrieve indicators",
"dataTypeList": [
"domain",
"fqdn",
"url",
"hash",
"ip"
],
"command": "SEKOIAIntelligenceCenter/sekoia_intelligence_center_analyzer.py",
"baseConfig": "SEKOIAIntelligenceCenter",
"config": {
"service": "indicators"
},
"configurationItems": [
{
"name": "api_key",
"description": "Intelligence center API key",
"type": "string",
"multi": false,
"required": true
},
{
"name": "url",
"description": "Intelligence center URL",
"type": "string",
"multi": false,
"required": false
}
],
"registration_required": true,
"subscription_required": true,
"free_subscription": false,
"service_homepage": "https://sekoia.io/",
"service_logo": {
"path": "assets/sekoia_logo.png",
"caption": "logo"
},
"screenshots": [
{
"path": "assets/SEKOIAIntelligenceCenter_Indicators_long.png",
"caption": "SEKOIAIntelligenceCenter_Indicators long report sample"
}
]
}
14 changes: 14 additions & 0 deletions analyzers/SEKOIAIntelligenceCenter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Get more context around domain names, IP adresses, urls and file hashes using the
[SEKOIA.IO](https://sekoia.io) Intelligence Database.

The analyzer comes in 2 flavors:

- SEKOIAIntelligenceCenter_**Indicators**: Find indicators matching the observable provided.
- SEKOIAIntelligenceCenter_**Context**: Get indicators and their context for the observable provided.

#### Requirements
You need an active [SEKOIA.IO Intelligence Center](https://sekoia.io/) subscription to use the analyzer:

- Provide your API key as a value for the `api_key` parameter.

To get any help don't hesitate to contact [email protected].
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions analyzers/SEKOIAIntelligenceCenter/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cortexutils
requests
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env python3
# encoding: utf-8
from ipaddress import ip_address

import requests
from cortexutils.analyzer import Analyzer
from requests import HTTPError


class IntelligenceCenterAnalyzer(Analyzer):

TYPES_MAPPING = {
"url": "url",
"domain": "domain-name",
"fqdn": "domain-name",
"hash": "file",
"ip": ["ipv4-addr", "ipv6-addr"],
}

DEFAULT_URL = "https://app.sekoia.io"

@property
def url(self):
path = ""
if self.service == "context":
path = "/context"
return "{}/api/v2/inthreat/indicators{}".format(self.base_url, path)

def __init__(self):
Analyzer.__init__(self)
self.service = self.get_param("config.service", None, "Service parameter is missing")
self.api_key = self.get_param("config.api_key", None, "Missing Api Key")
self.base_url = self.get_param("config.url", self.DEFAULT_URL)
if not self.base_url:
# Case of empty string
self.base_url = self.DEFAULT_URL

def run(self):
ic_type = self.get_ic_type()
value = self.get_data()
results = self.perform_request({"type": ic_type, "value": value})
self.report({"results": results})

def summary(self, raw):
count = len(raw.get("results", []))
value = "{} result{}".format(count, "s" if count > 1 else "")
if count == 0:
level = "safe"
else:
level = "malicious"

return {"taxonomies": [self.build_taxonomy(level, "SEKOIA", self.service, value)]}

def get_ic_type(self):
if self.data_type not in self.TYPES_MAPPING.keys():
self.error("Invalid data type")
if self.data_type != "ip":
return self.TYPES_MAPPING[self.data_type]

# Check what kind of IP it is.
try:
address = ip_address(self.get_data())
return "ipv4-addr" if address.version == 4 else "ipv6-addr"
except ValueError:
self.error("Invalid IP address")

def perform_request(self, payload):
"""
Send the request to the API.

The main error codes are handled here
"""
headers = {"Authorization": "Bearer {}".format(self.api_key)}
try:
response = requests.get(self.url, params=payload, headers=headers)
response.raise_for_status()
return response.json()["items"]
except HTTPError as ex:
if ex.response.status_code == 401:
self.error("Unauthorized to query the API. Is the API key valid ?")
if ex.response.status_code == 403:
self.error(
"Forbidden to query the API. Does the API key has the right permissions ?"
)
if ex.response.status_code == 429:
self.error("Quota exhausted.")
self.error("API returned with the error code {}".format(str(ex.response.status_code)))


if __name__ == "__main__":
IntelligenceCenterAnalyzer().run()
75 changes: 75 additions & 0 deletions thehive-templates/SEKOIAIntelligenceCenter_Context_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<div class="sekoia-report" ng-if="success">
<div ng-if="content.results.length === 0">
No results found
</div>
<h2 ng-if="content.results.length > 0">{{content.results.length}} Context(s)</h2>
<div ng-repeat="bundle in content.results track by 'id'">
<h4>{{bundle.id}}</h4>
<div ng-if="bundle.objects.length > 0" ng-repeat="item in bundle.objects">
<div class="panel panel-info" ng-if="item.type !== 'relationship' && item.type !== 'marking-definition'">
<div class="panel-heading">
<strong>{{item.name | fang}}</strong>
</div>
<div class="panel-body">
<div>
<dl class="dl-horizontal">
<dt>Type</dt>
<dd class="wrap">{{item.type}}</dd>
</dl>
<dl class="dl-horizontal" ng-if="item.aliases">
<dt>Aliases</dt>
<dd>
<div ng-if="item.aliases.length === 0">No aliases defined</div>
<div ng-if="item.aliases.length > 0">
<span class="label label-info" style="margin: 2px;" ng-repeat="alias in item.aliases">{{alias}}</span>
</div>
</dd>
</dl>
<dl class="dl-horizontal">
<dt>Description</dt>
<dd class="wrap">{{item.description || 'No description' }}</dd>
</dl>
<dl class="dl-horizontal" ng-if="item.type === 'indicator'">
<dt>Pattern</dt>
<dd class="wrap">{{item.pattern }}</dd>
</dl>
<dl class="dl-horizontal" ng-if="item.type === 'indicator'">
<dt>Pattern type</dt>
<dd class="wrap">{{item.pattern_type || 'stix' }}</dd>
</dl>
<dl class="dl-horizontal" ng-if="item.type === 'indicator'">
<dt>Valid from</dt>
<dd class="wrap">{{item.valid_from.split("T")[0] || 'no set' }}</dd>
</dl>
<dl class="dl-horizontal" ng-if="item.type === 'indicator'">
<dt>Valid until</dt>
<dd class="wrap">{{item.valid_until.split("T")[0] || 'no set' }}</dd>
</dl>
<dl class="dl-horizontal" ng-if="item.kill_chain_phases">
<dt>Kill chain phases</dt>
<dd>
<div ng-if="item.kill_chain_phases.length === 0">No kill chain defined</div>
<div ng-if="item.kill_chain_phases.length > 0">
<span class="label label-info" style="margin: 2px;" ng-repeat="chain in item.kill_chain_phases">{{chain.phase_name}}</span>
</div>
</dd>
</dl>
</div>
</div>
</div>
</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">
<dl class="dl-horizontal" ng-if="content.errorMessage">
<dt><i class="fa fa-warning"></i> SEKOIA: </dt>
<dd class="wrap">{{content.errorMessage}}</dd>
</dl>
</div>
</div>
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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<div class="sekoia-report" ng-if="success">
<div ng-if="content.results.length === 0">
No results found
</div>
<h2 ng-if="content.results.length > 0">{{content.results.length}} Indicator(s)</h2>
<div class="panel panel-info" ng-if="content.results.length > 0" ng-repeat="indicator in content.results track by 'id'">
<div class="panel-heading">
<strong>{{indicator.name | fang}}</strong>
</div>
<div class="panel-body">
<div>
<dl class="dl-horizontal">
<dt>Description</dt>
<dd class="wrap">{{indicator.description || 'No description' }}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Pattern</dt>
<dd class="wrap">{{indicator.pattern }}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Pattern type</dt>
<dd class="wrap">{{indicator.pattern_type || 'stix' }}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Valid from</dt>
<dd class="wrap">{{indicator.valid_from.split("T")[0] || 'no set' }}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Valid until</dt>
<dd class="wrap">{{indicator.valid_until.split("T")[0] || 'no set' }}</dd>
</dl>
<dl class="dl-horizontal" ng-if="indicator.kill_chain_phases">
<dt>Kill chain phases</dt>
<dd>
<div ng-if="indicator.kill_chain_phases.length === 0">No kill chain defined</div>
<div ng-if="indicator.kill_chain_phases.length > 0">
<span class="label label-info" style="margin: 2px;" ng-repeat="chain in indicator.kill_chain_phases">{{chain.phase_name}}</span>
</div>
</dd>
</dl>
</div>
</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">
<dl class="dl-horizontal" ng-if="content.errorMessage">
<dt><i class="fa fa-warning"></i> SEKOIA: </dt>
<dd class="wrap">{{content.errorMessage}}</dd>
</dl>
</div>
</div>
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>