Skip to content

Commit

Permalink
Merge pull request #1241 from Mv35/feature/c25-cortex-analyzer
Browse files Browse the repository at this point in the history
feat(c25): adds cluster25's cortex analyzer
  • Loading branch information
nusantara-self authored Dec 18, 2024
2 parents 3e206f4 + 2275601 commit bcb5e15
Show file tree
Hide file tree
Showing 9 changed files with 662 additions and 0 deletions.
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

0 comments on commit bcb5e15

Please sign in to comment.