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',
                )
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
    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
Beispiel #6
0
    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
Beispiel #7
0
    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