def rel_fs_store(): cam = Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS) idy = Identity(id=IDENTITY_ID, **IDENTITY_KWARGS) ind = Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS) mal = Malware(id=MALWARE_ID, **MALWARE_KWARGS) rel1 = Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0]) rel2 = Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1]) rel3 = Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2]) stix_objs = [cam, idy, ind, mal, rel1, rel2, rel3] fs = FileSystemStore(FS_PATH) for o in stix_objs: fs.add(o) yield fs for o in stix_objs: filepath = os.path.join(FS_PATH, o.type, o.id, _timestamp2filename(o.modified) + '.json') # Some test-scoped fixtures (e.g. fs_store) delete all campaigns, so by # the time this module-scoped fixture tears itself down, it may find # its campaigns already gone, which causes not-found errors. try: os.remove(filepath) except OSError as e: # 3 is the ERROR_PATH_NOT_FOUND windows error code. Which has an # errno symbolic value, but not the windows meaning... if e.errno in (errno.ENOENT, 3): continue raise
def _generate_stix_bundle(self, country, city, observable_id): # Generate stix bundle country_identity = Identity( name=country.name, identity_class='group', custom_properties={ 'x_opencti_identity_type': 'country', 'x_opencti_alias': [country.official_name], } ) city_identity = Identity( name=city, identity_class='group', custom_properties={ 'x_opencti_identity_type': 'city' } ) city_to_country = Relationship( relationship_type='localization', source_ref=city_identity.id, target_ref=country_identity.id, ) observable_to_city = Relationship( relationship_type='localization', source_ref=observable_id, target_ref=city_identity.id, custom_properties={ 'x_opencti_weight': self.helper.connect_confidence_level } ) return Bundle(objects=[country_identity, city_identity, city_to_country, observable_to_city]).serialize()
def rel_mem_store(): cam = Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS) idy = Identity(id=IDENTITY_ID, **IDENTITY_KWARGS) ind = Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS) mal = Malware(id=MALWARE_ID, **MALWARE_KWARGS) rel1 = Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0]) rel2 = Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1]) rel3 = Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2]) stix_objs = [cam, idy, ind, mal, rel1, rel2, rel3] yield MemoryStore(stix_objs)
def _generate_stix_bundle(self, country, city, loc, observable_id): # Generate stix bundle country_location = Location( id=OpenCTIStix2Utils.generate_random_stix_id("location"), name=country.name, country=country.official_name if hasattr(country, "official_name") else country.name, custom_properties={ "x_opencti_location_type": "Country", "x_opencti_aliases": [ country.official_name if hasattr(country, "official_name") else country.name ], }, ) loc_split = loc.split(",") city_location = Location( id=OpenCTIStix2Utils.generate_random_stix_id("location"), name=city, country=country.official_name if hasattr(country, "official_name") else country.name, latitude=loc_split[0], longitude=loc_split[1], custom_properties={"x_opencti_location_type": "City"}, ) city_to_country = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="located-at", source_ref=city_location.id, target_ref=country_location.id, ) observable_to_city = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="located-at", source_ref=observable_id, target_ref=city_location.id, confidence=self.helper.connect_confidence_level, ) return Bundle( objects=[ country_location, city_location, city_to_country, observable_to_city, ], allow_custom=True, ).serialize()
def process_observable_relations(self, object_attributes, result_table, start_element=0): if start_element == 0: result_table = [] if len(object_attributes) == 1: return [] for x in range(start_element + 1, len(object_attributes)): result_table.append( Relationship( relationship_type="corresponds", source_ref=object_attributes[start_element]["indicator"] ["id"], target_ref=object_attributes[x]["indicator"]["id"], description="Same file", custom_properties={"x_opencti_ignore_dates": True}, )) if start_element != len(object_attributes): return self.process_observable_relations(object_attributes, result_table, start_element + 1) else: return result_table
def _process_hash(self, observable): hash_value = observable["observable_value"] artifact_id = None bundle_objects = [] try: # Attempt to download the file using the private V3 API method request_url = ( f"https://www.virustotal.com/api/v3/files/{hash_value}/download" ) req = urllib.request.Request(request_url, headers=self.headers) response = urllib.request.urlopen(req) file_contents = response.read() assert file_contents is not None # Get the mime type for the file mime_type = magic.from_buffer(file_contents, mime=True) # Upload the file as an Artifact kwargs = { "file_name": hash_value, "data": file_contents, "mime_type": mime_type, "x_opencti_description": f"Downloaded from Virustotal using hash {hash_value}", } response = self.helper.api.stix_cyber_observable.upload_artifact( **kwargs) self.helper.log_info(response) artifact_id = response["standard_id"] except Exception as e: raise Exception( f"Failed to download/upload Artifact with hash {hash_value}, exception: {e}" ) # Create a relationship between the StixFile and the new Artifact relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="related-to", created_by_ref=self.identity, source_ref=observable["standard_id"], target_ref=artifact_id, allow_custom=True, ) bundle_objects.append(relationship) if bundle_objects: bundle = Bundle(objects=bundle_objects, allow_custom=True).serialize() bundles_sent = self.helper.send_stix2_bundle(bundle) return f"Sent {len(bundles_sent)} stix bundle(s) for worker import" else: return "Nothing to attach"
def rel_fs_store(): cam = Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS) idy = Identity(id=IDENTITY_ID, **IDENTITY_KWARGS) ind = Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS) mal = Malware(id=MALWARE_ID, **MALWARE_KWARGS) rel1 = Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0]) rel2 = Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1]) rel3 = Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2]) stix_objs = [cam, idy, ind, mal, rel1, rel2, rel3] fs = FileSystemStore(FS_PATH) for o in stix_objs: fs.add(o) yield fs for o in stix_objs: os.remove(os.path.join(FS_PATH, o.type, o.id + '.json'))
def create_relationship(): """ Creates a relationship object for the Sigma Rules """ stix_custom_relationship = [] with open('attack_pattern.json', 'rt') as attack_pattern: attack_patterns = json.load(attack_pattern) for attack_pattern in attack_patterns: if 'attack-pattern' in attack_pattern['attack_pattern']: with open('sigma_rules_stix_bundle.json', 'rt') as sigma_rules_stix_bundle: sigma_rules = json.load(sigma_rules_stix_bundle) for sigma_rule in sigma_rules['objects']: if 'tags' in sigma_rule: for attacks in sigma_rule['tags']: if attacks.lower().split("attack.")[-1] == attack_pattern['technique_id'].lower(): relationship = Relationship(relationship_type="sigma_rules", source_ref=sigma_rule['id'], target_ref=attack_pattern['attack_pattern'], created_by_ref="identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5", description="Example", revoked=False, labels="Example", external_references=[ { "description": "Example", "source_name": "Example", "url": "https://example.com" } ], object_marking_refs=["marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"] # granular_markings=["Example"], ) stix_custom_relationship.append(relationship) create_bundle(stix_custom_relationship)
def create_relationship(indicator_list, malware): relation_list = [] for indicator in indicator_list: relation = Relationship(source_ref=indicator, target_ref=malware['id'], relationship_type='indicates') relation_list.append(relation) return relation_list
def _process_hash(self, observable): hash_value = observable["observable_value"] artifact_id = None bundle_objects = [] try: # Attempt to source the file contents from the hash bytes_obj = io.BytesIO() response = self.api_client.download_file(hash_value, bytes_obj) bytes_obj.seek(0) file_contents = bytes_obj.read() mime_type = magic.from_buffer(file_contents, mime=True) # Upload the file as an Artifact kwargs = { "file_name": hash_value, "data": file_contents, "mime_type": mime_type, "x_opencti_description": f"Downloaded from Virustotal via hash {hash_value}", } response = self.helper.api.stix_cyber_observable.upload_artifact( **kwargs) self.helper.log_info(response) artifact_id = response["standard_id"] except Exception as e: raise Exception( f"Failed to download/upload Artifact with hash {hash_value}, exception: {e}" ) # Create a relationship between the StixFile and the new Artifact relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="related-to", created_by_ref=self.identity, source_ref=observable["standard_id"], target_ref=artifact_id, allow_custom=True, ) bundle_objects.append(relationship) if bundle_objects: bundle = Bundle(objects=bundle_objects, allow_custom=True).serialize() bundles_sent = self.helper.send_stix2_bundle(bundle) return f"Sent {len(bundles_sent)} stix bundle(s) for worker import" else: return "Nothing to attach"
def link_to(self, link_type, target, attributes=None): """Creates a link between two YetiObjects. Args: link_type: The type of link. target: The YetiObject to link to. attributes: A dictionary with attributes to add to the link. """ stix_rel = Relationship(relationship_type=link_type, source_ref=self.id, target_ref=target.id) graph = self._db.graph('stix') edge_collection = graph.edge_collection('relationships') document = { '_from': self._arango_id, '_to': target._arango_id, # pylint: disable=protected-access 'attributes': stix_rel.serialize(), } return edge_collection.insert(document)
def link_to(self, target, relationship_type=None, stix_rel=None): """Creates a link between two YetiObjects. Args: target: The YetiObject to link to. relationship_type: The type of link. (e.g. targets, uses, mitigates) stix_rel: STIX Relationship object """ from yeti.core.relationships import Relationship if stix_rel is None: stix_rel = StixRelationship(relationship_type=relationship_type, source_ref=self.id, target_ref=target.id) stix_rel = json.loads(stix_rel.serialize()) existing = list(Relationship.filter({'attributes.id': stix_rel['id']})) if existing: return existing[0] # pylint: disable=protected-access return Relationship(self._arango_id, target._arango_id, stix_rel).save()
def _generate_stix_bundle(self, country, city, observable_id): # Generate stix bundle country_identity = Identity( name=country.name, identity_class="group", custom_properties={ "x_opencti_identity_type": "country", "x_opencti_alias": [ country.official_name if hasattr(country, "official_name") else country.name ], }, ) city_identity = Identity( name=city, identity_class="group", custom_properties={"x_opencti_identity_type": "city"}, ) city_to_country = Relationship( relationship_type="localization", source_ref=city_identity.id, target_ref=country_identity.id, ) observable_to_city = Relationship( relationship_type="localization", source_ref=observable_id, target_ref=city_identity.id, custom_properties={ "x_opencti_weight": self.helper.connect_confidence_level, "x_opencti_ignore_dates": True, }, ) return Bundle(objects=[ country_identity, city_identity, city_to_country, observable_to_city, ]).serialize()
def _generate_stix_bundle(self, country, city, observable_id): # Generate stix bundle country_identity = Location( name=country.name, country=country.official_name if hasattr(country, "official_name") else country.name, custom_properties={ "x_opencti_location_type": "Country", "x_opencti_aliases": [ country.official_name if hasattr(country, "official_name") else country.name ], }, ) city_identity = Location( name=city, country=country.official_name if hasattr(country, "official_name") else country.name, custom_properties={"x_opencti_location_type": "city"}, ) city_to_country = Relationship( relationship_type="located-at", source_ref=city_identity.id, target_ref=country_identity.id, ) observable_to_city = Relationship( relationship_type="located-at", source_ref=observable_id, target_ref=city_identity.id, confidence=self.helper.connect_confidence_level, ) return Bundle(objects=[ country_identity, city_identity, city_to_country, observable_to_city, ]).serialize()
def amitt_relationship(self): """ """ # Merge all UUID dictionaries. stix_objects = {**self.stix_campaign_uuid, **self.stix_intrusion_set_uuid, **self.stix_tactic_uuid, **self.stix_identity_uuid, **self.stix_technique_uuid, **self.stix_threat_actor_uuid, **self.stix_incident_uuid} for i in self.it.itertuples(): if i.id == "I00000T000": continue if i.source in stix_objects and i.target in stix_objects: relationship = Relationship( source_ref=stix_objects[i.source], target_ref=stix_objects[i.target], relationship_type=i.relationship ) self.stix_objects.append(relationship) self.stix_relationship_uuid[i.id] = relationship.id
def create_relationship( relationship_type: str, author: Identity, source: _DomainObject, target: _DomainObject, object_marking_refs: List[MarkingDefinition], start_time: datetime, stop_time: datetime, confidence_level: int, ) -> Relationship: """Create a relationship.""" return Relationship( created_by_ref=author, relationship_type=relationship_type, source_ref=source.id, target_ref=target.id, object_marking_refs=object_marking_refs, start_time=start_time, stop_time=stop_time, confidence=confidence_level, )
def create_relationship( relationship_type: str, created_by: Identity, source: _DomainObject, target: _DomainObject, confidence: int, object_markings: List[MarkingDefinition], start_time: Optional[datetime] = None, stop_time: Optional[datetime] = None, ) -> Relationship: """Create a relationship.""" return Relationship( created_by_ref=created_by, relationship_type=relationship_type, source_ref=source, target_ref=target, start_time=start_time, stop_time=stop_time, confidence=confidence, object_marking_refs=object_markings, )
def stixer(vulns, path): for vuln in vulns: timestamp = datetime.now() vuln_name = vuln['name'] vuln_description = vuln['description'] if args.mitigations: mitigation_description = get_mitigation(vuln['name']) else: mitigation_description = "" snippet_android = vuln['code'] coa_name = vuln_name + "_coa" file_path = PurePath(vuln['file']) target = f"{str(file_path)}:{vuln['line']}" coa = CourseOfAction(type="course-of-action", name=coa_name, description=mitigation_description, x_actions=[{ "mitigation_android": "No snippet avaliable" }], allow_custom=True) vuln = Vulnerability(type="vulnerability", name=vuln_name, description=vuln_description, vulnerable_snippet=snippet_android, allow_custom=True) mitigates = Relationship(coa, 'mitigates', vuln) observed_object = File(name=target) observed_data = ObservedData(first_observed=timestamp, last_observed=timestamp, number_observed=1, objects={0: observed_object}) sight = Sighting(vuln, observed_data_refs=[observed_data]) bundle = Bundle(coa, mitigates, vuln, sight, observed_data, observed_object) with open(f"{path}/{vuln_name}.json", "w") as f: f.write(str(bundle) + "\n")
def get_neighbors(self, obj): """Fethes neighbors for a Yeti object. Args: obj: Yeti object to fetch neighbors for. Returns: A dictionary containing a list of STIX 2 Relationships (in the 'edge' key) and a `{id: object}` dictionary of Yeti objects (in the 'vertices' key). """ if isinstance(obj, entity.Entity): uri = 'entities/{0:s}/neighbors/'.format(obj.id) if isinstance(obj, indicator_base.Indicator): uri = 'indicators/{0:s}/neighbors/'.format(obj.id) response = self._do_get(uri) response['edges'] = [Relationship(**r) for r in response['edges']] vertices = [] for item in response['vertices']: vertices.append(TYPES_DICT[item['type']](**item)) response['vertices'] = vertices return response
def create_relationship( relationship_type: str, author: Identity, source: STIXDomainObject, target: STIXDomainObject, object_marking_refs: List[MarkingDefinition], first_seen: datetime, last_seen: datetime, confidence_level: int, ) -> Relationship: """Create a relationship.""" return Relationship( created_by_ref=author, relationship_type=relationship_type, source_ref=source.id, target_ref=target.id, object_marking_refs=object_marking_refs, custom_properties={ CustomProperties.FIRST_SEEN: first_seen, CustomProperties.LAST_SEEN: last_seen, CustomProperties.WEIGHT: confidence_level, }, )
def generate_case_bundle(self, case): markings = [] if case["tlp"] == 0: markings.append(TLP_WHITE) if case["tlp"] == 1: markings.append(TLP_GREEN) if case["tlp"] == 2: markings.append(TLP_AMBER) if case["tlp"] == 3: markings.append(TLP_RED) if len(markings) == 0: markings.append(TLP_WHITE) bundle_objects = [] incident = StixXOpenCTIIncident( id=OpenCTIStix2Utils.generate_random_stix_id("x-opencti-incident"), name=case["title"], description=case["description"], first_seen=datetime.utcfromtimestamp( int(case["createdAt"]) / 1000).strftime("%Y-%m-%dT%H:%M:%SZ"), last_seen=datetime.utcfromtimestamp( int(case["updatedAt"]) / 1000).strftime("%Y-%m-%dT%H:%M:%SZ"), object_marking_refs=markings, labels=case["tags"] if "tags" in case else [], created_by_ref=self.identity["standard_id"], ) bundle_objects.append(incident) # Get observables observables = self.thehive_api.get_case_observables( case_id=case["id"]).json() for observable in observables: if observable["dataType"] == "hash": if len(observable["data"]) == 32: data_type = "file_md5" elif len(observable["data"]) == 40: data_type = "file_sha1" elif len(observable["data"]) == 64: data_type = "file_sha256" else: data_type = "unknown" else: data_type = observable["dataType"] observable_key = OBSERVABLES_MAPPING[data_type] if observable_key is not None: stix_observable = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable"), key=observable_key, value=observable["data"], description=observable["message"], x_opencti_score=80 if observable["ioc"] else 50, object_marking_refs=markings, labels=observable["tags"] if "tags" in observable else [], created_by_ref=self.identity["standard_id"], x_opencti_create_indicator=observable["ioc"], ) stix_observable_relation = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="related-to", created_by_ref=self.identity["standard_id"], source_ref=stix_observable.id, target_ref=incident.id, object_marking_refs=markings, ) bundle_objects.append(stix_observable) bundle_objects.append(stix_observable_relation) if observable["sighted"]: fake_indicator_id = ( "indicator--c1034564-a9fb-429b-a1c1-c80116cc8e1e") stix_sighting = Sighting( id=OpenCTIStix2Utils.generate_random_stix_id( "sighting"), first_seen=datetime.utcfromtimestamp( int(observable["startDate"] / 1000)).strftime("%Y-%m-%dT%H:%M:%SZ"), last_seen=datetime.utcfromtimestamp( int(observable["startDate"] / 1000 + 3600)).strftime("%Y-%m-%dT%H:%M:%SZ"), where_sighted_refs=[self.identity["standard_id"]], sighting_of_ref=fake_indicator_id, custom_properties={ "x_opencti_sighting_of_ref": stix_observable.id }, ) bundle_objects.append(stix_sighting) bundle = Bundle(objects=bundle_objects).serialize() return bundle
def _send_knowledge(self, observable, report): bundle_objects = [] final_observable = observable if observable["entity_type"] in ["StixFile", "Artifact"]: final_observable = self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], key="hashes.MD5", value=report["md5"]) final_observable = self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], key="hashes.SHA-1", value=report["sha1"]) final_observable = self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], key="hashes.SHA-256", value=report["sha256"], ) if "name" not in final_observable or final_observable[ "name"] is None: self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], key="x_opencti_additional_names", value=report["submit_name"], operation="add", ) if final_observable["entity_type"] == "StixFile": self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], key="size", value=str(report["size"]), ) self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], key="x_opencti_score", value=str(report["threat_score"]), ) # Create external reference external_reference = self.helper.api.external_reference.create( source_name="Hybrid Analysis", url="https://www.hybrid-analysis.com/sample/" + report["sha256"], description="Hybrid Analysis Report", ) self.helper.api.stix_cyber_observable.add_external_reference( id=final_observable["id"], external_reference_id=external_reference["id"], ) # Create tags for tag in report["type_short"]: tag_ha = self.helper.api.label.create(value=tag, color="#0059f7") self.helper.api.stix_cyber_observable.add_label( id=final_observable["id"], label_id=tag_ha["id"]) # Attach the TTPs for tactic in report["mitre_attcks"]: if (tactic["malicious_identifiers_count"] > 0 or tactic["suspicious_identifiers_count"] > 0): attack_pattern = AttackPattern( id=OpenCTIStix2Utils.generate_random_stix_id( "attack-pattern"), created_by_ref=self.identity, name=tactic["technique"], custom_properties={ "x_mitre_id": tactic["attck_id"], }, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="uses", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=attack_pattern.id, object_marking_refs=[TLP_WHITE], ) bundle_objects.append(attack_pattern) bundle_objects.append(relationship) # Attach the domains for domain in report["domains"]: domain_stix = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable"), key="Domain-Name.value", value=domain, created_by_ref=self.identity, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="communicates-with", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=domain_stix.id, object_marking_refs=[TLP_WHITE], ) bundle_objects.append(domain_stix) bundle_objects.append(relationship) # Attach the IP addresses for host in report["hosts"]: host_stix = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable"), key=self.detect_ip_version(host) + ".value", value=host, created_by_ref=self.identity, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="communicates-with", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=host_stix.id, object_marking_refs=[TLP_WHITE], ) bundle_objects.append(host_stix) bundle_objects.append(relationship) # Attach other files for file in report["extracted_files"]: if file["threat_level"] > 0: file_stix = File( id=OpenCTIStix2Utils.generate_random_stix_id("file"), hashes={ "MD5": file["md5"], "SHA-1": file["sha1"], "SHA-256": file["sha256"], }, size=file["size"], name=file["name"], custom_properties={"x_opencti_labels": file["type_tags"]}, created_by_ref=self.identity, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="drops", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=file_stix.id, ) bundle_objects.append(file_stix) bundle_objects.append(relationship) for tactic in report["mitre_attcks"]: if (tactic["malicious_identifiers_count"] > 0 or tactic["suspicious_identifiers_count"] > 0): attack_pattern = AttackPattern( id=OpenCTIStix2Utils.generate_random_stix_id( "attack-pattern"), created_by_ref=self.identity, name=tactic["technique"], custom_properties={ "x_mitre_id": tactic["attck_id"], }, ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="uses", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=attack_pattern.id, ) bundle_objects.append(attack_pattern) bundle_objects.append(relationship) if len(bundle_objects) > 0: bundle = Bundle(objects=bundle_objects).serialize() bundles_sent = self.helper.send_stix2_bundle(bundle) return ("Sent " + str(len(bundles_sent)) + " stix bundle(s) for worker import") else: return "Nothing to attach"
def _send_knowledge(self, observable, report): bundle_objects = [] final_observable = observable target = report["target"]["file"] task_id = report["info"]["id"] final_observable = self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], input={ "key": "hashes.MD5", "value": target["md5"] }, ) final_observable = self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], input={ "key": "hashes.SHA-1", "value": target["sha1"] }, ) final_observable = self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], input={ "key": "hashes.SHA-256", "value": target["sha256"], }, ) self.helper.api.stix_cyber_observable.update_field( id=final_observable["id"], input={ "key": "x_opencti_score", "value": str(int(report["malscore"] * 10)), }, ) # Create external references # Analysis URL external_reference = self.helper.api.external_reference.create( source_name="CAPEv2 Sandbox Analysis", url=f"{self.cape_url}/analysis/{task_id}/", description="CAPEv2 Sandbox Analysis", ) self.helper.api.stix_cyber_observable.add_external_reference( id=final_observable["id"], external_reference_id=external_reference["id"], ) # JSON report external_reference = self.helper.api.external_reference.create( source_name="CAPEv2 Sandbox JSON Report", url=f"{self.cape_url}/filereport/{task_id}/json/", description="CAPEv2 Sandbox JSON Report", ) self.helper.api.stix_cyber_observable.add_external_reference( id=final_observable["id"], external_reference_id=external_reference["id"], ) # HTML Report external_reference = self.helper.api.external_reference.create( source_name="CAPEv2 Sandbox HTML Report", url=f"{self.cape_url}/filereport/{task_id}/html/", description="CAPEv2 Sandbox HTML Report", ) self.helper.api.stix_cyber_observable.add_external_reference( id=final_observable["id"], external_reference_id=external_reference["id"], ) # Create label if family was detected if "detections" in report and report["detections"]: label = self.helper.api.label.create(value=report["detections"], color="#0059f7") self.helper.api.stix_cyber_observable.add_label( id=final_observable["id"], label_id=label["id"]) # Create a Note containing the TrID results trid_json = json.dumps(report["trid"], indent=2) note = Note( abstract="TrID Analysis", content=f"```\n{trid_json}\n```", created_by_ref=self.identity, object_refs=[final_observable["standard_id"]], ) bundle_objects.append(note) # Attach the TTPs for tactic_dict in report["ttps"]: attack_id = tactic_dict["ttp"] signature = tactic_dict["signature"] attack_pattern = AttackPattern( id=OpenCTIStix2Utils.generate_random_stix_id("attack-pattern"), created_by_ref=self.identity, name=signature, custom_properties={ "x_mitre_id": attack_id, }, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="uses", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=attack_pattern.id, object_marking_refs=[TLP_WHITE], ) bundle_objects.append(attack_pattern) bundle_objects.append(relationship) # Handle procdumps and attach any Flare CAPA TTPs if "procdump" in report and report["procdump"]: # Download the zip archive of procdump files zip_contents = self._get_procdump_zip(task_id) zip_obj = io.BytesIO(zip_contents) # Extract with "infected" password zip_file = pyzipper.AESZipFile(zip_obj) zip_file.setpassword(b"infected") # Process each entry in the procdump key for procdump_dict in report["procdump"]: # If less noise was specified if self.less_noise: # and no Yara matches, skip this procdump if not procdump_dict["cape_yara"] or not procdump_dict[ "yara"]: continue sha256 = procdump_dict["sha256"] cape_type = procdump_dict["cape_type"] module_path = procdump_dict["module_path"] procdump_contents = zip_file.read(sha256) mime_type = magic.from_buffer(procdump_contents, mime=True) kwargs = { "file_name": module_path, "data": procdump_contents, "mime_type": mime_type, "x_opencti_description": cape_type, } response = self.helper.api.stix_cyber_observable.upload_artifact( **kwargs) self.helper.log_info( f'Uploaded procdump with sha256 "{sha256}" and type "{cape_type}".' ) # Build labels yara_rules = [] cape_yara_rules = [] yara_rules.extend( [yara_dict["name"] for yara_dict in procdump_dict["yara"]]) cape_yara_rules.extend([ yara_dict["name"] for yara_dict in procdump_dict["cape_yara"] ]) cape_yara_rules.extend([ yara_dict["meta"]["cape_type"] for yara_dict in procdump_dict["cape_yara"] ]) # Create and apply yara rule based labels for yara_rule in yara_rules: label = self.helper.api.label.create(value=yara_rule, color="#0059f7") self.helper.api.stix_cyber_observable.add_label( id=response["id"], label_id=label["id"]) # Create and apply cape yara rule based labels for cape_yara_rule in cape_yara_rules: label = self.helper.api.label.create(value=cape_yara_rule, color="#ff8178") self.helper.api.stix_cyber_observable.add_label( id=response["id"], label_id=label["id"]) # Create label for cape_type label = self.helper.api.label.create(value=cape_type, color="#0059f7") self.helper.api.stix_cyber_observable.add_label( id=response["id"], label_id=label["id"]) # Create relationship between uploaded procdump Artifact and original relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="related-to", created_by_ref=self.identity, source_ref=response["standard_id"], target_ref=final_observable["standard_id"], ) bundle_objects.append(relationship) # Handle Flare CAPA TTPs if "flare_capa" in procdump_dict and procdump_dict[ "flare_capa"]: attck_dict = procdump_dict["flare_capa"]["ATTCK"] for tactic in attck_dict: tp_list = attck_dict[tactic] for tp in tp_list: attack_pattern = AttackPattern( id=OpenCTIStix2Utils.generate_random_stix_id( "attack-pattern"), created_by_ref=self.identity, name=tactic, custom_properties={ "x_mitre_id": tp, }, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="uses", created_by_ref=self.identity, source_ref=response["standard_id"], target_ref=attack_pattern.id, object_marking_refs=[TLP_WHITE], ) bundle_objects.append(attack_pattern) bundle_objects.append(relationship) else: self.helper.log_info( f"Could not find flare_capa key or was empty in procdump {sha256}." ) # Close the zip file zip_file.close() else: self.helper.log_info( "Skipping processing process dumps, procdump key is empty or not exists." ) # Attach CnC addresses if any configs were found if "CAPE" in report and report["CAPE"]["configs"]: configs_list = report["CAPE"]["configs"] for config_dict in configs_list: for detection_name in config_dict: # Create a Note containing the config note = Note( abstract=f"{detection_name} Config", content= f"```\n{json.dumps(config_dict, indent=2)}\n```", created_by_ref=self.identity, object_refs=[final_observable["standard_id"]], ) bundle_objects.append(note) if not "address" in config_dict[detection_name]: self.helper.log_info( f'Could not find an "address" key in {detection_name} config.' ) continue address_list = config_dict[detection_name]["address"] for address in address_list: parsed = address.rsplit(":", 1)[0] if self._is_ipv4_address(parsed): host_stix = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable"), labels=[detection_name, "c2 server"], key="IPv4-Addr.value", value=parsed, created_by_ref=self.identity, ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="communicates-with", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=host_stix.id, ) bundle_objects.append(host_stix) bundle_objects.append(relationship) else: domain = urlparse(address).hostname domain_stix = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable"), labels=[detection_name, "c2 server"], key="Domain-Name.value", value=domain, created_by_ref=self.identity, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="communicates-with", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=domain_stix.id, ) bundle_objects.append(domain_stix) bundle_objects.append(relationship) # Attach the domains for domain_dict in report.get("network", {}).get("domains", []): domain_stix = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable"), key="Domain-Name.value", value=domain_dict["domain"], created_by_ref=self.identity, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="communicates-with", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=domain_stix.id, ) bundle_objects.append(domain_stix) bundle_objects.append(relationship) # Attach the IP addresses for host_dict in report.get("network", {}).get("hosts", []): host = host_dict["ip"] host_stix = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable"), key="IPv4-Addr.value", value=host, created_by_ref=self.identity, ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="communicates-with", created_by_ref=self.identity, source_ref=final_observable["standard_id"], target_ref=host_stix.id, ) bundle_objects.append(host_stix) bundle_objects.append(relationship) # Handle CAPE payloads and attach Flare CAPA TTPs if any if ("CAPE" in report and "payloads" in report["CAPE"] and report["CAPE"]["payloads"]): # Download the zip archive of payloads zip_contents = self._get_payloads_zip(task_id) zip_obj = io.BytesIO(zip_contents) # Extract with "infected" password zip_file = pyzipper.AESZipFile(zip_obj) zip_file.setpassword(b"infected") # Process each payload for payload_dict in report["CAPE"]["payloads"]: module_path = payload_dict["module_path"] sha256 = payload_dict["sha256"] cape_type = payload_dict["cape_type"] payload_contents = zip_file.read(sha256) mime_type = magic.from_buffer(payload_contents, mime=True) kwargs = { "file_name": module_path, "data": payload_contents, "mime_type": mime_type, "x_opencti_description": cape_type, } response = self.helper.api.stix_cyber_observable.upload_artifact( **kwargs) self.helper.log_info( f'Uploaded CAPE payload with sha256 "{sha256}" and type "{cape_type}".' ) # Create and apply label label = self.helper.api.label.create(value=cape_type, color="#ff8178") self.helper.api.stix_cyber_observable.add_label( id=response["id"], label_id=label["id"]) # Create relationship between uploaded payload Artifact and original relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="related-to", created_by_ref=self.identity, source_ref=response["standard_id"], target_ref=final_observable["standard_id"], ) bundle_objects.append(relationship) # Handle Flare CAPA TTPs if any if "flare_capa" in payload_dict and payload_dict["flare_capa"]: attck_dict = payload_dict["flare_capa"]["ATTCK"] for tactic in attck_dict: for tp in attck_dict[tactic]: attack_pattern = AttackPattern( id=OpenCTIStix2Utils.generate_random_stix_id( "attack-pattern"), created_by_ref=self.identity, name=tactic, custom_properties={ "x_mitre_id": tp, }, object_marking_refs=[TLP_WHITE], ) relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship"), relationship_type="uses", created_by_ref=self.identity, source_ref=response["standard_id"], target_ref=attack_pattern.id, object_marking_refs=[TLP_WHITE], ) bundle_objects.append(attack_pattern) bundle_objects.append(relationship) else: self.helper.log_info( f"Could not find flare_capa key or was empty in CAPE payload {sha256}." ) # Close the zip file zip_file.close() else: self.helper.log_info( "Skipping processing CAPE payloads, payloads key is empty or not exists." ) # Serialize and send all bundles if bundle_objects: bundle = Bundle(objects=bundle_objects, allow_custom=True).serialize() bundles_sent = self.helper.send_stix2_bundle(bundle) return f"Sent {len(bundles_sent)} stix bundle(s) for worker import" else: return "Nothing to attach"
def process_attribute(self, author, report_threats, attribute, generic_actor): resolved_attributes = self.resolve_type(attribute['type'], attribute['value']) if resolved_attributes is None: return None for resolved_attribute in resolved_attributes: # Default values attribute_threats = self.prepare_threats(attribute['Galaxy']) if 'Tag' in attribute: attribute_markings = self.resolve_markings(attribute['Tag']) else: attribute_markings = [TLP_WHITE] if len(report_threats) == 0 and len(attribute_threats) == 0: attribute_threats.append(generic_actor) indicator = Indicator( name='Indicator', description=attribute['comment'], pattern= "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']", labels=['malicious-activity'], created_by_ref=author, object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_observable_type': resolved_attribute['type'], 'x_opencti_observable_value': resolved_attribute['value'], }) relationships = [] for report_threat_ref in report_threats: relationships.append( Relationship(relationship_type='indicates', created_by_ref=author, source_ref=indicator.id, target_ref=report_threat_ref.id, description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp( int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp( int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level })) for attribute_threat_ref in attribute_threats: relationships.append( Relationship(relationship_type='indicates', created_by_ref=author, source_ref=indicator.id, target_ref=attribute_threat_ref.id, description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp( int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp( int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level })) return { 'indicator': indicator, 'relationships': relationships, 'attribute_threats': attribute_threats, 'markings': attribute_markings }
def process_attribute( self, author, event_elements, event_markings, attribute_external_references, attribute, ): try: resolved_attributes = self.resolve_type( attribute["type"], attribute["value"] ) if resolved_attributes is None: return None for resolved_attribute in resolved_attributes: ### Pre-process # Elements attribute_elements = self.prepare_elements(attribute["Galaxy"], author) # Markings & Tags attribute_tags = [] if "Tag" in attribute: attribute_markings = self.resolve_markings( attribute["Tag"], with_default=False ) attribute_tags = self.resolve_tags(attribute["Tag"]) if len(attribute_markings) == 0: attribute_markings = event_markings else: attribute_markings = event_markings ### Create the indicator observable_type = resolved_attribute["type"] observable_value = resolved_attribute["value"] name = resolved_attribute["value"] pattern_type = "stix" # observable type is yara for instance if observable_type in PATTERNTYPES: pattern_type = observable_type observable_type = "Unknown" genuine_pattern = ( "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']" ) pattern = observable_value name = ( attribute["comment"] if len(attribute["comment"]) > 0 else observable_type ) # observable type is not in stix 2 elif observable_type not in OPENCTISTIX2: return None # observable type is in stix else: if "transform" in OPENCTISTIX2[observable_type]: if ( OPENCTISTIX2[observable_type]["transform"]["operation"] == "remove_string" ): observable_value = observable_value.replace( OPENCTISTIX2[observable_type]["transform"]["value"], "" ) lhs = ObjectPath( OPENCTISTIX2[observable_type]["type"], OPENCTISTIX2[observable_type]["path"], ) genuine_pattern = str( ObservationExpression( EqualityComparisonExpression(lhs, observable_value) ) ) pattern = genuine_pattern indicator = Indicator( name=name, description=attribute["comment"], pattern=genuine_pattern, valid_from=datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), labels=["malicious-activity"], created_by_ref=author, object_marking_refs=attribute_markings, external_references=attribute_external_references, custom_properties={ "x_opencti_indicator_pattern": pattern, "x_opencti_observable_type": observable_type, "x_opencti_observable_value": observable_value, "x_opencti_pattern_type": pattern_type, "x_opencti_tags": attribute_tags, }, ) ### Create the relationships relationships = [] # Event threats for threat in ( event_elements["intrusion_sets"] + event_elements["malwares"] + event_elements["tools"] ): relationships.append( Relationship( relationship_type="indicates", created_by_ref=author, source_ref=indicator.id, target_ref=threat.id, description=attribute["comment"], object_marking_refs=attribute_markings, custom_properties={ "x_opencti_first_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_last_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_weight": self.helper.connect_confidence_level, }, ) ) # Attribute threats for threat in ( attribute_elements["intrusion_sets"] + attribute_elements["malwares"] + attribute_elements["tools"] ): relationships.append( Relationship( relationship_type="indicates", created_by_ref=author, source_ref=indicator.id, target_ref=threat.id, description=attribute["comment"], object_marking_refs=attribute_markings, custom_properties={ "x_opencti_first_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_last_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_weight": self.helper.connect_confidence_level, }, ) ) # Event Attack Patterns for attack_pattern in event_elements["attack_patterns"]: if len(event_elements["malwares"]) > 0: threats = event_elements["malwares"] elif len(event_elements["intrusion_sets"]) > 0: threats = event_elements["intrusion_sets"] else: threats = [] for threat in threats: relationship_uses = Relationship( relationship_type="uses", created_by_ref=author, source_ref=threat.id, target_ref=attack_pattern.id, description=attribute["comment"], object_marking_refs=attribute_markings, custom_properties={ "x_opencti_first_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_last_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_weight": self.helper.connect_confidence_level, "x_opencti_ignore_dates": True, }, ) relationships.append(relationship_uses) relationship_indicates = Relationship( relationship_type="indicates", created_by_ref=author, source_ref=indicator.id, target_ref="malware--fa42a846-8d90-4e51-bc29-71d5b4802168", # Fake description=attribute["comment"], object_marking_refs=attribute_markings, custom_properties={ "x_opencti_first_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_last_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_weight": self.helper.connect_confidence_level, "x_opencti_source_ref": indicator.id, "x_opencti_target_ref": relationship_uses.id, }, ) relationships.append(relationship_indicates) # Attribute Attack Patterns for attack_pattern in attribute_elements["attack_patterns"]: if len(attribute_elements["malwares"]) > 0: threats = attribute_elements["malwares"] elif len(attribute_elements["intrusion_sets"]) > 0: threats = attribute_elements["intrusion_sets"] else: threats = [] for threat in threats: relationship_uses = Relationship( relationship_type="uses", created_by_ref=author, source_ref=threat.id, target_ref=attack_pattern.id, description=attribute["comment"], object_marking_refs=attribute_markings, custom_properties={ "x_opencti_first_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_last_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_weight": self.helper.connect_confidence_level, "x_opencti_ignore_dates": True, }, ) relationships.append(relationship_uses) relationship_indicates = Relationship( relationship_type="indicates", created_by_ref=author, source_ref=indicator.id, target_ref="malware--fa42a846-8d90-4e51-bc29-71d5b4802168", # Fake description=attribute["comment"], object_marking_refs=attribute_markings, custom_properties={ "x_opencti_first_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_last_seen": datetime.utcfromtimestamp( int(attribute["timestamp"]) ).strftime("%Y-%m-%dT%H:%M:%SZ"), "x_opencti_weight": self.helper.connect_confidence_level, "x_opencti_source_ref": indicator.id, "x_opencti_target_ref": relationship_uses.id, "x_opencti_ignore_dates": True, }, ) relationships.append(relationship_indicates) return { "indicator": indicator, "relationships": relationships, "attribute_elements": attribute_elements, "markings": attribute_markings, } except: return None
def process_attribute(self, author, event_elements, event_markings, attribute): resolved_attributes = self.resolve_type(attribute['type'], attribute['value']) if resolved_attributes is None: return None for resolved_attribute in resolved_attributes: ### Pre-process # Elements attribute_elements = self.prepare_elements(attribute['Galaxy']) # Markings if 'Tag' in attribute: attribute_markings = self.resolve_markings(attribute['Tag'], with_default=False) if len(attribute_markings) == 0: attribute_markings = event_markings else: attribute_markings = event_markings ### Create the indicator observable_type = resolved_attribute['type'] observable_value = resolved_attribute['value'] pattern_type = 'stix' if observable_type in PATTERNTYPES: pattern_type = observable_type elif observable_type not in OPENCTISTIX2: return None else: if 'transform' in OPENCTISTIX2[observable_type]: if OPENCTISTIX2[observable_type]['transform']['operation'] == 'remove_string': observable_value = observable_value.replace(OPENCTISTIX2[observable_type]['transform']['value'], '') lhs = ObjectPath(OPENCTISTIX2[observable_type]['type'], OPENCTISTIX2[observable_type]['path']) observable_value = ObservationExpression(EqualityComparisonExpression(lhs, observable_value)) try: indicator = Indicator( name=resolved_attribute['value'], description=attribute['comment'], pattern=str(observable_value), valid_from=datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime('%Y-%m-%dT%H:%M:%SZ'), labels=['malicious-activity'], created_by_ref=author, object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_observable_type': resolved_attribute['type'], 'x_opencti_observable_value': resolved_attribute['value'], 'x_opencti_pattern_type': pattern_type } ) except: return None ### Create the relationships relationships = [] # Event threats for threat in (event_elements['intrusion_sets'] + event_elements['malwares'] + event_elements['tools']): relationships.append( Relationship( relationship_type='indicates', created_by_ref=author, source_ref=indicator.id, target_ref=threat.id, description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level } ) ) # Attribute threats for threat in (attribute_elements['intrusion_sets'] + attribute_elements['malwares'] + attribute_elements[ 'tools']): relationships.append( Relationship( relationship_type='indicates', created_by_ref=author, source_ref=indicator.id, target_ref=threat.id, description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level } ) ) # Event Attack Patterns for attack_pattern in event_elements['attack_patterns']: if len(event_elements['malwares']) > 0: threats = event_elements['malwares'] elif len(event_elements['intrusion_sets']) > 0: threats = event_elements['intrusion_sets'] else: threats = [] for threat in threats: relationship_uses = Relationship( relationship_type='uses', created_by_ref=author, source_ref=threat.id, target_ref=attack_pattern.id, description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level, 'x_opencti_ignore_dates': True } ) relationships.append(relationship_uses) relationship_indicates = Relationship( relationship_type='indicates', created_by_ref=author, source_ref=indicator.id, target_ref='malware--fa42a846-8d90-4e51-bc29-71d5b4802168', # Fake description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level, 'x_opencti_source_ref': indicator.id, 'x_opencti_target_ref': relationship_uses.id } ) relationships.append(relationship_indicates) # Attribute Attack Patterns for attack_pattern in attribute_elements['attack_patterns']: if len(attribute_elements['malwares']) > 0: threats = attribute_elements['malwares'] elif len(attribute_elements['intrusion_sets']) > 0: threats = attribute_elements['intrusion_sets'] else: threats = [] for threat in threats: relationship_uses = Relationship( relationship_type='uses', created_by_ref=author, source_ref=threat.id, target_ref=attack_pattern.id, description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level, 'x_opencti_ignore_dates': True } ) relationships.append(relationship_uses) relationship_indicates = Relationship( relationship_type='indicates', created_by_ref=author, source_ref=indicator.id, target_ref='malware--fa42a846-8d90-4e51-bc29-71d5b4802168', # Fake description=attribute['comment'], object_marking_refs=attribute_markings, custom_properties={ 'x_opencti_first_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_last_seen': datetime.utcfromtimestamp(int(attribute['timestamp'])).strftime( '%Y-%m-%dT%H:%M:%SZ'), 'x_opencti_weight': self.helper.connect_confidence_level, 'x_opencti_source_ref': indicator.id, 'x_opencti_target_ref': relationship_uses.id, 'x_opencti_ignore_dates': True } ) relationships.append(relationship_indicates) return { 'indicator': indicator, 'relationships': relationships, 'attribute_elements': attribute_elements, 'markings': attribute_markings }
try: title, pattern = ioc_to_title_and_pattern(ioc) except Exception as e: logging.error(f"Skipping indicator: {e}") continue description = " ".join( title.split()[:2]) + f" involved with {args.threat_name}" indicator = Indicator(labels="malicious-activity", pattern_type='stix', pattern=pattern, valid_from=datetime.now(), description=description, name=title, created_by_ref=identity) relationship = Relationship(relationship_type='indicates', source_ref=indicator.id, target_ref=malware.id) objects.append(indicator) objects.append(relationship) for ap in aps: relationship = Relationship(relationship_type='indicates', source_ref=indicator.id, target_ref=ap.id) objects.append(relationship) bundle = Bundle(objects=objects) if args.output: with open(args.output, 'w') as f: f.write(str(bundle)) else:
def process_yara_rules(self) -> None: try: rules_json = self.valhalla_client.get_rules_json() response = ApiResponse.parse_obj(rules_json) except Exception as err: self.helper.log_error(f"error downloading rules: {err}") return None for yr in response.rules: # Handle reference URLs supplied by the Valhalla API refs = [] if yr.reference is not None and yr.reference != "" and yr.reference != "-": try: san_url = urlparse(yr.reference) ref = ExternalReference( source_name="Nextron Systems Valhalla API", url=san_url.geturl(), description="Rule Reference: " + san_url.geturl(), ) refs.append(ref) except Exception: self.helper.log_error( f"error parsing ref url: {yr.reference}") continue indicator = Indicator( name=yr.name, description=yr.cti_description, pattern_type="yara", pattern=yr.content, labels=yr.tags, valid_from=yr.cti_date, valid_until=datetime.utcnow() + relativedelta(years=2), object_marking_refs=[self.default_marking], created_by_ref=self.organization, confidence=self.confidence_level, external_references=refs, custom_properties={ "x_opencti_main_observable_type": "StixFile", "x_opencti_detection": True, "x_opencti_score": yr.score, }, ) self.bundle_objects.append(indicator) # Handle Tags - those include MITRE ATT&CK tags that we want to # create relationships for for tag in yr.tags: # handle Mitre ATT&CK relation indicator <-> attack-pattern if re.search(r"^T\d{4}$", tag): attack_pattern_id = self._ATTACK_MAPPING.get(tag) if attack_pattern_id is None or attack_pattern_id == "": self.helper.log_info( f"no attack_pattern found for {tag}") return None ap_rel = Relationship( relationship_type="indicates", source_ref=indicator, target_ref=attack_pattern_id, description="Yara Rule from Valhalla API", created_by_ref=self.organization, confidence=self.confidence_level, object_marking_refs=[self.default_marking], ) self.bundle_objects.append(ap_rel) # handle Mitre ATT&CK group relation indicator <-> intrusion-set if re.search(r"^G\d{4}$", tag): intrusion_set_id = self._ATTACK_MAPPING.get(tag) if intrusion_set_id == "" or intrusion_set_id is None: self.helper.log_info( f"no intrusion_set found for {tag}") return None is_rel = Relationship( relationship_type="indicates", source_ref=indicator, target_ref=intrusion_set_id, description="Yara Rule from Valhalla API", created_by_ref=self.organization, confidence=self.confidence_level, object_marking_refs=[self.default_marking], ) self.bundle_objects.append(is_rel)
def _process_results(self, observable, results): bundle_objects = [] unpack_id = results["id"] # Create external reference analysis_url = f"https://www.unpac.me/results/{unpack_id}" external_reference = self.helper.api.external_reference.create( source_name="UnpacMe Results", url=analysis_url, description="UnpacMe Results", ) self.helper.api.stix_cyber_observable.add_external_reference( id=observable["id"], external_reference_id=external_reference["id"], ) # Create default labels extracted_label = self.helper.api.label.create( value="extracted", color=self.default_tag_color) # Parse the results label_ids = [] for result_dict in results["results"]: sha256 = result_dict["hashes"]["sha256"] # If less noise, check to ensure the files were identified as malware if self.less_noise: self.helper.log_info("Less noise is enabled.") if not result_dict["malware_id"]: self.helper.log_info( f"Skipping upload of {sha256} as it had no matching family." ) continue # Download the file file_contents = self.unpacme_client.download(sha256=sha256) # Upload as Artifact to OpenCTI mime_type = magic.from_buffer(file_contents, mime=True) kwargs = { "file_name": sha256, "data": file_contents, "mime_type": mime_type, "x_opencti_description": "UnpacMe extracted file.", } response = self.helper.api.stix_cyber_observable.upload_artifact( **kwargs) # Create Relationship between original and newly uploaded Artifact relationship = Relationship( id=OpenCTIStix2Utils.generate_random_stix_id("relationship"), relationship_type="related-to", created_by_ref=self.identity, source_ref=response["standard_id"], target_ref=observable["standard_id"], ) bundle_objects.append(relationship) # Attach default "extracted" label if response["id"] != observable["id"]: self.helper.api.stix_cyber_observable.add_label( id=response["id"], label_id=extracted_label["id"]) # If found malware ids, attach as labels for malware_id_dict in result_dict["malware_id"]: family_label = self.helper.api.label.create( value=malware_id_dict["name"], color=self.family_color) self.helper.api.stix_cyber_observable.add_label( id=response["id"], label_id=family_label["id"]) label_ids.append(family_label["id"]) # Attach all identified tags to the Artifact for label_id in label_ids: self.helper.api.stix_cyber_observable.add_label( id=observable["id"], label_id=family_label["id"]) # Serialize and send all bundles if bundle_objects: bundle = Bundle(objects=bundle_objects, allow_custom=True).serialize() bundles_sent = self.helper.send_stix2_bundle(bundle) return f"Sent {len(bundles_sent)} stix bundle(s) for worker import" else: return "Nothing to attach"
def update_list(bundle_id: str, raw_companies_file: str, companies: dict, sectors: dict) -> Bundle: bundles = [] with open(raw_companies_file) as csv_file: csv_dict_reader = DictReader(csv_file, delimiter=',') for row in csv_dict_reader: if row['createdBy'] == "ANSSI": creator = anssi else: creator = row['createdBy'] # has to be a STIX ID entity_id = None entity_creation_date = None if row['name'] in companies: entity_id = companies[row['name']]['id'] entity_creation_date = companies[row['name']]['created'] if row['x_opencti_aliases']: aliases = row['x_opencti_aliases'].split(',') else: aliases = [] if row['other_stix_ids']: stix_ids = row['x_opencti_stix_ids'].split(',') else: stix_ids = [] markings = row['objectMarking'].split(',') company = Identity( id=entity_id, name=row['name'], created=entity_creation_date, description=row['description'], contact_information=row['contact_information'], roles="", # There's no real point for having a role here. Only companies/entities here, no people identity_class='organization', created_by_ref=creator, object_marking_refs=[TLP_WHITE], custom_properties={ 'x_opencti_aliases': aliases, 'x_opencti_organization_type': row['x_opencti_organization_type'], 'x_opencti_stix_ids': stix_ids, } ) bundles.append(company) company_sectors = row['sectors'].split(',') for company_sector in company_sectors: if company_sector == '': print(f"Error: Organization {row['name']} has no Sector assigned!!") return [] relevant_sector = sectors.get(company_sector, None) # search through aliases if relevant_sector is None: for sector_name, sector in sectors.items(): aliases = sector.get('x_opencti_aliases', []) if company_sector in aliases: relevant_sector = sector if relevant_sector is None: print(f"Sector '{company_sector}' for company '{row['name']}' can't be found") continue relationship_id = None relationship_name = f"{company.id}_{relevant_sector['id']}" relationship_creation_date = None if relationship_name in companies: relationship_id = companies[relationship_name]['id'] relationship_creation_date = companies[relationship_name]['created'] sector_relationship = Relationship( id=relationship_id, created=relationship_creation_date, relationship_type='part-of', source_ref=company.id, target_ref=relevant_sector['id'], description=f"Company '{row['name']}' is part of sector '{company_sector}'", confidence=100, created_by_ref=creator, object_marking_refs=[TLP_WHITE], ) bundles.append(sector_relationship) if bundle_id: bundle = Bundle( objects=bundles, id=bundle_id, allow_custom=True ) else: bundle = Bundle( objects=bundles, allow_custom=True ) return bundle