def produce(self, tc_data: Union[list, dict]) -> Iterable[Visitor]: """Yield visitors that will apply relationship objects to a Stream of STIX data. Args: tc_data: the ThreatConnect data to produce Relationships based on. Yields: Visitors that will create STIX Relationship objects. """ if not isinstance(tc_data, list): tc_data = [tc_data] for data in tc_data: associations = data.pop('associations', []) for association in associations: source_xid = Batch.generate_xid( [data.get('summary'), data.get('ownerName')]) target_xid = Batch.generate_xid( [association.get('summary'), association.get('ownerName')]) yield stix2.Relationship( source_ref=f'indicator--{source_xid}', target_ref=f'indicator--{target_xid}', relationship_type='uses', )
def _file_consume_handler(indicators: Iterable[Indicator], batch_xid_array): """Produce ThreatConnect file mappings from a list of STIX 2.1 indicators Args: stix_data: STIX Indicator objects to parse. Returns: A array of indicator mappings. """ file_indicators = list( filter(lambda i: 'file:hashes' in i.path, indicators)) sha256_indicators = list( filter(lambda i: 'SHA-156' in i.path.upper(), file_indicators)) sha2_indicators = list( filter(lambda i: 'SHA-1' in i.path.upper(), file_indicators)) md5_indicators = list( filter(lambda i: 'MD5' in i.path.upper(), file_indicators)) if len(file_indicators) <= 0: return [] batch_xid_array.append('File') mappings = [] if (len(file_indicators) <= 3 and len(sha256_indicators) <= 1 and len(sha2_indicators) <= 1 and len(md5_indicators) <= 1): value = ' : '.join([v.value for v in file_indicators]) mappings.append({ 'type': 'File', 'summary': value, 'confidence': '@.confidence', 'xid': Batch.generate_xid(batch_xid_array + [value]), }) else: for i in file_indicators: mappings.append({ 'type': 'File', 'summary': i.value, 'confidence': '@.confidence', 'xid': Batch.generate_xid(batch_xid_array + [i.value]), }) return mappings
def _default_consume_handler(indicators: Iterable[Indicator], batch_xid_array): """Produce ThreatConnect URL/EmailAddress/Host/ASN mappings from STIX 2.1 indicators Args: stix_data: STIX Indicator objects to parse. Returns: A array of indicator mappings. """ type_map = { 'url:value': 'URL', 'email-addr:value': 'EmailAddress', 'domain-name:value': 'Host', 'autonomous-system:name': 'ASN', 'email-message:subject': 'EmailSubject' } mappings = [] for i in filter(lambda i: i.path in type_map.keys(), indicators): indicator_type = type_map.get(i.path) mappings.append({ 'type': indicator_type, 'summary': i.value, 'confidence': '@.confidence', 'xid': Batch.generate_xid(batch_xid_array + [indicator_type, i.value]), }) return mappings
def _file_consume_handler(indicators: Iterable[Indicator], batch_xid_array): """Produce ThreatConnect file mappings from a list of STIX 2.1 indicators Args: stix_data: STIX Indicator objects to parse. Returns: A array of indicator mappings. """ file_indicators = list( filter(lambda i: 'file:hashes' in i.path, indicators)) if len(file_indicators) <= 0: return [] batch_xid_array.append('File') mappings = [] # Changed logic to handle: APP-2560 for i in file_indicators: mappings.append({ 'type': 'File', 'summary': i.value, 'confidence': '@.confidence', 'xid': Batch.generate_xid(batch_xid_array + [i.value]), }) return mappings
def _ip_consume_handler(indicators: Iterable[Indicator], batch_xid_array): """Produce ThreatConnect Address/CIDR mappings from a list of STIX 2.1 indicators Args: stix_data: STIX Indicator objects to parse. Returns: A array of indicator mappings. """ mappings = [] for i in filter( lambda i: i.path in ['ipv4-addr:value', 'ipv6-addr:value'], indicators): parse_map = None if i.path == 'ipv4-addr:value': if '/' in i.value and i.value.split( '/')[1] != '32': # this is a CIDR parse_map = { 'type': 'CIDR', 'summary': i.value, } else: # this is an address parse_map = { 'type': 'Address', 'summary': i.value.split('/')[0], } elif i.path == 'ipv6-addr:value': if '/' in i.value and i.value.split( '/')[1] != '128': # this is a CIDR parse_map = { 'type': 'CIDR', 'summary': i.value, } else: # this is an address parse_map = { 'type': 'Address', 'summary': i.value.split('/')[0], } parse_map['confidence'] = '@.confidence' parse_map['xid'] = Batch.generate_xid( batch_xid_array + [parse_map.get('type'), parse_map.get('summary')]) mappings.append(parse_map) return mappings
def consume( self, stix_data: dict, collection_id, collection_name, collection_path, custom_type_mapping: dict = None, ): """Convert stix_data (in parsed JSON format) into ThreatConnect objects. Args: stix_data: one or more stix_data dictionaries collection_path: the url path to the collection collection_name: the collection name collection_id: the collection id custom_type_mapping: stix type to a mapping function which takes stix_data as a param. Yields: ThreatConnect objects """ type_mapping = { 'email-addr': self.email_address_mapping, 'autonomous-system': self.as_object_mapping, 'domain-name': self.domain_name_mapping, 'ipv4-addr': self.ipv4_mapping, 'ipv6-addr': self.ipv6_mapping, 'windows-registry-key': self.registry_key_mapping, 'url': self.url_mapping, } type_mapping.update(custom_type_mapping or {}) visitor_mapping = {'relationship': self.relationship} tc_data = [] for data in stix_data.get('objects', []): object_id = data.get('id') xid = Batch.generate_xid(object_id) safe_default_map = copy.deepcopy(self.default_map) source_value = [f'Object ID: {object_id}'] if collection_id: source_value.append(f'Collection ID: {collection_id}') xid = Batch.generate_xid([object_id, collection_id]) if collection_name: source_value.append(f'Collection Name: {collection_name}') if collection_path: source_value.append(f'Collection Path: {collection_path}') if not collection_path.endswith('/'): collection_path += '/' source_value.append( f'Object Path: {collection_path}objects/{object_id}/') for attribute in safe_default_map.get('attribute', []): if attribute.get('type') == 'Source': attribute['value'] = '\n'.join(source_value) attribute['displayed'] = True break _type = data.get('type').lower() mapping_method = visitor_mapping.get(_type) if mapping_method: # for visitor in mapping_method.consume(data): # self.register_visitor(visitor) continue mapping_method = type_mapping.get(_type) if _type == 'indicator': if stix_data.get('pattern_type', 'stix') != 'stix': continue for map_ in self.indicator.consume_mappings( data, collection_id): if not map_: continue map_.update(safe_default_map) tc_data = itertools.chain(tc_data, self._map(data, map_)) elif mapping_method: safe_default_map['xid'] = xid map_ = self.smart_update(safe_default_map, mapping_method(data)) tc_data = itertools.chain(tc_data, self._map(data, map_)) # else: # self.default_map['xid'] = xid # tc_data = itertools.chain(tc_data, self._map(data, self.default_map)) # continue for visitor in self._visitors: tc_data = visitor.visit(tc_data) yield from tc_data
def consume( self, stix_data: dict, collection_id, collection_name, collection_path, custom_type_mapping: dict = None, additional_known_mapping=None, ): """Convert stix_data (in parsed JSON format) into ThreatConnect objects. Args: stix_data: one or more stix_data dictionaries collection_path: the url path to the collection collection_name: the collection name collection_id: the collection id custom_type_mapping: stix type to a mapping function which takes stix_data as a param. additional_known_mapping (list[str]): provide a str of the known supported mapping to use. Supported known mappings are [ 'email-addr', 'autonomous-system', 'domain-name', 'ipv4-addr', 'ipv6-addr', 'window-registry-key', 'url' ] Yields: ThreatConnect objects """ if additional_known_mapping is None: additional_known_mapping = [] if additional_known_mapping is None: additional_known_mapping = [] additional_known_mappings = { # Per a conversation with Richard Cody only indicators from # stix type: indicator should be mapped to. 'email-addr': self.email_address_mapping, 'autonomous-system': self.as_object_mapping, 'domain-name': self.domain_name_mapping, 'ipv4-addr': self.ipv4_mapping, 'ipv6-addr': self.ipv6_mapping, 'windows-registry-key': self.registry_key_mapping, 'url': self.url_mapping, } type_mapping = { 'campaign': self.campaign_mapping, 'intrusion-set': self.intrusion_set_mapping, 'report': self.report_mapping, 'threat-actor': self.threat_actor_mapping, 'tool': self.tool_mapping, } type_mapping.update(custom_type_mapping or {}) for mapping in additional_known_mapping: additional_mapping = additional_known_mappings.get(mapping) if not additional_mapping: self.logger.log.warning(f'provided additional mapping: {mapping} is not supported') type_mapping[mapping] = additional_mapping visitor_mapping = {'relationship': self.relationship} tc_data = [] for data in stix_data.get('objects', []): object_id = data.get('id') xid = Batch.generate_xid(object_id) safe_default_map = copy.deepcopy(self.default_map) source_value = [f'Object ID: {object_id}'] if collection_id: source_value.append(f'Collection ID: {collection_id}') xid = Batch.generate_xid([object_id, collection_id]) if collection_name: source_value.append(f'Collection Name: {collection_name}') if collection_path: source_value.append(f'Collection Path: {collection_path}') if not collection_path.endswith('/'): collection_path += '/' source_value.append(f'Object Path: {collection_path}objects/{object_id}/') for attribute in safe_default_map.get('attribute', []): if attribute.get('type') == 'Source': attribute['value'] = '\n'.join(source_value) attribute['displayed'] = True break _type = data.get('type').lower() mapping_method = visitor_mapping.get(_type) if mapping_method: # for visitor in mapping_method.consume(data): # self.register_visitor(visitor) continue mapping_method = type_mapping.get(_type) if _type == 'indicator': if stix_data.get('pattern_type', 'stix') != 'stix': continue for map_ in self.indicator.consume_mappings(data, collection_id): if not map_: continue map_.update(safe_default_map) tc_data = itertools.chain(tc_data, self._map(data, map_)) elif mapping_method: safe_default_map['xid'] = xid map_ = self.smart_update(safe_default_map, mapping_method(data)) tc_data = itertools.chain(tc_data, self._map(data, map_)) # else: # self.default_map['xid'] = xid # tc_data = itertools.chain(tc_data, self._map(data, self.default_map)) # continue for visitor in self._visitors: tc_data = visitor.visit(tc_data) yield from tc_data