diff --git a/threatr/modules/scarletshark_helpers/__init__.py b/threatr/modules/scarletshark_helpers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/threatr/modules/scarletshark_helpers/domain.py b/threatr/modules/scarletshark_helpers/domain.py deleted file mode 100644 index e64732a..0000000 --- a/threatr/modules/scarletshark_helpers/domain.py +++ /dev/null @@ -1,42 +0,0 @@ -from typing import Optional - -from scarlet_shark_client.client import ClientFactory - -from threatr.core.models import Entity, EntitySuperType, EntityType - - -class Domain: - def __init__(self, domain_name: str, client: ClientFactory): - self.domain_name = domain_name - self.client = client - - @staticmethod - def create_entity(domain_data: dict, vendor: str = 'ScarletShark') -> Optional[Entity]: - domain_name = domain_data.get('domain', None) - if not domain_name: - return None - domain, created = Entity.objects.update_or_create( - name=domain_name, - super_type=EntitySuperType.get_types().get("OBSERVABLE"), - type=EntityType.get_types("OBSERVABLE").get("DOMAIN"), - defaults={ - "description": domain_data.get('domain_description', ''), - } - ) - if created: - domain.attributes = {"source_vendor": vendor} - # Source URL - if not domain.source_url and domain_data.get('reference_url', None): - domain.source_url = domain_data.get('reference_url') - # Age of the domain registration - domain_age = domain_data.get('age', -1) - if domain_age > -1: - domain.attributes['age'] = domain_age - registration_date = domain_data.get('registered', None) - if registration_date: - domain.attributes.setdefault('registration_date', registration_date) - domain.save() - return None - - def search(self): - return None diff --git a/threatr/modules/scarletshark_helpers/model.py b/threatr/modules/scarletshark_helpers/model.py deleted file mode 100644 index 4890f1e..0000000 --- a/threatr/modules/scarletshark_helpers/model.py +++ /dev/null @@ -1,2 +0,0 @@ -def __convert_domain(data: dict): - pass diff --git a/threatr/modules/scarletshark_module.py b/threatr/modules/scarletshark_module.py index 8be8c58..b27d162 100644 --- a/threatr/modules/scarletshark_module.py +++ b/threatr/modules/scarletshark_module.py @@ -6,9 +6,9 @@ Entity, EntityRelation, Request, - Event, + Event, EntitySuperType, EntityType, ) -from threatr.modules.module import AnalysisModule +from threatr.modules.module import AnalysisModule, ModuleUtils logger = logging.getLogger(__name__) @@ -24,6 +24,8 @@ class ScarletShark(AnalysisModule): def __init__(self, request: Request, credentials: dict): self.request = request self.credentials = credentials + key = self.credentials.get('api_key', '') + self.client = ClientFactory.get_client(key, api_version='v0.4', print_json=False) @classmethod def unique_identifier(cls) -> str: @@ -40,40 +42,39 @@ def description(cls) -> str: @classmethod def supported_types(cls) -> dict[str, list[str]]: return { - 'observable': ['ipv4', 'ipv6', 'domain', 'sha256', 'url'] + 'observable': ['domain'], + 'actor': ['threat_actor'] } def fail_fast(self) -> bool: return super().fail_fast() def execute_request(self) -> dict: - key = self.credentials.get("api_key") - client = ClientFactory.get_client(key, api_version='v0.4', print_json=True) obj_type = self.request.type.short_name if obj_type.lower() in ["sha256"]: - self.vendor_response = client.search_hash(sha256=self.request.value) + self.vendor_response = self.client.search_hash(sha256=self.request.value) with open("/app/scarlet_shark_hash.json", mode="w") as out: json.dump(self.vendor_response, out) elif obj_type.lower() in ["domain"]: - self.vendor_response = client.search_domain(self.request.value) + self.vendor_response = self.client.search_domain(self.request.value) with open("/app/scarlet_shark_domain.json", mode="w") as out: json.dump(self.vendor_response, out) elif obj_type.lower() in ["ipv4", "ipv6"]: - obj = client.search_ip(ip=self.request.value).get('ips', []) + obj = self.client.search_ip(ip=self.request.value).get('ips', []) if obj: obj = obj[0] self.vendor_response = obj with open("/app/scarlet_shark_ip.json", mode="w") as out: json.dump(self.vendor_response, out) elif obj_type.lower() in ["url"]: - obj = client.search_url(url=self.request.value).get('urls', []) + obj = self.client.search_url(url=self.request.value).get('urls', []) if obj: obj = obj[0] self.vendor_response = obj with open("/app/scarlet_shark_url.json", mode="w") as out: json.dump(self.vendor_response, out) elif obj_type.lower() in ["email"]: - obj = client.search_email(email=self.request.value) + obj = self.client.search_email(email=self.request.value) if obj: obj = obj[0] self.vendor_response = obj @@ -81,8 +82,92 @@ def execute_request(self) -> dict: json.dump(self.vendor_response, out) return self.vendor_response + def __process_threat_actor(self, actor_id): + if actor_id < 1: return None + result = self.client.search_threat_actors(threat_actor_id=actor_id) + print(result) + if not result: return None + aliases = result.get('aliases', []) + if not aliases: return None + name = aliases.pop(0).get('alias_name') + description = result.get('description', '') + actor, _ = Entity.objects.update_or_create( + name=name.strip(), + super_type=EntitySuperType.get_types().get("ACTOR"), + type=EntityType.get_types("ACTOR").get("THREAT_ACTOR"), + ) + ModuleUtils.merge_attributes(actor, { + 'source_vendor': self.vendor(), + }) + if not actor.description and description: + actor.description = description + actor.save() + for alias in aliases: + alias_name = alias.get('alias_name', '') + if not alias_name: continue + a, _ = Entity.objects.update_or_create( + name=alias_name.strip(), + super_type=EntitySuperType.get_types().get("ACTOR"), + type=EntityType.get_types("ACTOR").get("THREAT_ACTOR"), + ) + ModuleUtils.merge_attributes(a, { + 'source_vendor': self.vendor(), + }) + aka, _ = EntityRelation.objects.update_or_create( + name="also known as", + obj_from=actor, + obj_to=a, + ) + return actor + + def __process_domain(self): + if not self.vendor_response.get('domain', ''): + return None + domain = self.vendor_response.get('domain', '') + registration_date = self.vendor_response.get('registered', '') + reference_url = self.vendor_response.get('reference_url', '') + domain_description = self.vendor_response.get('domain_description', '') + tags = self.vendor_response.get('tags', []) + d, _ = Entity.objects.update_or_create( + name=domain.strip(), + super_type=EntitySuperType.get_types().get("OBSERVABLE"), + type=EntityType.get_types("OBSERVABLE").get("DOMAIN"), + ) + ModuleUtils.merge_attributes(d, { + 'source_vendor': self.vendor(), + 'registration_date': registration_date, + 'reference_url': reference_url + }) + ModuleUtils.merge_tags(d, tags) + if not d.description and domain_description: + d.description = domain_description + threat_actor_id = self.vendor_response.get('threat_actor_id', 0) + threat_actor = self.__process_threat_actor(threat_actor_id) + if threat_actor: + d.attributes['is_malicious'] = True + d.attributes['operated_by'] = str(threat_actor.id) + relation, created = EntityRelation.objects.update_or_create( + name="operated by", obj_from=d, obj_to=threat_actor + ) + if created: + relation.attributes = {"source_vendor": self.vendor()} + relation.save() + d.save() + return d + def save_results(self): - pass + # Create or update root entity + root, created = Entity.objects.update_or_create( + name=self.request.value, + super_type=self.request.super_type, + type=self.request.type, + ) + ModuleUtils.merge_attributes(root, { + 'source_vendor': self.vendor(), + }) + root.save() + if self.request.type.short_name.lower() == 'domain': + self.__process_domain() def get_results(self) -> ([Entity], [EntityRelation], [Event]): return self.entities, self.relations, self.events