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(c25): adds cluster25's cortex analyzer #1241

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
53 changes: 53 additions & 0 deletions analyzers/Cluster25/C25CortexAnalyzer_investigate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "C25CortexAnalyzer_Investigate",
"version": "1.0",
"author": "Cluster25",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Use Cluster25's CTI API to investigate an observable.",
"dataTypeList": ["domain", "file", "hash", "ip", "mail", "url"],
"command": "c25-cortex-analyzer/c25_cortex_analyzer.py",
"baseConfig": "c25-cortex-analyzer",
"config": {
"check_tlp": false,
"check_pap": false,
"auto_extract_artifacts": true,
"service": "investigate"
},
"configurationItems": [
{
"name": "client_id",
"description": "Cluster25 CTI API credentials",
"type": "string",
"multi": false,
"required": true
},
{
"name": "client_key",
"description": "Cluster25 CTI API credentials",
"type": "string",
"multi": false,
"required": true
},
{
"name": "base_url",
"description": "Cluster25 CTI API base url",
"type": "string",
"multi": false,
"required": true
}
],
"registration_required": true,
"subscription_required": true,
"free_subscription": false,
"service_homepage": "https://www.duskrise.com/the-c25-intelligence/",
"service_logo": {"path":"assets/cluster25_logo.png", "caption": "logo"},
"screenshots": [
{"path":"assets/short_report_sample.png",
"caption":"report sample"
},
{
"path": "assets/long_report_sample.png",
"caption:":"report sample"
}]
}
7 changes: 7 additions & 0 deletions analyzers/Cluster25/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM python:3.11

WORKDIR /worker
COPY . c25_analyzer

RUN pip install --no-cache-dir -r c25_analyzer/requirements.txt
ENTRYPOINT c25_analyzer/c25_cortex_analyzer.py
101 changes: 101 additions & 0 deletions analyzers/Cluster25/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Cluster25 Cortex Analyzer

Allows to query Cluster25's CTI API investigation service.
Running the analyzer will return a short report with taxonomies,
as well as a long report and extracted artefacts.

## Requirements:
* C25 API KEY
* C25 CLIENT ID
* C25 BASE URL

Raw investigate result query example:
```json
{
"indicator": "211.56.98.146",
"indicator_type": "ipv4",
"whitelisted": false,
"tags": [],
"score": 70,
"is_known": false,
"actors": [],
"related_indicators": {
"by_file": [],
"by_content": []
},
"related_contexts": [],
"created_dt": null,
"modified_dt": null,
"attacker_activities": [],
"targeted_sectors": [],
"targeted_countries": [],
"file_info": null,
"cve_info": null,
"asn_info": null,
"btcaddress_info": null,
"family_info": null,
"stats": {
"harmless": 61,
"malicious": 5,
"suspicious": 0,
"undetected": 23
},
"communicating_files": [],
"contacted_ips": [],
"contacted_domains": [],
"contacted_urls": [],
"dropped_files": [],
"passive_dns": {
"resolutions": [
{
"record_name": "c3kr.simonxu.cc",
"record_value": "211.56.98.146",
"record_type": "A",
"first_seen": "2021-03-26T14:16:15",
"last_seen": "2021-03-26T14:16:55",
"country_name": "South Korea",
"$$hashKey": "object:64"
},
{
"record_name": "counter.yadro.ru",
"record_value": "211.56.98.146",
"record_type": "A",
"first_seen": "2018-10-19T22:00:00",
"last_seen": "2018-10-19T22:00:00",
"country_name": "South Korea",
"$$hashKey": "object:65"
}
]
},
"whois": {
"ip": null,
"created_date": null,
"updated_date": "[email protected]",
"expires_date": null,
"registrant": {
"name": "IP Manager",
"organization": "Korea Telecom",
"street1": "Gyeonggi-do Bundang-gu, Seongnam-si Buljeong-ro 90",
"street2": null,
"city": null,
"state": null,
"country": null,
"country_code": null,
"postal_code": "13606",
"raw_text": null,
"unparsable": null
},
"registrar_name": null,
"name_servers_hostnames": null,
"name_servers_ips": null,
"email_provider": null,
"email_registrant": null,
"status": null
},
"guessed_types": [],
"intelligence": null,
"first_seen": null,
"last_seen": null,
"dns_resolutions": null
}
```
Binary file added analyzers/Cluster25/assets/cluster25_logo.png
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.
97 changes: 97 additions & 0 deletions analyzers/Cluster25/c25_cortex_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
# encoding: utf-8
from typing import Optional, List

import requests
from cortexutils.analyzer import Analyzer


class Cluster25Client:
def __init__(
self,
customer_id: Optional[str] = None,
customer_key: Optional[str] = None,
base_url: Optional[str] = None
):
self.client_id = customer_id
self.client_secret = customer_key
self.base_url = base_url
self.current_token = self._get_cluster25_token()
self.headers = {"Authorization": f"Bearer {self.current_token}"}

def _get_cluster25_token(
self
) -> List[dict]:
payload = {"client_id": self.client_id, "client_secret": self.client_secret}
r = requests.post(url=f"{self.base_url}/token", json=payload, headers={"Content-Type": "application/json"})
if r.status_code != 200:
raise Exception(f"Unable to retrieve the token from C25 platform, status {r.status_code}")
return r.json()["data"]["token"]

def investigate(
self,
indicator
) -> dict:
params = {'indicator': indicator.get('value')}
r = requests.get(url=f"{self.base_url}/investigate", params=params, headers=self.headers)
if r.status_code != 200:
return {'error': f"Unable to retrieve investigate result for indicator '{indicator.get('value')}' "
f"from C25 platform, status {r.status_code}"}
return r.json()["data"]


class C25CortexAnalyzer(Analyzer):
def __init__(
self
):
Analyzer.__init__(self)
self.c25_api_key = self.get_param("config.client_key", None, "Missing Cluster25 api key")
self.c25_client_id = self.get_param("config.client_id", None, "Missing Cluster25 client id")
self.c25_base_url = self.get_param("config.base_url", None, "Missing Cluster25 base url")
self.c25_api_client = Cluster25Client(self.c25_client_id, self.c25_api_key, self.c25_base_url)

def investigate(
self,
indicator: str
) -> dict:
return self.c25_api_client.investigate({'value': indicator})

def summary(
self,
indicator_data: dict
) -> dict:
taxonomies = []
namespace = "C25"
level = 'info'
if indicator_data.get('indicator'):
taxonomies.append(self.build_taxonomy(level, namespace, "Indicator", indicator_data.get('indicator')))
if indicator_data.get('indicator_type'):
taxonomies.append(
self.build_taxonomy(level, namespace, "Indicator Type", indicator_data.get('indicator_type')))
if indicator_data.get('score'):
if indicator_data.get('score') < 50:
level = 'safe'
elif 50 <= indicator_data.get('score') < 80:
level = 'suspicious'
else:
level = 'malicious'
taxonomies.append(self.build_taxonomy(level, namespace, "Score", indicator_data.get('score')))
if len(taxonomies) == 0:
taxonomies.append(self.build_taxonomy(level, namespace, 'Threat', 'Not found'))

return {"taxonomies": taxonomies}

def run(
self
):
try:
indicator = self.get_param('data', None, 'Data is missing')
indicator_data = self.investigate(indicator)
if indicator_data:
self.report(indicator_data)
except Exception as e:
self.error(e)


if __name__ == '__main__':
C25CortexAnalyzer().run()
2 changes: 2 additions & 0 deletions analyzers/Cluster25/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests~=2.31.0
cortexutils~=2.2.0
Loading