def test_object_factory_list_replace(): ext_ref = stix2.ExternalReference(source_name="ACME Threat Intel", description="Threat report from ACME") ext_ref2 = stix2.ExternalReference(source_name="Yet Another Threat Report", description="Threat report from YATR") factory = stix2.ObjectFactory(external_references=ext_ref, list_append=False) ind = factory.create(stix2.Indicator, external_references=ext_ref2, **INDICATOR_KWARGS) assert len(ind.external_references) == 1 assert ind.external_references[0].source_name == "Yet Another Threat Report"
def test_object_factory_list_append(): ext_ref = stix2.ExternalReference(source_name="ACME Threat Intel", description="Threat report from ACME") ext_ref2 = stix2.ExternalReference(source_name="Yet Another Threat Report", description="Threat report from YATR") ext_ref3 = stix2.ExternalReference(source_name="Threat Report #3", description="One more threat report") factory = stix2.ObjectFactory(external_references=ext_ref) ind = factory.create(stix2.Indicator, external_references=ext_ref2, **INDICATOR_KWARGS) assert ind.external_references[1].source_name == "Yet Another Threat Report" ind = factory.create(stix2.Indicator, external_references=[ext_ref2, ext_ref3], **INDICATOR_KWARGS) assert ind.external_references[2].source_name == "Threat Report #3"
def test_external_reference_capec(): ref = stix2.ExternalReference( source_name="capec", external_id="CAPEC-550", ) assert str(ref) == CAPEC assert re.match("ExternalReference\(source_name=u?'capec', external_id=u?'CAPEC-550'\)", repr(ref))
def test_external_reference_capec_url(): ref = stix2.ExternalReference( source_name="capec", external_id="CAPEC-550", url="http://capec.mitre.org/data/definitions/550.html", ) assert str(ref) == CAPEC_URL
def test_external_reference_bugzilla(): ref = stix2.ExternalReference( source_name="ACME Bugzilla", external_id="1370", url="https://www.example.com/bugs/1370", ) assert str(ref) == BUGZILLA
def test_external_reference_threat_report(): ref = stix2.ExternalReference( source_name="ACME Threat Intel", description="Threat report", url="http://www.example.com/threat-report.pdf", ) assert str(ref) == THREAT_REPORT
def test_external_reference_veris(): ref = stix2.ExternalReference( source_name="veris", external_id="0001AA7F-C601-424A-B2B8-BE6C9F5164E7", url= "https://github.com/vz-risk/VCDB/blob/master/data/json/0001AA7F-C601-424A-B2B8-BE6C9F5164E7.json", ) assert str(ref) == VERIS
def test_external_reference_source_required(): with pytest.raises(stix2.exceptions.MissingFieldsError) as excinfo: stix2.ExternalReference() assert excinfo.value.cls == stix2.ExternalReference assert excinfo.value.fields == ["source_name"] assert str( excinfo.value ) == "Missing required field(s) for ExternalReference: (source_name)."
def test_object_factory_external_resource(): ext_ref = stix2.ExternalReference(source_name="ACME Threat Intel", description="Threat report") factory = stix2.ObjectFactory(external_references=ext_ref) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert ind.external_references[0].source_name == "ACME Threat Intel" assert ind.external_references[0].description == "Threat report" ind2 = factory.create(stix2.Indicator, external_references=None, **INDICATOR_KWARGS) assert 'external_references' not in ind2
def test_external_reference_offline(): ref = stix2.ExternalReference( source_name="ACME Threat Intel", description="Threat report", ) assert str(ref) == OFFLINE assert re.match("ExternalReference\(source_name=u?'ACME Threat Intel', description=u?'Threat report'\)", repr(ref)) # Yikes! This works assert eval("stix2." + repr(ref)) == ref
def test_external_reference_capec(): ref = stix2.ExternalReference( source_name="capec", external_id="CAPEC-550", ) assert str(ref) == CAPEC assert repr( ref ) == "ExternalReference(external_id='CAPEC-550', source_name='capec')"
def test_external_reference_veris(): ref = stix2.ExternalReference( source_name="veris", external_id="0001AA7F-C601-424A-B2B8-BE6C9F5164E7", hashes={ "SHA-256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b" }, url="https://github.com/vz-risk/VCDB/blob/master/data/json/0001AA7F-C601-424A-B2B8-BE6C9F5164E7.json", ) assert str(ref) == VERIS
def test_external_reference_offline(): ref = stix2.ExternalReference( source_name="ACME Threat Intel", description="Threat report", ) assert str(ref) == OFFLINE assert repr( ref ) == "ExternalReference(description='Threat report', source_name='ACME Threat Intel')" # Yikes! This works assert eval("stix2." + repr(ref)) == ref
def test_vulnerability_example(): vulnerability = stix2.Vulnerability( id="vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061", created="2016-05-12T08:17:27.000Z", modified="2016-05-12T08:17:27.000Z", name="CVE-2016-1234", external_references=[ stix2.ExternalReference(source_name='cve', external_id="CVE-2016-1234"), ], ) assert str(vulnerability) == EXPECTED
def test_vulnerability_example(): vulnerability = stix2.v21.Vulnerability( id=VULNERABILITY_ID, created="2016-05-12T08:17:27.000Z", modified="2016-05-12T08:17:27.000Z", name="CVE-2016-1234", external_references=[ stix2.ExternalReference( source_name='cve', external_id="CVE-2016-1234", ), ], ) assert vulnerability.serialize(pretty=True) == EXPECTED
def Maker(self): flag=False if self.sourcetext.get() == "": tk.messagebox.showerror("Error", "Source name cannot be empty!", parent=self) return dict = {} dict.update({"source_name" : self.sourcetext.get()}) if not (len(self.desctext.get())==0): dict.update({"description": self.desctext.get()}) flag=True if not (len(self.urltext.get())==0): dict.update({"url": self.urltext.get()}) flag=True if not (len(self.hashestext.get())==0): dict2 = {} dict2.update({self.hash_var.get() : self.hashestext.get()}) dict.update({"hashes" : dict2}) if not (len(self.ext_idtext.get())==0): dict.update({"external_id": self.ext_idtext.get()}) flag=True if not (flag): tk.messagebox.showerror("Error", "At least one of the Description, URL, or External ID properties must be present.", parent=self) return try: exreftofile(self.sourcetext.get(), stix2.ExternalReference(**dict)) except Exception as e: tk.messagebox.showerror("Error", str(e), parent=self) return self.getlist() tk.messagebox.showinfo("Success", "External Reference created successfully!", parent=self) self.sourcetext.delete(0, tk.END) self.desctext.delete(0, tk.END) self.urltext.delete(0, tk.END) self.hashestext.delete(0, tk.END) self.ext_idtext.delete(0, tk.END)
def process_reports(self, reports): if reports is None: printer.error("No results") return for report in reports: name = report["name"] id = report["id"] stix2_objects = [] stix2_object_refs = [] # FFS AV, consistency! if 'tlp' in report: tlp_id = REF_TLPS[report['tlp'].upper()] elif 'TLP' in report: tlp_id = REF_TLPS[report['TLP'].upper()] else: tlp_id = REF_TLPS['WHITE'] sectors = report['industries'] if sectors: unmatched_sectors = [] added_sector = False for sector in [html.unescape(x.upper()) for x in sectors]: sector_name = None sector_id = None if sector in SECTOR_MAPPINGS: # sector_ids.append(self.octi_sectors[SECTOR_MAPPINGS[sector]]) sector_name = SECTOR_MAPPINGS[sector] try: sector_id = self.octi_sectors[ SECTOR_MAPPINGS[sector]] except Exception as e: printer.error(e) continue else: printer.debug(f"Looking for sector {sector}") match = difflib.get_close_matches( sector, self.octi_sectors.keys(), 1) if not len(match): printer.error( f"Unable to determine a matching sector for {sector}" ) unmatched_sectors.append(sector) continue # sector_ids.append(self.octi_sectors[match[0]]) sector_name = match[0] sector_id = self.octi_sectors[match[0]] if sector_name is not None: s = stix2.Identity(id=sector_id, name=sector_name, identity_class='class', custom_properties={ 'x_opencti_identity_type': 'sector' }) printer.debug(f"Adding sector {sector_name}") stix2_objects.append(s) stix2_object_refs.append(s) added_sector = True if not added_sector: printer.warn("Adding 'UNKNOWN' placeholder sector") s = stix2.Identity(id=self.octi_sectors["UNKNOWN"], name="Unknown", identity_class='class', custom_properties={ 'x_opencti_identity_type': 'sector' }) stix2_objects.append(s) stix2_object_refs.append(s) description = report['description'] if len(unmatched_sectors): description = description + "\n\n###\nUnable to find a match for the following sectors, " \ "please review manually:\n - " + '\n - '.join(unmatched_sectors) printer.info(f"Generating STIX2 for {name} ({id})") author = stix2.Identity(name=report['author_name'], identity_class='organization') stix2_objects.append(author) adversary = None if report['adversary']: printer.debug("Adding adversary {}".format( report['adversary'])) adversary = stix2.IntrusionSet(name=report['adversary']) stix2_object_refs.append(adversary) stix2_objects.append(adversary) if report['targeted_countries']: for country in report['targeted_countries']: printer.debug(f"Adding country {country}") c = stix2.Identity(name=country, identity_class='organization', custom_properties={ 'x_opencti_identity_type': 'country' }) stix2_objects.append(c) stix2_object_refs.append(c) external_refs = [] for eref in report['references']: external_refs.append( stix2.ExternalReference(source_name=tldextract.extract( eref).registered_domain, url=eref)) indicators = report["indicators"] if indicators: for indicator in indicators: resolved_type = self.resolve_type( indicator["type"].lower()) if resolved_type != None and indicator["is_active"]: observable_type = resolved_type observable_value = indicator["indicator"] pattern_type = 'stix' try: if observable_type in PATTERNTYPES: pattern_type = observable_type elif observable_type not in OPENCTISTIX2: printer.info("Not in stix2 dict") 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 = stix2.ObjectPath( OPENCTISTIX2[observable_type]['type'], OPENCTISTIX2[observable_type]['path']) observable_value = stix2.ObservationExpression( stix2.EqualityComparisonExpression( lhs, observable_value)) except Exception as e: printer.error(e) printer.info( "Could not determine suitable pattern") try: indicator_obj = stix2.Indicator( name=indicator["indicator"], description=indicator["description"], pattern=str(observable_value), valid_from=indicator["created"], labels=['malicious-activity'], created_by_ref=author, object_marking_refs=[tlp_id], custom_properties={ 'x_opencti_observable_type': resolved_type, 'x_opencti_observable_value': indicator["indicator"], 'x_opencti_pattern_type': pattern_type }) stix2_object_refs.append(indicator_obj) stix2_objects.append(indicator_obj) except Exception as e: printer.error(e) printer.info("Couldn't fetch indicator") else: printer.error("No indicators") report = stix2.Report(name=name, description=description, created_by_ref=author, labels=['threat-report'], published=report['created'], created=report['created'], modified=report['modified'], object_refs=stix2_object_refs, object_marking_refs=[tlp_id], external_references=external_refs) stix2_objects.append(report) bundle = stix2.Bundle(stix2_objects).serialize() if not self.dryrun: self.opencti_connector_helper.send_stix2_bundle( bundle, None, True, False) printer.info("Sending to OpenCTI") #printer.debug(str(bundle)) else: printer.debug(f"No sectors, disregarding '{name}'")
def run(self): self.helper.log_info("Fetching data CYBERCRIME-TRACKER.NET...") tlp = self.helper.api.marking_definition.read( filters=[ {"key": "definition", "values": "TLP:{}".format(self.connector_tlp)} ] ) while True: try: # Get the current timestamp and check timestamp = int(time.time()) current_state = self.helper.get_state() if current_state is not None and "last_run" in current_state: last_run = current_state["last_run"] self.helper.log_info( "Connector last run: {}".format( datetime.datetime.utcfromtimestamp(last_run).strftime( "%Y-%m-%d %H:%M:%S" ) ) ) else: last_run = None self.helper.log_info("Connector has never run") # Run if it is the first time or we are past the interval if last_run is None or ((timestamp - last_run) > self.interval): self.helper.log_info("Connector will run!") now = datetime.datetime.utcfromtimestamp(timestamp) friendly_name = "MITRE run @ " + now.strftime("%Y-%m-%d %H:%M:%S") work_id = self.helper.api.work.initiate_work( self.helper.connect_id, friendly_name ) # Get Feed Content feed = feedparser.parse(self.feed_url) self.helper.log_info( "Found: {} entries.".format(len(feed["entries"])) ) self.feed_summary = { "Source": feed["feed"]["title"], "Date": self._time_to_datetime( feed["feed"]["published_parsed"] ), "Details": feed["feed"]["subtitle"], "Link": feed["feed"]["link"], } # Create the bundle bundle_objects = list() organization = stix2.Identity( id=OpenCTIStix2Utils.generate_random_stix_id("identity"), name="CYBERCRIME-TRACKER.NET", identity_class="organization", description="Tracker collecting and sharing daily updates of C2 IPs/Urls. http://cybercrime-tracker.net", ) bundle_objects.append(organization) for entry in feed["entries"]: parsed_entry = self.parse_feed_entry(entry) external_reference = stix2.ExternalReference( source_name="{}".format(self.feed_summary["Source"]), url=parsed_entry["ext_link"], ) indicator_pattern = self.gen_indicator_pattern(parsed_entry) malware = stix2.Malware( id=OpenCTIStix2Utils.generate_random_stix_id("malware"), is_family=True, name=parsed_entry["type"], description="{} malware.".format(parsed_entry["type"]), ) bundle_objects.append(malware) indicator = None if self.create_indicators: indicator = stix2.Indicator( id=OpenCTIStix2Utils.generate_random_stix_id( "indicator" ), name=parsed_entry["url"], description="C2 URL for: {}".format( parsed_entry["type"] ), labels=["C2 Server"], pattern_type="stix", pattern=indicator_pattern, valid_from=parsed_entry["date"], created=parsed_entry["date"], modified=parsed_entry["date"], created_by_ref=organization.id, object_marking_refs=[tlp["standard_id"]], external_references=[external_reference], custom_properties={ "x_opencti_main_observable_type": "Url" }, ) bundle_objects.append(indicator) relation = stix2.Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship" ), source_ref=indicator.id, target_ref=malware.id, relationship_type="indicates", start_time=self._time_to_datetime( entry["published_parsed"] ), stop_time=self._time_to_datetime( entry["published_parsed"] ) + datetime.timedelta(0, 3), description="URLs associated to: " + parsed_entry["type"], confidence=self.confidence_level, created_by_ref=organization.id, object_marking_refs=[tlp["standard_id"]], created=parsed_entry["date"], modified=parsed_entry["date"], external_references=[external_reference], ) bundle_objects.append(relation) if self.create_observables: observable_url = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable" ), key="Url.value", labels=["C2 Server"], value=parsed_entry["url"], created_by_ref=organization.id, object_marking_refs=[tlp["standard_id"]], external_references=[external_reference], ) bundle_objects.append(observable_url) observable_ip = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable" ), key="IPv4-Addr.value", labels=["C2 Server"], value=parsed_entry["ip"], created_by_ref=organization.id, object_marking_refs=[tlp["standard_id"]], external_references=[external_reference], ) bundle_objects.append(observable_ip) observable_domain = None if "domain" in parsed_entry.keys(): observable_domain = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable" ), key="Domain-Name.value", labels=["C2 Server"], value=parsed_entry["domain"], created_by_ref=organization.id, object_marking_refs=[tlp["standard_id"]], external_references=[external_reference], ) bundle_objects.append(observable_domain) if indicator is not None: relationship_1 = stix2.Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship" ), relationship_type="based-on", created_by_ref=organization.id, source_ref=indicator.id, target_ref=observable_url.id, ) bundle_objects.append(relationship_1) relationship_2 = stix2.Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship" ), relationship_type="based-on", created_by_ref=organization.id, source_ref=indicator.id, target_ref=observable_ip.id, ) bundle_objects.append(relationship_2) if observable_domain is not None: relationship_3 = stix2.Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship" ), relationship_type="based-on", created_by_ref=organization.id, source_ref=indicator.id, target_ref=observable_domain.id, ) bundle_objects.append(relationship_3) # create stix bundle bundle = stix2.Bundle(objects=bundle_objects) # send data self.helper.send_stix2_bundle( bundle=bundle.serialize(), update=self.update_existing_data, work_id=work_id, ) # Store the current timestamp as a last run message = ( "Connector successfully run, storing last_run as: {}".format( str(timestamp) ) ) self.helper.log_info(message) self.helper.set_state({"last_run": timestamp}) self.helper.api.work.to_processed(work_id, message) self.helper.log_info( "Last_run stored, next run in: {} seconds.".format( str(round(self.interval, 2)) ) ) time.sleep(60) else: new_interval = self.interval - (timestamp - last_run) self.helper.log_info( "Connector will not run. \ Next run in: {} seconds.".format( str(round(new_interval, 2)) ) ) time.sleep(60) except (KeyboardInterrupt, SystemExit): self.helper.log_info("Connector stop") exit(0) except Exception as e: self.helper.log_error(str(e)) time.sleep(60)
def fetch_and_send(self): bundle_objects = list() # create an identity for the coalition team organization = stix2.Identity( name="Cyber Threat Coalition Team", identity_class="organization", description="Team of Experts collecting and sharing pandemic related " "cyber threat intelligence during the COVID-19 crisis time", ) # add organization in bundle bundle_objects.append(organization) report_object_refs = list() for collection in ["domain", "ip", "url", "hash"]: # fetch backlist url = self.cyber_threat_coalition_base_url + "/" + str(collection) + ".txt" response = requests.get(url=url) if response.status_code != 200: raise Exception( "Unable to fetch {0} blacklist, server returned status: {1}", collection, response.status_code, ) opencti_type = None pattern_type = "stix" tags = [{"tag_type": "Event", "value": "COVID-19", "color": "#fc036b"}] # parse content for data in response.iter_lines(decode_unicode=True): if data and not data.startswith("#"): if collection == "domain": opencti_type = "domain" elif collection == "ip": opencti_type = "ipv4-addr" elif collection == "url": opencti_type = "url" data = urllib.parse.quote(data, "/:") elif collection == "hash": opencti_type = self.get_hash_type(data) try: indicator = stix2.Indicator( name=data, pattern=self._OPENCTI_TYPE[opencti_type].format(data), labels=["malicious-activity"], created_by_ref=organization, object_marking_refs=[stix2.TLP_WHITE], custom_properties={ CustomProperties.OBSERVABLE_TYPE: opencti_type, CustomProperties.OBSERVABLE_VALUE: data, CustomProperties.PATTERN_TYPE: pattern_type, CustomProperties.TAG_TYPE: tags, }, ) except Exception as ex: self.helper.log_error( "an exception occurred while converting data to STIX indicator " "for data.value: {} , skipping IOC, exception: {}".format( data, ex ) ) continue # add indicator in bundle and report_refs bundle_objects.append(indicator) report_object_refs.append(indicator["id"]) # create a global threat report report_uuid = "report--552b3ae6-8522-409d-8b72-a739bc1926aa" report_external_reference = stix2.ExternalReference( source_name="Cyber Threat Coalition", url="https://www.cyberthreatcoalition.org", external_id="COVID19-CTC", ) stix_report = stix2.Report( id=report_uuid, name="COVID-19 Cyber Threat Coalition (CTC) BlackList", type="report", description="This report represents the whole COVID-19 CTC blacklist.", published=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), created_by_ref=organization, object_marking_refs=[stix2.TLP_WHITE], labels=["threat-report"], external_references=[report_external_reference], object_refs=report_object_refs, custom_properties={CustomProperties.TAG_TYPE: tags,}, ) # add report in bundle bundle_objects.append(stix_report) # create stix bundle bundle = stix2.Bundle(objects=bundle_objects) # send data self.helper.send_stix2_bundle( bundle=bundle.serialize(), update=self.update_existing_data )
def test_external_reference_source_required(): with pytest.raises(stix2.exceptions.MissingPropertiesError) as excinfo: stix2.ExternalReference() assert excinfo.value.cls == stix2.ExternalReference assert excinfo.value.properties == ["source_name"]
identity_class="unknown") init_comp = stix2.KillChainPhase( kill_chain_name="mandiant-attack-lifecycle-model", phase_name="initial-compromise") malware = stix2.Malware(id="malware--d1c612bc-146f-4b65-b7b0-9a54a14150a4", created="2015-04-23T11:12:34.760Z", modified="2015-04-23T11:12:34.760Z", name="Poison Ivy Variant d1c6", labels=["remote-access-trojan"], kill_chain_phases=[init_comp]) ref = stix2.ExternalReference( source_name="capec", description="phishing", url="https://capec.mitre.org/data/definitions/98.html", external_id="CAPEC-98") attack_pattern = stix2.AttackPattern( id="attack-pattern--8ac90ff3-ecf8-4835-95b8-6aea6a623df5", created="2015-05-07T14:22:14.760Z", modified="2015-05-07T14:22:14.760Z", name="Phishing", description="Spear phishing used as a delivery mechanism for malware.", kill_chain_phases=[init_comp], external_references=[ref]) relationship1 = stix2.Relationship(threat_actor, 'uses', malware) relationship2 = stix2.Relationship(threat_actor, 'uses', attack_pattern) relationship3 = stix2.Relationship(threat_actor, 'attributed-to', identity)
resource_level="government" primary_motivation="ideology", secondary_motivations=["dominance"], sophistication="strategic" ) identity1 = stix2.Identity( id="identity--8c6af861-7b20-41ef-9b59-6344fd872a8f", created="2016-08-08T15:50:10.983Z", modified="2016-08-08T15:50:10.983Z", name="Franistan Intelligence", identity_class="organisation" ) ref_bpp = stix2.ExternalReference( source_name="website", url="http://www.bpp.bn" ) identity2 = stix2.Identity( id="identity--ddfe7140-2ba4-48e4-b19a-df069432103b", created="2016-08-08T15:50:10.983Z", modified="2016-08-08T15:50:10.983Z", name="Branistan Peoples Party", identity_class="organisation" external_references= [ref_bpp] ) ref_capec1 = stix2.ExternalReference( source_name="capec", url="https://capec.mitre.org/data/definitions/148.html", external_id="CAPEC-148"
def test_external_reference_source_required(): with pytest.raises(ValueError) as excinfo: ref = stix2.ExternalReference() assert str( excinfo.value ) == "Missing required field(s) for ExternalReference: (source_name)."
def fetch_and_send(self): timestamp = int(time.time()) now = datetime.utcfromtimestamp(timestamp) friendly_name = "Cyber Threat Coalition run @ " + now.strftime( "%Y-%m-%d %H:%M:%S" ) work_id = self.helper.api.work.initiate_work( self.helper.connect_id, friendly_name ) bundle_objects = list() # create an identity for the coalition team organization = stix2.Identity( id=OpenCTIStix2Utils.generate_random_stix_id("identity"), name="Cyber Threat Coalition Team", identity_class="organization", description="Team of Experts collecting and sharing pandemic related " "cyber threat intelligence during the COVID-19 crisis time", ) # add organization in bundle bundle_objects.append(organization) report_object_refs = list() for collection in ["domain", "ip", "url", "hash"]: # fetch backlist url = self.cyber_threat_coalition_base_url + "/" + str(collection) + ".txt" response = requests.get(url=url) if response.status_code != 200: raise Exception( "Unable to fetch {0} blacklist, server returned status: {1}", collection, response.status_code, ) pattern_type = "stix" labels = ["COVID-19", "malicious-activity"] # parse content for data in response.iter_lines(decode_unicode=True): observable_type = None observable_resolver = None if data and not data.startswith("#"): if collection == "domain": observable_resolver = "Domain-Name" observable_type = "Domain-Name" elif collection == "ip": observable_resolver = "IPv4-Addr" observable_type = "IPv4-Addr" elif collection == "url": observable_resolver = "Url" observable_type = "Url" data = urllib.parse.quote(data, "/:") elif collection == "hash": observable_resolver = self.get_hash_type() observable_type = "File" indicator = None if observable_resolver is None or observable_type is None: return if self.cyber_threat_coalition_create_indicators: indicator = stix2.Indicator( id=OpenCTIStix2Utils.generate_random_stix_id("indicator"), name=data, pattern_type=pattern_type, pattern=self._INDICATOR_PATTERN[observable_resolver].format( data ), labels=labels, created_by_ref=organization, object_marking_refs=[stix2.TLP_WHITE], custom_properties={ "x_opencti_main_observable_type": observable_type, }, ) bundle_objects.append(indicator) report_object_refs.append(indicator["id"]) if self.cyber_threat_coalition_create_observables: observable = SimpleObservable( id=OpenCTIStix2Utils.generate_random_stix_id( "x-opencti-simple-observable" ), key=observable_type + "." + ".".join(self._OBSERVABLE_PATH[observable_resolver]), value=data, labels=labels, created_by_ref=organization, object_marking_refs=[stix2.TLP_WHITE], ) bundle_objects.append(observable) report_object_refs.append(observable["id"]) if indicator is not None: relationship = stix2.Relationship( id=OpenCTIStix2Utils.generate_random_stix_id( "relationship" ), relationship_type="based-on", created_by_ref=organization, source_ref=indicator.id, target_ref=observable.id, ) bundle_objects.append(relationship) report_object_refs.append(relationship["id"]) # create a global threat report report_uuid = "report--552b3ae6-8522-409d-8b72-a739bc1926aa" report_external_reference = stix2.ExternalReference( source_name="Cyber Threat Coalition", url="https://www.cyberthreatcoalition.org", external_id="COVID19-CTC", ) if report_object_refs: stix_report = stix2.Report( id=report_uuid, name="COVID-19 Cyber Threat Coalition (CTC) BlackList", type="report", description="This report represents the whole COVID-19 CTC blacklist.", published=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), created_by_ref=organization, object_marking_refs=[stix2.TLP_WHITE], labels=labels, external_references=[report_external_reference], object_refs=report_object_refs, ) # add report in bundle bundle_objects.append(stix_report) # create stix bundle bundle = stix2.Bundle(objects=bundle_objects) # send data self.helper.send_stix2_bundle( bundle=bundle.serialize(), update=self.update_existing_data, work_id=work_id ) return work_id
def process_reports(self, reports): if reports is None: printer.error("No results") return for report in reports: name = report["name"] id = report["id"] stix2_objects = [] stix2_object_refs = [] # FFS AV, consistency! if 'tlp' in report: tlp_id = REF_TLPS[report['tlp'].upper()] elif 'TLP' in report: tlp_id = REF_TLPS[report['TLP'].upper()] else: tlp_id = REF_TLPS['WHITE'] sectors = report['industries'] if sectors: unmatched_sectors = [] added_sector = False for sector in [html.unescape(x.upper()) for x in sectors]: sector_name = None sector_id = None if sector in SECTOR_MAPPINGS: # sector_ids.append(self.octi_sectors[SECTOR_MAPPINGS[sector]]) sector_name = SECTOR_MAPPINGS[sector] sector_id = self.octi_sectors[SECTOR_MAPPINGS[sector]] else: printer.debug(f"Looking for sector {sector}") match = difflib.get_close_matches( sector, self.octi_sectors.keys(), 1) if not len(match): printer.error( f"Unable to determine a matching sector for {sector}" ) unmatched_sectors.append(sector) continue # sector_ids.append(self.octi_sectors[match[0]]) sector_name = match[0] sector_id = self.octi_sectors[match[0]] if sector_name is not None: s = stix2.Identity(id=sector_id, name=sector_name, identity_class='class', custom_properties={ 'x_opencti_identity_type': 'sector' }) printer.debug(f"Adding sector {sector_name}") stix2_objects.append(s) stix2_object_refs.append(s) added_sector = True if not added_sector: printer.warn("Adding 'UNKNOWN' placeholder sector") s = stix2.Identity(id=self.octi_sectors["UNKNOWN"], name="Unknown", identity_class='class', custom_properties={ 'x_opencti_identity_type': 'sector' }) stix2_objects.append(s) stix2_object_refs.append(s) description = report['description'] if len(unmatched_sectors): description = description + "\n\n###\nUnable to find a match for the following sectors, " \ "please review manually:\n - " + '\n - '.join(unmatched_sectors) printer.info(f"Generating STIX2 for {name} ({id})") author = stix2.Identity(name=report['author_name'], identity_class='organization') stix2_objects.append(author) adversary = None if report['adversary']: printer.debug("Adding adversary {}".format( report['adversary'])) adversary = stix2.IntrusionSet(name=report['adversary']) stix2_object_refs.append(adversary) stix2_objects.append(adversary) if report['targeted_countries']: for country in report['targeted_countries']: printer.debug(f"Adding country {country}") c = stix2.Identity(name=country, identity_class='organization', custom_properties={ 'x_opencti_identity_type': 'country' }) stix2_objects.append(c) stix2_object_refs.append(c) external_refs = [] for eref in report['references']: external_refs.append( stix2.ExternalReference(source_name=tldextract.extract( eref).registered_domain, url=eref)) report = stix2.Report(name=name, description=description, created_by_ref=author, labels=['threat-report'], published=report['created'], created=report['created'], modified=report['modified'], object_refs=stix2_object_refs, object_marking_refs=[tlp_id], external_references=external_refs) stix2_objects.append(report) bundle = stix2.Bundle(stix2_objects) if not self.dryrun: self.opencti_connector_helper.send_stix2_bundle( str(bundle), self.connector_config['entities']) printer.info("Sending to OpenCTI") printer.debug(str(bundle)) else: printer.debug(f"No sectors, disregarding '{name}'")