Skip to content

Commit

Permalink
Merge pull request #1256 from deepanshu-eiq/eiq-analyser
Browse files Browse the repository at this point in the history
Added EclecticIQ Analyser
  • Loading branch information
nusantara-self authored Sep 18, 2024
2 parents c2fcae3 + cbc6b7a commit 3720d30
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 0 deletions.
65 changes: 65 additions & 0 deletions analyzers/EclecticIQ/EclecticIQ_SearchObservable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "EclecticIQ_SearchObservable",
"author": "BW",
"license": "AGPL-V3",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers/",
"version": "2.0",
"description": "Query EclecticIQ Intelligence Center for a specific observable.",
"dataTypeList": [
"domain",
"ip",
"url",
"fqdn",
"uri_path",
"user-agent",
"hash",
"mail",
"mail_subject",
"registry",
"regexp",
"other",
"filename"
],
"config": {
"service": "search_observable"
},
"baseConfig": "EclecticIQ",
"command": "EclecticIQ/eclecticiq.py",
"configurationItems": [
{
"name": "name",
"description": "Name of EclecticIQ instance",
"multi": false,
"required": false,
"type": "string"
},
{
"name": "url",
"description": "URL of EclecticIQ instance",
"type": "string",
"multi": false,
"required": true
},
{
"name": "key",
"description": "API key for EclecticIQ instance",
"type": "string",
"multi": false,
"required": true
},
{
"name": "cert_check",
"description": "Verify server certificate",
"type": "boolean",
"multi": false,
"required": true,
"defaultValue": true
}
],
"registration_required": true,
"subscription_required": true,
"free_subscription": false,
"service_homepage": "https://www.eclecticiq.com",
"service_logo": { "path": "assets/logo.png", "caption": "logo" },
"screenshots": []
}
14 changes: 14 additions & 0 deletions analyzers/EclecticIQ/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[EclecticIQ](https://www.eclecticiq.com/) is a cyber threat intelligence platform which provides aggregation and analysis capabilities for threat intelligence data and integration with organization assets.

The analyzer comes in one flavor to look for an observable in the platform and return any parent entities and their context.

- EclecticIQ\_**SearchObservable**: returns entity data for a specific observable

#### Requirements

The EclecticIQ analyzer requires you to have access to an [EclecticIQ Intelligence Center](https://www.eclecticiq.com/) instance.

Three parameters are required for each instance to make the analyzer work:

- `url` : URL of the instance, e.g. "https://intel-platform.local"
- `key` : API Key for a user of the EclecticIQ Intelligence Center instance
Binary file added analyzers/EclecticIQ/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 127 additions & 0 deletions analyzers/EclecticIQ/eclecticiq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env python3
import typing as tp

import requests

from cortexutils.analyzer import Analyzer


class EclecticIQAnalyzer(Analyzer):
"""Searches for given Observables in configured EclecticIQ instance.
All standard Cortex data types are supported."""

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

self.service = self.get_param("config.service", default="search_observable")

self.name = self.get_param(
"config.name", message="No EclecticIQ instance name given."
)
self.url = self.get_param("config.url", message="No EclecticIQ url given.")
self.key = self.get_param("config.key", message="No EclecticIQ api key given.")
self.data = self.get_param("data", message="Data is missing")

if self.get_param("config.cert_check", True):
self.ssl = self.get_param("config.cert_path", True)
else:
self.ssl = False

self.session = requests.Session()
self.session.verify = self.ssl
self.session.proxies = self.get_param("config.proxy")
self.session.headers.update(
{"Accept": "application/json", "Authorization": f"Bearer {self.key}"}
)

def summary(self, raw):
level = "info"
namespace = "EIQ"
predicate = "API"
found = len(raw["results"].get("entities", []))
value = f"Found {found} entities" if found > 0 else "Not found"
taxonomy = self.build_taxonomy(level, namespace, predicate, value)
return {"taxonomies": [taxonomy]}

def get_source(self, url):
response = self.session.get(url)
return response.json()["data"]["name"]

@staticmethod
def get_confidence(data):
confidence = data.get("confidence", None)
if isinstance(confidence, dict):
confidence = confidence.get("value")
return confidence

def run(self):
"""
Query EclecticIQ instance for data by querying observable for
observable id and then querying entities endpoint for parent entities
Return dict response to cortex
"""

results = {
"name": self.name,
"url": self.url,
"obs_value": self.data,
}
obs_id = self.add_observable_info(results)
if not obs_id:
# exit early for no data
return self.report({})

entities_info = self.get_entities_info(obs_id)
if not entities_info:
# exit early for no data
return self.report({})

results["count"] = entities_info["count"]
results["entities"] = []
for entity in entities_info["data"]:
source_name = self.get_source(entity["sources"][0])
entity_data = entity.get("data", {})
results["entities"].append(
{
"id": entity["id"],
"title": entity_data.get("title"),
"type": entity_data.get("type"),
"confidence": self.get_confidence(entity_data),
"tags": entity.get("meta", {}).get("tags"),
"timestamp": entity.get("meta", {}).get(
"estimated_threat_start_time"
),
"source_name": source_name,
}
)

self.report({"results": results})

def add_observable_info(self, results: dict) -> tp.Optional[str]:
url = self.url + "/api/v2/observables" # set observable url
params = {"filter[value]": self.data} # use data in filter param
response = self.session.get(url, params=params)
if not response.json().get("count"):
return None

data = response.json()["data"]
results["obs_type"] = data[0]["type"]
results["obs_score"] = data[0].get("meta", {}).get("maliciousness")
return data[0]["id"]

def get_entities_info(self, obs_id: str) -> tp.Optional[dict]:
url = self.url + "/api/v2/entities" # set entity url
params = {"filter[observables]": obs_id} # use observable id in filter param

response = self.session.get(url, params=params)
response_json = response.json()

if not response_json.get("count"):
return None

return response_json


if __name__ == "__main__":
EclecticIQAnalyzer().run()
2 changes: 2 additions & 0 deletions analyzers/EclecticIQ/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cortexutils
requests
40 changes: 40 additions & 0 deletions thehive-templates/EclecticIQ_SearchObservable_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<div class="panel panel-info" ng-if="!content.results">No Data</div>

<div class="panel panel-info" ng-repeat="res in content.results.entities">
<div class="panel-heading">{{res.type}} - {{res.title}}</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>ID:</dt>
<dd>
<a target="_blank" href="{{content.results.url}}/entity/{{res.id}}"
>{{res.type}}--{{res.id}}</a
>
</dd>
</dl>
<dl class="dl-horizontal">
<dt>Entity Type:</dt>
<dd>{{res.type}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Timestamp:</dt>
<dd>{{res.timestamp}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Source Name:</dt>
<dd>{{res.source_name}}</dd>
</dl>
<dl class="dl-horizontal" ng-if="res.tags.length > 0">
<dt>Tags:</dt>
<dd><ul><li ng-repeat="tag in ::res.tags | orderBy:'toString()'">{{tag}}</li></ul></dd>
</dl>
</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>
7 changes: 7 additions & 0 deletions thehive-templates/EclecticIQ_SearchObservable_1_0/short.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<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 3720d30

Please sign in to comment.