Пример #1
0
def find_hashes(htype):
    r = mymisp.search(controller='attributes', type_attribute=htype)
    echeck(r)
    if not r.get('response'):
        return
    for a in r['response']['Attribute']:
        attribute = MISPAttribute(mymisp.describe_types)
        attribute.from_dict(**a)
        if '|' in attribute.type and '|' in attribute.value:
            c, value = attribute.value.split('|')
            comment = '{} - {}'.format(attribute.comment, c)
        else:
            comment = attribute.comment
            value = attribute.value
        mhash = value.replace(':', ';')
        mfile = 'MISP event {} {}'.format(a['event_id'], comment.replace(':', ';').replace('\r', '').replace('\n', ''))
        print('{}:*:{}:73'.format(mhash, mfile))
Пример #2
0
def handler(q=False):
    if q is False:
        return False
    request = json.loads(q)

    if request.get('config'):
        if (request['config'].get('api_id') is
                None) or (request['config'].get('api_secret') is None):
            misperrors['error'] = "Censys API credentials are missing"
            return misperrors
    else:
        misperrors['error'] = "Please provide config options"
        return misperrors

    api_id = request['config']['api_id']
    api_secret = request['config']['api_secret']

    if not request.get('attribute') or not check_input_attribute(
            request['attribute']):
        return {
            'error':
            f'{standard_error_message}, which should contain at least a type, a value and an uuid.'
        }
    attribute = request['attribute']
    if not any(input_type == attribute['type']
               for input_type in mispattributes['input']):
        return {'error': 'Unsupported attribute type.'}

    attribute = MISPAttribute()
    attribute.from_dict(**request['attribute'])
    # Lists to accomodate multi-types attribute
    conn = list()
    types = list()
    values = list()
    results = list()

    if "|" in attribute.type:
        t_1, t_2 = attribute.type.split('|')
        v_1, v_2 = attribute.value.split('|')
        # We cannot use the port information
        if t_2 == "port":
            types.append(t_1)
            values.append(v_1)
        else:
            types = [t_1, t_2]
            values = [v_1, v_2]
    else:
        types.append(attribute.type)
        values.append(attribute.value)

    for t in types:
        # ip, ip-src or ip-dst
        if t[:2] == "ip":
            conn.append(
                censys.ipv4.CensysIPv4(api_id=api_id, api_secret=api_secret))
        elif t == 'domain' or t == "hostname":
            conn.append(
                censys.websites.CensysWebsites(api_id=api_id,
                                               api_secret=api_secret))
        elif 'x509-fingerprint' in t:
            conn.append(
                censys.certificates.CensysCertificates(api_id=api_id,
                                                       api_secret=api_secret))

    found = True
    for c in conn:
        val = values.pop(0)
        try:
            r = c.view(val)
            results.append(parse_response(r, attribute))
            found = True
        except censys.base.CensysNotFoundException:
            found = False
        except Exception:
            misperrors['error'] = "Connection issue"
            return misperrors

    if not found:
        misperrors['error'] = "Nothing could be found on Censys"
        return misperrors

    return {'results': remove_duplicates(results)}
Пример #3
0
class PassiveSSLParser():
    def __init__(self, attribute, authentication):
        self.misp_event = MISPEvent()
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self.misp_event.add_attribute(**self.attribute)
        self.pssl = pypssl.PyPSSL(basic_auth=authentication)
        self.cert_hash = 'x509-fingerprint-sha1'
        self.cert_type = 'pem'
        self.mapping = {
            'issuer': ('text', 'issuer'),
            'keylength': ('text', 'pubkey-info-size'),
            'not_after': ('datetime', 'validity-not-after'),
            'not_before': ('datetime', 'validity-not-before'),
            'subject': ('text', 'subject')
        }

    def get_results(self):
        if hasattr(self, 'result'):
            return self.result
        event = json.loads(self.misp_event.to_json())
        results = {key: event[key] for key in ('Attribute', 'Object')}
        return {'results': results}

    def parse(self):
        value = self.attribute.value.split(
            '|')[0] if '|' in self.attribute.type else self.attribute.value

        try:
            results = self.pssl.query(value)
        except Exception:
            self.result = {
                'error':
                'There is an authentication error, please make sure you supply correct credentials.'
            }
            return

        if not results:
            self.result = {'error': 'Not found'}
            return

        if 'error' in results:
            self.result = {'error': results['error']}
            return

        for ip_address, certificates in results.items():
            ip_uuid = self._handle_ip_attribute(ip_address)
            for certificate in certificates['certificates']:
                self._handle_certificate(certificate, ip_uuid)

    def _handle_certificate(self, certificate, ip_uuid):
        x509 = MISPObject('x509')
        x509.add_attribute(self.cert_hash,
                           type=self.cert_hash,
                           value=certificate)
        cert_details = self.pssl.fetch_cert(certificate)
        info = cert_details['info']
        for feature, mapping in self.mapping.items():
            attribute_type, object_relation = mapping
            x509.add_attribute(object_relation,
                               type=attribute_type,
                               value=info[feature])
        x509.add_attribute(self.cert_type, type='text', value=self.cert_type)
        x509.add_reference(ip_uuid, 'seen-by')
        self.misp_event.add_object(**x509)

    def _handle_ip_attribute(self, ip_address):
        if ip_address == self.attribute.value:
            return self.attribute.uuid
        ip_attribute = MISPAttribute()
        ip_attribute.from_dict(**{
            'type': self.attribute.type,
            'value': ip_address
        })
        self.misp_event.add_attribute(**ip_attribute)
        return ip_attribute.uuid
Пример #4
0
 def parse_network_interactions(self):
     domaininfo = self.data['domaininfo']
     if domaininfo:
         for domain in domaininfo['domain']:
             if domain['@ip'] != 'unknown':
                 domain_object = MISPObject('domain-ip')
                 for key, mapping in domain_object_mapping.items():
                     attribute_type, object_relation = mapping
                     domain_object.add_attribute(
                         object_relation, **{
                             'type': attribute_type,
                             'value': domain[key]
                         })
                 self.misp_event.add_object(**domain_object)
                 reference = dict(referenced_uuid=domain_object.uuid,
                                  relationship_type='contacts')
                 self.add_process_reference(domain['@targetid'],
                                            domain['@currentpath'],
                                            reference)
             else:
                 attribute = MISPAttribute()
                 attribute.from_dict(**{
                     'type': 'domain',
                     'value': domain['@name']
                 })
                 self.misp_event.add_attribute(**attribute)
                 reference = dict(referenced_uuid=attribute.uuid,
                                  relationship_type='contacts')
                 self.add_process_reference(domain['@targetid'],
                                            domain['@currentpath'],
                                            reference)
     ipinfo = self.data['ipinfo']
     if ipinfo:
         for ip in ipinfo['ip']:
             attribute = MISPAttribute()
             attribute.from_dict(**{'type': 'ip-dst', 'value': ip['@ip']})
             self.misp_event.add_attribute(**attribute)
             reference = dict(referenced_uuid=attribute.uuid,
                              relationship_type='contacts')
             self.add_process_reference(ip['@targetid'], ip['@currentpath'],
                                        reference)
     urlinfo = self.data['urlinfo']
     if urlinfo:
         for url in urlinfo['url']:
             target_id = int(url['@targetid'])
             current_path = url['@currentpath']
             attribute = MISPAttribute()
             attribute_dict = {'type': 'url', 'value': url['@name']}
             if target_id != -1 and current_path != 'unknown':
                 self.references[self.process_references[(
                     target_id, current_path)]].append({
                         'referenced_uuid':
                         attribute.uuid,
                         'relationship_type':
                         'contacts'
                     })
             else:
                 attribute_dict[
                     'comment'] = 'From Memory - Enriched via the joe_import module'
             attribute.from_dict(**attribute_dict)
             self.misp_event.add_attribute(**attribute)
Пример #5
0
class VirusTotalParser():
    def __init__(self):
        super(VirusTotalParser, self).__init__()
        self.misp_event = MISPEvent()

    def declare_variables(self, apikey, attribute):
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self.apikey = apikey

    def get_result(self):
        event = json.loads(self.misp_event.to_json())
        results = {
            key: event[key]
            for key in ('Attribute', 'Object') if (key in event and event[key])
        }
        return {'results': results}

    def parse_urls(self, query_result):
        for feature in ('detected_urls', 'undetected_urls'):
            if feature in query_result:
                for url in query_result[feature]:
                    value = url['url'] if isinstance(url, dict) else url[0]
                    self.misp_event.add_attribute('url', value)

    def parse_resolutions(self, resolutions, subdomains=None, uuids=None):
        domain_ip_object = MISPObject('domain-ip')
        if self.attribute.type == 'domain':
            domain_ip_object.add_attribute('domain',
                                           type='domain',
                                           value=self.attribute.value)
            attribute_type, relation, key = ('ip-dst', 'ip', 'ip_address')
        else:
            domain_ip_object.add_attribute('ip',
                                           type='ip-dst',
                                           value=self.attribute.value)
            attribute_type, relation, key = ('domain', 'domain', 'hostname')
        for resolution in resolutions:
            domain_ip_object.add_attribute(relation,
                                           type=attribute_type,
                                           value=resolution[key])
        if subdomains:
            for subdomain in subdomains:
                attribute = MISPAttribute()
                attribute.from_dict(**dict(type='domain', value=subdomain))
                self.misp_event.add_attribute(**attribute)
                domain_ip_object.add_reference(attribute.uuid, 'subdomain')
        if uuids:
            for uuid in uuids:
                domain_ip_object.add_reference(uuid, 'sibling-of')
        self.misp_event.add_object(**domain_ip_object)

    def parse_vt_object(self, query_result):
        if query_result['response_code'] == 1:
            vt_object = MISPObject('virustotal-report')
            vt_object.add_attribute('permalink',
                                    type='link',
                                    value=query_result['permalink'])
            detection_ratio = '{}/{}'.format(query_result['positives'],
                                             query_result['total'])
            vt_object.add_attribute('detection-ratio',
                                    type='text',
                                    value=detection_ratio)
            self.misp_event.add_object(**vt_object)

    def get_query_result(self, query_type):
        params = {query_type: self.attribute.value, 'apikey': self.apikey}
        return requests.get(self.base_url, params=params)
class RFEnricher:
    """Class for enriching an attribute with data from Recorded Future.
       The enrichment data is returned as a custom MISP object.
    """
    def __init__(self, api_token: str, attribute_props: dict):
        self.api_token = api_token
        self.event = MISPEvent()
        self.enrichment_object = MISPObject('Recorded Future Enrichment')
        self.enrichment_object.from_dict(
            **{
                'meta-category':
                'misc',
                'description':
                'An object containing the enriched attribute and related '
                'entities from Recorded Future.',
                'distribution':
                0
            })

        # Create a copy of enriched attribute to add tags to
        temp_attr = MISPAttribute()
        temp_attr.from_dict(**attribute_props)
        self.enriched_attribute = MISPAttribute()
        self.enriched_attribute.from_dict(**{
            'value': temp_attr.value,
            'type': temp_attr.type,
            'distribution': 0
        })

        self.related_attributes = []
        self.color_picker = RFColors()
        self.galaxy_finder = GalaxyFinder()

        # Mapping from MISP-type to RF-type
        self.type_to_rf_category = {
            'ip': 'ip',
            'ip-src': 'ip',
            'ip-dst': 'ip',
            'domain': 'domain',
            'hostname': 'domain',
            'md5': 'hash',
            'sha1': 'hash',
            'sha256': 'hash',
            'uri': 'url',
            'url': 'url',
            'vulnerability': 'vulnerability',
            'weakness': 'vulnerability'
        }

        # Related entities from RF portrayed as related attributes in MISP
        self.related_attribute_types = [
            'RelatedIpAddress', 'RelatedInternetDomainName', 'RelatedHash',
            'RelatedEmailAddress', 'RelatedCyberVulnerability'
        ]
        # Related entities from RF portrayed as tags in MISP
        self.galaxy_tag_types = ['RelatedMalware', 'RelatedThreatActor']

    def enrich(self):
        """Run the enrichment."""
        category = self.type_to_rf_category.get(self.enriched_attribute.type)

        try:
            response = rf_lookup(self.api_token, category,
                                 self.enriched_attribute.value)
            json_response = json.loads(response.content)
        except requests.HTTPError as error:
            misperrors['error'] = f'Error when requesting data from Recorded Future. ' \
                                  f'{error.response} : {error.response.reason}'
            raise error

        try:
            # Add risk score and risk rules as tags to the enriched attribute
            risk_score = json_response['data']['risk']['score']
            hex_color = self.color_picker.riskscore_color(risk_score)
            tag_name = f'recorded-future:risk-score="{risk_score}"'
            self.add_tag(tag_name, hex_color)
            for evidence in json_response['data']['risk']['evidenceDetails']:
                risk_rule = evidence['rule']
                criticality = evidence['criticality']
                hex_color = self.color_picker.riskrule_color(criticality)
                tag_name = f'recorded-future:risk-rule="{risk_rule}"'
                self.add_tag(tag_name, hex_color)

            # Retrieve related entities
            for related_entity in json_response['data']['relatedEntities']:
                related_type = related_entity['type']
                if related_type in self.related_attribute_types:
                    # Related entities returned as additional attributes
                    for related in related_entity['entities']:
                        if int(related["count"]) > 4:
                            indicator = related['entity']['name']
                            self.add_related_attribute(indicator, related_type)
                elif related_type in self.galaxy_tag_types:
                    # Related entities added as galaxy-tags to the enriched attribute
                    galaxy_tags = []
                    for related in related_entity['entities']:
                        if int(related["count"]) > 4:
                            indicator = related['entity']['name']
                            galaxy = self.galaxy_finder.find_galaxy_match(
                                indicator, related_type)
                            # Handle deduplication of galaxy tags
                            if galaxy and galaxy not in galaxy_tags:
                                galaxy_tags.append(galaxy)
                    for galaxy in galaxy_tags:
                        self.add_tag(galaxy)
        except KeyError as error:
            misperrors[
                'error'] = 'Unexpected format in Recorded Future api response.'
            raise error

    def add_related_attribute(self, indicator: str, related_type: str) -> None:
        """Helper method for adding an indicator to the related attribute list."""
        out_type = self.get_output_type(related_type, indicator)
        attribute = MISPAttribute()
        attribute.from_dict(**{
            'value': indicator,
            'type': out_type,
            'distribution': 0
        })
        self.related_attributes.append((related_type, attribute))

    def add_tag(self, tag_name: str, hex_color: str = None) -> None:
        """Helper method for adding a tag to the enriched attribute."""
        tag = MISPTag()
        tag_properties = {'name': tag_name}
        if hex_color:
            tag_properties['colour'] = hex_color
        tag.from_dict(**tag_properties)
        self.enriched_attribute.add_tag(tag)

    def get_output_type(self, related_type: str, indicator: str) -> str:
        """Helper method for translating a Recorded Future related type to a MISP output type."""
        output_type = 'text'
        if related_type == 'RelatedIpAddress':
            output_type = 'ip-dst'
        elif related_type == 'RelatedInternetDomainName':
            output_type = 'domain'
        elif related_type == 'RelatedHash':
            hash_len = len(indicator)
            if hash_len == 64:
                output_type = 'sha256'
            elif hash_len == 40:
                output_type = 'sha1'
            elif hash_len == 32:
                output_type = 'md5'
        elif related_type == 'RelatedEmailAddress':
            output_type = 'email-src'
        elif related_type == 'RelatedCyberVulnerability':
            signature = indicator.split('-')[0]
            if signature == 'CVE':
                output_type = 'vulnerability'
            elif signature == 'CWE':
                output_type = 'weakness'
        return output_type

    def get_results(self) -> dict:
        """Build and return the enrichment results."""
        self.enrichment_object.add_attribute('Enriched attribute',
                                             **self.enriched_attribute)
        for related_type, attribute in self.related_attributes:
            self.enrichment_object.add_attribute(related_type, **attribute)
        self.event.add_object(**self.enrichment_object)
        event = json.loads(self.event.to_json())
        result = {key: event[key] for key in ['Object'] if key in event}
        return {'results': result}
class MVAPI():
    def __init__(self, attribute, api_key, client_id, client_secret):
        self.misp_event = MISPEvent()
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self.misp_event.add_attribute(**self.attribute)

        self.base_url = 'https://api.mvision.mcafee.com'
        self.session = requests.Session()

        self.api_key = api_key
        auth = (client_id, client_secret)

        self.logging()
        self.auth(auth)

    def logging(self):
        self.logger = logging.getLogger('logs')
        self.logger.setLevel('INFO')
        handler = logging.StreamHandler()
        formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def auth(self, auth):
        iam_url = "https://iam.mcafee-cloud.com/iam/v1.1/token"

        headers = {
            'x-api-key': self.api_key,
            'Content-Type': 'application/vnd.api+json'
        }

        payload = {
            "grant_type": "client_credentials",
            "scope": "ins.user ins.suser ins.ms.r"
        }

        res = self.session.post(iam_url,
                                headers=headers,
                                auth=auth,
                                data=payload)

        if res.status_code != 200:
            self.logger.error(
                'Could not authenticate to get the IAM token: {0} - {1}'.
                format(res.status_code, res.text))
            sys.exit()
        else:
            self.logger.info('Successful authenticated.')
            access_token = res.json()['access_token']
            headers['Authorization'] = 'Bearer ' + access_token
            self.session.headers = headers

    def search_ioc(self):
        filters = {
            'filter[type][eq]':
            self.attribute.type,
            'filter[value]':
            self.attribute.value,
            'fields':
            'id, type, value, coverage, uid, is_coat, is_sdb_dirty, category, comment, campaigns, threat, prevalence'
        }
        res = self.session.get(self.base_url + '/insights/v2/iocs',
                               params=filters)

        if res.ok:
            if len(res.json()['data']) == 0:
                self.logger.info('No Hash details in MVISION Insights found.')
            else:
                self.logger.info(
                    'Successfully retrieved MVISION Insights details.')
                self.logger.debug(res.text)
                return res.json()
        else:
            self.logger.error('Error in search_ioc. HTTP {0} - {1}'.format(
                str(res.status_code), res.text))
            sys.exit()

    def prep_result(self, ioc):
        res = ioc['data'][0]
        results = []

        # Parse out Attribute Category
        category_attr = {
            'type':
            'text',
            'object_relation':
            'text',
            'value':
            'Attribute Category: {0}'.format(res['attributes']['category'])
        }
        results.append(category_attr)

        # Parse out Attribute Comment
        comment_attr = {
            'type': 'text',
            'object_relation': 'text',
            'value':
            'Attribute Comment: {0}'.format(res['attributes']['comment'])
        }
        results.append(comment_attr)

        # Parse out Attribute Dat Coverage
        cover_attr = {
            'type':
            'text',
            'object_relation':
            'text',
            'value':
            'Dat Version Coverage: {0}'.format(
                res['attributes']['coverage']['dat_version']['min'])
        }
        results.append(cover_attr)

        # Parse out if Dirty
        cover_attr = {
            'type': 'text',
            'object_relation': 'text',
            'value': 'Is Dirty: {0}'.format(res['attributes']['is-sdb-dirty'])
        }
        results.append(cover_attr)

        # Parse our targeted countries
        countries_dict = []
        countries = res['attributes']['prevalence']['countries']

        for country in countries:
            countries_dict.append(country['iso_code'])

        country_attr = {
            'type': 'text',
            'object_relation': 'text',
            'value': 'Targeted Countries: {0}'.format(countries_dict)
        }
        results.append(country_attr)

        # Parse out targeted sectors
        sectors_dict = []
        sectors = res['attributes']['prevalence']['sectors']

        for sector in sectors:
            sectors_dict.append(sector['sector'])

        sector_attr = {
            'type': 'text',
            'object_relation': 'text',
            'value': 'Targeted Sectors: {0}'.format(sectors_dict)
        }
        results.append(sector_attr)

        # Parse out Threat Classification
        threat_class_attr = {
            'type':
            'text',
            'object_relation':
            'text',
            'value':
            'Threat Classification: {0}'.format(
                res['attributes']['threat']['classification'])
        }
        results.append(threat_class_attr)

        # Parse out Threat Name
        threat_name_attr = {
            'type': 'text',
            'object_relation': 'text',
            'value':
            'Threat Name: {0}'.format(res['attributes']['threat']['name'])
        }
        results.append(threat_name_attr)

        # Parse out Threat Severity
        threat_sev_attr = {
            'type':
            'text',
            'object_relation':
            'text',
            'value':
            'Threat Severity: {0}'.format(
                res['attributes']['threat']['severity'])
        }
        results.append(threat_sev_attr)

        # Parse out Attribute ID
        attr_id = {
            'type': 'text',
            'object_relation': 'text',
            'value': 'Attribute ID: {0}'.format(res['id'])
        }
        results.append(attr_id)

        # Parse out Campaign Relationships
        campaigns = ioc['included']

        for campaign in campaigns:
            campaign_attr = {
                'type': 'campaign-name',
                'object_relation': 'campaign-name',
                'value': campaign['attributes']['name']
            }
            results.append(campaign_attr)

        mv_insights_obj = MISPObject(name='MVISION Insights Details')
        for mvi_res in results:
            mv_insights_obj.add_attribute(**mvi_res)
        mv_insights_obj.add_reference(self.attribute.uuid,
                                      'mvision-insights-details')

        self.misp_event.add_object(mv_insights_obj)

        event = json.loads(self.misp_event.to_json())
        results_mvi = {
            key: event[key]
            for key in ('Attribute', 'Object') if (key in event and event[key])
        }

        return {'results': results_mvi}
Пример #8
0
class ShodanParser():
    def __init__(self, attribute):
        self.misp_event = MISPEvent()
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self.misp_event.add_attribute(**self.attribute)
        self.ip_address_mapping = {
            'asn': {
                'type': 'AS',
                'object_relation': 'asn'
            },
            'city': {
                'type': 'text',
                'object_relation': 'city'
            },
            'country_code': {
                'type': 'text',
                'object_relation': 'country-code'
            },
            'country_name': {
                'type': 'text',
                'object_relation': 'country'
            },
            'isp': {
                'type': 'text',
                'object_relation': 'ISP'
            },
            'latitude': {
                'type': 'float',
                'object_relation': 'latitude'
            },
            'longitude': {
                'type': 'float',
                'object_relation': 'longitude'
            },
            'org': {
                'type': 'text',
                'object_relation': 'organization'
            },
            'postal_code': {
                'type': 'text',
                'object_relation': 'zipcode'
            },
            'region_code': {
                'type': 'text',
                'object_relation': 'region-code'
            }
        }
        self.ip_port_mapping = {
            'domains': {
                'type': 'domain',
                'object_relation': 'domain'
            },
            'hostnames': {
                'type': 'hostname',
                'object_relation': 'hostname'
            }
        }
        self.vulnerability_mapping = {
            'cvss': {
                'type': 'float',
                'object_relation': 'cvss-score'
            },
            'summary': {
                'type': 'text',
                'object_relation': 'summary'
            }
        }
        self.x509_mapping = {
            'bits': {
                'type': 'text',
                'object_relation': 'pubkey-info-size'
            },
            'expires': {
                'type': 'datetime',
                'object_relation': 'validity-not-after'
            },
            'issued': {
                'type': 'datetime',
                'object_relation': 'validity-not-before'
            },
            'issuer': {
                'type': 'text',
                'object_relation': 'issuer'
            },
            'serial': {
                'type': 'text',
                'object_relation': 'serial-number'
            },
            'sig_alg': {
                'type': 'text',
                'object_relation': 'signature_algorithm'
            },
            'subject': {
                'type': 'text',
                'object_relation': 'subject'
            },
            'type': {
                'type': 'text',
                'object_relation': 'pubkey-info-algorithm'
            },
            'version': {
                'type': 'text',
                'object_relation': 'version'
            }
        }

    def query_shodan(self, apikey):
        # Query Shodan and get the results in a json blob
        api = shodan.Shodan(apikey)
        query_results = api.host(self.attribute.value)

        # Parse the information about the IP address used as input
        ip_address_attributes = []
        for feature, mapping in self.ip_address_mapping.items():
            if query_results.get(feature):
                attribute = {'value': query_results[feature]}
                attribute.update(mapping)
                ip_address_attributes.append(attribute)
        if ip_address_attributes:
            ip_address_object = MISPObject('ip-api-address')
            for attribute in ip_address_attributes:
                ip_address_object.add_attribute(**attribute)
            ip_address_object.add_attribute(**self._get_source_attribute())
            ip_address_object.add_reference(self.attribute.uuid, 'describes')
            self.misp_event.add_object(ip_address_object)

        # Parse the hostnames / domains and ports associated with the IP address
        if query_results.get('ports'):
            ip_port_object = MISPObject('ip-port')
            ip_port_object.add_attribute(**self._get_source_attribute())
            feature = self.attribute.type.split('-')[1]
            for port in query_results['ports']:
                attribute = {
                    'type': 'port',
                    'object_relation': f'{feature}-port',
                    'value': port
                }
                ip_port_object.add_attribute(**attribute)
            for feature, mapping in self.ip_port_mapping.items():
                for value in query_results.get(feature, []):
                    attribute = {'value': value}
                    attribute.update(mapping)
                    ip_port_object.add_attribute(**attribute)
            ip_port_object.add_reference(self.attribute.uuid, 'extends')
            self.misp_event.add_object(ip_port_object)
        else:
            if any(
                    query_results.get(feature)
                    for feature in ('domains', 'hostnames')):
                domain_ip_object = MISPObject('domain-ip')
                domain_ip_object.add_attribute(**self._get_source_attribute())
                for feature in ('domains', 'hostnames'):
                    for value in query_results[feature]:
                        attribute = {
                            'type': 'domain',
                            'object_relation': 'domain',
                            'value': value
                        }
                        domain_ip_object.add_attribute(**attribute)
                domain_ip_object.add_reference(self.attribute.uuid, 'extends')
                self.misp_event.add_object(domain_ip_object)

        # Parse data within the "data" field
        if query_results.get('vulns'):
            vulnerabilities = {}
            for data in query_results['data']:
                # Parse vulnerabilities
                if data.get('vulns'):
                    for cve, vulnerability in data['vulns'].items():
                        if cve not in vulnerabilities:
                            vulnerabilities[cve] = vulnerability
                # Also parse the certificates
                if data.get('ssl'):
                    self._parse_cert(data['ssl'])
            for cve, vulnerability in vulnerabilities.items():
                vulnerability_object = MISPObject('vulnerability')
                vulnerability_object.add_attribute(**{
                    'type': 'vulnerability',
                    'object_relation': 'id',
                    'value': cve
                })
                for feature, mapping in self.vulnerability_mapping.items():
                    if vulnerability.get(feature):
                        attribute = {'value': vulnerability[feature]}
                        attribute.update(mapping)
                        vulnerability_object.add_attribute(**attribute)
                if vulnerability.get('references'):
                    for reference in vulnerability['references']:
                        vulnerability_object.add_attribute(
                            **{
                                'type': 'link',
                                'object_relation': 'references',
                                'value': reference
                            })
                vulnerability_object.add_reference(self.attribute.uuid,
                                                   'vulnerability-of')
                self.misp_event.add_object(vulnerability_object)
            for cve_id in query_results['vulns']:
                if cve_id not in vulnerabilities:
                    attribute = {'type': 'vulnerability', 'value': cve_id}
                    self.misp_event.add_attribute(**attribute)
        else:
            # We have no vulnerability data, we only check if we have
            # certificates within the "data" field
            for data in query_results['data']:
                if data.get('ssl'):
                    self._parse_cert(data['ssl']['cert'])

    def get_result(self):
        event = json.loads(self.misp_event.to_json())
        results = {
            key: event[key]
            for key in ('Attribute', 'Object') if (key in event and event[key])
        }
        return {'results': results}

    # When we want to add the IP address information in objects such as the
    # domain-ip or ip-port objects referencing the input IP address attribute
    def _get_source_attribute(self):
        return {
            'type': self.attribute.type,
            'object_relation': self.attribute.type,
            'value': self.attribute.value
        }

    def _parse_cert(self, certificate):
        x509_object = MISPObject('x509')
        for feature in ('serial', 'sig_alg', 'version'):
            if certificate.get(feature):
                attribute = {'value': certificate[feature]}
                attribute.update(self.x509_mapping[feature])
                x509_object.add_attribute(**attribute)
        # Parse issuer and subject value
        for feature in ('issuer', 'subject'):
            if certificate.get(feature):
                attribute_value = (
                    f'{identifier}={value}'
                    for identifier, value in certificate[feature].items())
                attribute = {'value': f'/{"/".join(attribute_value)}'}
                attribute.update(self.x509_mapping[feature])
                x509_object.add_attribute(**attribute)
        # Parse datetime attributes
        for feature in ('expires', 'issued'):
            if certificate.get(feature):
                attribute = {
                    'value':
                    datetime.strptime(certificate[feature], '%Y%m%d%H%M%SZ')
                }
                attribute.update(self.x509_mapping[feature])
                x509_object.add_attribute(**attribute)
        # Parse fingerprints
        if certificate.get('fingerprint'):
            for hash_type, hash_value in certificate['fingerprint'].items():
                x509_object.add_attribute(
                    **{
                        'type': f'x509-fingerprint-{hash_type}',
                        'object_relation': f'x509-fingerprint-{hash_type}',
                        'value': hash_value
                    })
        # Parse public key related info
        if certificate.get('pubkey'):
            for feature, value in certificate['pubkey'].items():
                attribute = {'value': value}
                attribute.update(self.x509_mapping[feature])
                x509_object.add_attribute(**attribute)
        x509_object.add_reference(self.attribute.uuid, 'identifies')
        self.misp_event.add_object(x509_object)
Пример #9
0
class VirusTotalParser(object):
    def __init__(self, apikey):
        self.apikey = apikey
        self.base_url = "https://www.virustotal.com/vtapi/v2/{}/report"
        self.misp_event = MISPEvent()
        self.parsed_objects = {}
        self.input_types_mapping = {'ip-src': self.parse_ip, 'ip-dst': self.parse_ip,
                                    'domain': self.parse_domain, 'hostname': self.parse_domain,
                                    'md5': self.parse_hash, 'sha1': self.parse_hash,
                                    'sha256': self.parse_hash, 'sha512': self.parse_hash,
                                    'url': self.parse_url}

    def query_api(self, attribute):
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        return self.input_types_mapping[self.attribute.type](self.attribute.value, recurse=True)

    def get_result(self):
        event = json.loads(self.misp_event.to_json())['Event']
        results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])}
        return {'results': results}

    ################################################################################
    ####                         Main parsing functions                         #### # noqa
    ################################################################################

    def parse_domain(self, domain, recurse=False):
        req = requests.get(self.base_url.format('domain'), params={'apikey': self.apikey, 'domain': domain})
        if req.status_code != 200:
            return req.status_code
        req = req.json()
        hash_type = 'sha256'
        whois = 'whois'
        feature_types = {'communicating': 'communicates-with',
                         'downloaded': 'downloaded-from',
                         'referrer': 'referring'}
        siblings = (self.parse_siblings(domain) for domain in req['domain_siblings'])
        uuid = self.parse_resolutions(req['resolutions'], req['subdomains'], siblings)
        for feature_type, relationship in feature_types.items():
            for feature in ('undetected_{}_samples', 'detected_{}_samples'):
                for sample in req.get(feature.format(feature_type), []):
                    status_code = self.parse_hash(sample[hash_type], False, uuid, relationship)
                    if status_code != 200:
                        return status_code
        if req.get(whois):
            whois_object = MISPObject(whois)
            whois_object.add_attribute('text', type='text', value=req[whois])
            self.misp_event.add_object(**whois_object)
        return self.parse_related_urls(req, recurse, uuid)

    def parse_hash(self, sample, recurse=False, uuid=None, relationship=None):
        req = requests.get(self.base_url.format('file'), params={'apikey': self.apikey, 'resource': sample})
        status_code = req.status_code
        if req.status_code == 200:
            req = req.json()
            vt_uuid = self.parse_vt_object(req)
            file_attributes = []
            for hash_type in ('md5', 'sha1', 'sha256'):
                if req.get(hash_type):
                    file_attributes.append({'type': hash_type, 'object_relation': hash_type,
                                            'value': req[hash_type]})
            if file_attributes:
                file_object = MISPObject('file')
                for attribute in file_attributes:
                    file_object.add_attribute(**attribute)
                file_object.add_reference(vt_uuid, 'analyzed-with')
                if uuid and relationship:
                    file_object.add_reference(uuid, relationship)
                self.misp_event.add_object(**file_object)
        return status_code

    def parse_ip(self, ip, recurse=False):
        req = requests.get(self.base_url.format('ip-address'), params={'apikey': self.apikey, 'ip': ip})
        if req.status_code != 200:
            return req.status_code
        req = req.json()
        if req.get('asn'):
            asn_mapping = {'network': ('ip-src', 'subnet-announced'),
                           'country': ('text', 'country')}
            asn_object = MISPObject('asn')
            asn_object.add_attribute('asn', type='AS', value=req['asn'])
            for key, value in asn_mapping.items():
                if req.get(key):
                    attribute_type, relation = value
                    asn_object.add_attribute(relation, type=attribute_type, value=req[key])
            self.misp_event.add_object(**asn_object)
        uuid = self.parse_resolutions(req['resolutions']) if req.get('resolutions') else None
        return self.parse_related_urls(req, recurse, uuid)

    def parse_url(self, url, recurse=False, uuid=None):
        req = requests.get(self.base_url.format('url'), params={'apikey': self.apikey, 'resource': url})
        status_code = req.status_code
        if req.status_code == 200:
            req = req.json()
            vt_uuid = self.parse_vt_object(req)
            if not recurse:
                feature = 'url'
                url_object = MISPObject(feature)
                url_object.add_attribute(feature, type=feature, value=url)
                url_object.add_reference(vt_uuid, 'analyzed-with')
                if uuid:
                    url_object.add_reference(uuid, 'hosted-in')
                self.misp_event.add_object(**url_object)
        return status_code

    ################################################################################
    ####                      Additional parsing functions                      #### # noqa
    ################################################################################

    def parse_related_urls(self, query_result, recurse, uuid=None):
        if recurse:
            for feature in ('detected_urls', 'undetected_urls'):
                if feature in query_result:
                    for url in query_result[feature]:
                        value = url['url'] if isinstance(url, dict) else url[0]
                        status_code = self.parse_url(value, False, uuid)
                        if status_code != 200:
                            return status_code
        else:
            for feature in ('detected_urls', 'undetected_urls'):
                if feature in query_result:
                    for url in query_result[feature]:
                        value = url['url'] if isinstance(url, dict) else url[0]
                        self.misp_event.add_attribute('url', value)
        return 200

    def parse_resolutions(self, resolutions, subdomains=None, uuids=None):
        domain_ip_object = MISPObject('domain-ip')
        if self.attribute.type == 'domain':
            domain_ip_object.add_attribute('domain', type='domain', value=self.attribute.value)
            attribute_type, relation, key = ('ip-dst', 'ip', 'ip_address')
        else:
            domain_ip_object.add_attribute('ip', type='ip-dst', value=self.attribute.value)
            attribute_type, relation, key = ('domain', 'domain', 'hostname')
        for resolution in resolutions:
            domain_ip_object.add_attribute(relation, type=attribute_type, value=resolution[key])
        if subdomains:
            for subdomain in subdomains:
                attribute = MISPAttribute()
                attribute.from_dict(**dict(type='domain', value=subdomain))
                self.misp_event.add_attribute(**attribute)
                domain_ip_object.add_reference(attribute.uuid, 'subdomain')
        if uuids:
            for uuid in uuids:
                domain_ip_object.add_reference(uuid, 'sibling-of')
        self.misp_event.add_object(**domain_ip_object)
        return domain_ip_object.uuid

    def parse_siblings(self, domain):
        attribute = MISPAttribute()
        attribute.from_dict(**dict(type='domain', value=domain))
        self.misp_event.add_attribute(**attribute)
        return attribute.uuid

    def parse_vt_object(self, query_result):
        vt_object = MISPObject('virustotal-report')
        vt_object.add_attribute('permalink', type='link', value=query_result['permalink'])
        detection_ratio = '{}/{}'.format(query_result['positives'], query_result['total'])
        vt_object.add_attribute('detection-ratio', type='text', value=detection_ratio)
        self.misp_event.add_object(**vt_object)
        return vt_object.uuid
Пример #10
0
        data = json.load(json_file)

        if 'Attribute' in data.get("response")[0].get("Event"):
            attributes = data.get("response")[0].get("Event").get("Attribute")
            for attribute in attributes:
                misp_tag = []
                if 'Tag' in attribute:
                    for tag in attribute.get('Tag'):
                        misp_tag.append(tag.get('name'))

                mispattribute = MISPAttribute()
                mispattribute.from_dict(
                    **{
                        'value': attribute.get("value"),
                        'category': attribute.get("category"),
                        'type': attribute.get("type"),
                        'to_ids': attribute.get("to_ids"),
                        'comment': attribute.get("comment"),
                        'Tag': misp_tag
                    })
                res = api.add_attribute(event, mispattribute)
                time.sleep(insert_sleep)
                count_attributes = count_attributes + 1

        if 'Object' in data.get("response")[0].get("Event"):
            objects = data.get("response")[0].get("Event").get("Object")
            for obj in objects:
                misp_object = MISPObject(obj.get('name'))
                if 'Attribute' in obj:
                    for attribute in obj.get('Attribute'):
                        misp_object.add_attribute(
Пример #11
0
class CytomicParser():
    def __init__(self, attribute, config_object):
        self.misp_event = MISPEvent()
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self.misp_event.add_attribute(**self.attribute)

        self.config_object = config_object

        if self.config_object:
            self.token = self.get_token()
        else:
            sys.exit('Missing configuration')

    def get_token(self):
        try:
            scope = self.config_object['scope']
            grant_type = self.config_object['grant_type']
            username = self.config_object['username']
            password = self.config_object['password']
            token_url = self.config_object['token_url']
            clientid = self.config_object['clientid']
            clientsecret = self.config_object['clientsecret']

            if scope and grant_type and username and password:
                data = {
                    'scope': scope,
                    'grant_type': grant_type,
                    'username': username,
                    'password': password
                }

                if token_url and clientid and clientsecret:
                    access_token_response = requests.post(
                        token_url,
                        data=data,
                        verify=False,
                        allow_redirects=False,
                        auth=(clientid, clientsecret))
                    tokens = json.loads(access_token_response.text)
                    if 'access_token' in tokens:
                        return tokens['access_token']
                    else:
                        self.result = {'error': 'No token received.'}
                        return
                else:
                    self.result = {
                        'error':
                        'No token_url, clientid or clientsecret supplied.'
                    }
                    return
            else:
                self.result = {
                    'error':
                    'No scope, grant_type, username or password supplied.'
                }
                return
        except Exception:
            self.result = {'error': 'Unable to connect to token_url.'}
            return

    def get_results(self):
        if hasattr(self, 'result'):
            return self.result
        event = json.loads(self.misp_event.to_json())
        results = {key: event[key] for key in ('Attribute', 'Object')}
        return {'results': results}

    def parse(self, searchkey):

        if self.token:

            endpoint_fileinformation = self.config_object[
                'endpoint_fileinformation']
            endpoint_machines = self.config_object['endpoint_machines']
            endpoint_machines_client = self.config_object[
                'endpoint_machines_client']
            query_machines = self.config_object['query_machines']
            query_machine_info = self.config_object['query_machine_info']

            # Update endpoint URLs
            query_endpoint_fileinformation = endpoint_fileinformation.format(
                md5=searchkey)
            query_endpoint_machines = endpoint_machines.format(md5=searchkey)

            # API calls
            api_call_headers = {'Authorization': 'Bearer ' + self.token}
            result_query_endpoint_fileinformation = requests.get(
                query_endpoint_fileinformation,
                headers=api_call_headers,
                verify=False)
            json_result_query_endpoint_fileinformation = json.loads(
                result_query_endpoint_fileinformation.text)

            if json_result_query_endpoint_fileinformation:

                cytomic_object = MISPObject('cytomic-orion-file')

                cytomic_object.add_attribute(
                    'fileName',
                    type='text',
                    value=json_result_query_endpoint_fileinformation[
                        'fileName'])
                cytomic_object.add_attribute(
                    'fileSize',
                    type='text',
                    value=json_result_query_endpoint_fileinformation[
                        'fileSize'])
                cytomic_object.add_attribute(
                    'last-seen',
                    type='datetime',
                    value=json_result_query_endpoint_fileinformation[
                        'lastSeen'])
                cytomic_object.add_attribute(
                    'first-seen',
                    type='datetime',
                    value=json_result_query_endpoint_fileinformation[
                        'firstSeen'])
                cytomic_object.add_attribute(
                    'classification',
                    type='text',
                    value=json_result_query_endpoint_fileinformation[
                        'classification'])
                cytomic_object.add_attribute(
                    'classificationName',
                    type='text',
                    value=json_result_query_endpoint_fileinformation[
                        'classificationName'])
                self.misp_event.add_object(**cytomic_object)

                result_query_endpoint_machines = requests.get(
                    query_endpoint_machines,
                    headers=api_call_headers,
                    verify=False)
                json_result_query_endpoint_machines = json.loads(
                    result_query_endpoint_machines.text)

                if query_machines and json_result_query_endpoint_machines and len(
                        json_result_query_endpoint_machines) > 0:
                    for machine in json_result_query_endpoint_machines:

                        if query_machine_info and machine['muid']:
                            query_endpoint_machines_client = endpoint_machines_client.format(
                                muid=machine['muid'])
                            result_endpoint_machines_client = requests.get(
                                query_endpoint_machines_client,
                                headers=api_call_headers,
                                verify=False)
                            json_result_endpoint_machines_client = json.loads(
                                result_endpoint_machines_client.text)

                            if json_result_endpoint_machines_client:

                                cytomic_machine_object = MISPObject(
                                    'cytomic-orion-machine')

                                clienttag = [{
                                    'name':
                                    json_result_endpoint_machines_client[
                                        'clientName']
                                }]

                                cytomic_machine_object.add_attribute(
                                    'machineName',
                                    type='target-machine',
                                    value=json_result_endpoint_machines_client[
                                        'machineName'],
                                    Tag=clienttag)
                                cytomic_machine_object.add_attribute(
                                    'machineMuid',
                                    type='text',
                                    value=machine['muid'])
                                cytomic_machine_object.add_attribute(
                                    'clientName',
                                    type='target-org',
                                    value=json_result_endpoint_machines_client[
                                        'clientName'],
                                    Tag=clienttag)
                                cytomic_machine_object.add_attribute(
                                    'clientId',
                                    type='text',
                                    value=machine['clientId'])
                                cytomic_machine_object.add_attribute(
                                    'machinePath',
                                    type='text',
                                    value=machine['lastPath'])
                                cytomic_machine_object.add_attribute(
                                    'first-seen',
                                    type='datetime',
                                    value=machine['firstSeen'])
                                cytomic_machine_object.add_attribute(
                                    'last-seen',
                                    type='datetime',
                                    value=machine['lastSeen'])
                                cytomic_machine_object.add_attribute(
                                    'creationDate',
                                    type='datetime',
                                    value=json_result_endpoint_machines_client[
                                        'creationDate'])
                                cytomic_machine_object.add_attribute(
                                    'clientCreationDateUTC',
                                    type='datetime',
                                    value=json_result_endpoint_machines_client[
                                        'clientCreationDateUTC'])
                                cytomic_machine_object.add_attribute(
                                    'lastSeenUtc',
                                    type='datetime',
                                    value=json_result_endpoint_machines_client[
                                        'lastSeenUtc'])
                                self.misp_event.add_object(
                                    **cytomic_machine_object)
        else:
            self.result = {'error': 'No (valid) token.'}
            return
Пример #12
0
class XforceExchange():
    def __init__(self, attribute, apikey, apipassword):
        self.base_url = "https://api.xforce.ibmcloud.com"
        self.misp_event = MISPEvent()
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self._apikey = apikey
        self._apipassword = apipassword
        self.result = {}
        self.objects = defaultdict(dict)
        self.status_mapping = {
            403:
            "Access denied, please check if your authentication is valid and if you did not reach the limit of queries.",
            404: "No result found for your query."
        }

    def parse(self):
        mapping = {
            'url': '_parse_url',
            'vulnerability': '_parse_vulnerability'
        }
        mapping.update(dict.fromkeys(('md5', 'sha1', 'sha256'), '_parse_hash'))
        mapping.update(dict.fromkeys(('domain', 'hostname'), '_parse_dns'))
        mapping.update(dict.fromkeys(('ip-src', 'ip-dst'), '_parse_ip'))
        to_call = mapping[self.attribute.type]
        getattr(self, to_call)(self.attribute.value)

    def get_result(self):
        if not self.misp_event.objects:
            if 'error' not in self.result:
                self.result[
                    'error'] = "No additional data found on Xforce Exchange."
            return self.result
        self.misp_event.add_attribute(**self.attribute)
        event = json.loads(self.misp_event.to_json())
        result = {
            key: event[key]
            for key in ('Attribute', 'Object') if (key in event and event[key])
        }
        return {'results': result}

    def _api_call(self, url):
        try:
            result = requests.get(url,
                                  auth=HTTPBasicAuth(self._apikey,
                                                     self._apipassword))
        except Exception as e:
            self.result['error'] = e
            return
        status_code = result.status_code
        if status_code != 200:
            try:
                self.result['error'] = self.status_mapping[status_code]
            except KeyError:
                self.result['error'] = 'An error with the API has occurred.'
            return
        return result.json()

    def _create_file(self, malware, relationship):
        file_object = MISPObject('file')
        for key, relation in zip(('filepath', 'md5'), ('filename', 'md5')):
            file_object.add_attribute(relation, malware[key])
        file_object.add_reference(self.attribute.uuid, relationship)
        return file_object

    def _create_url(self, malware):
        url_object = MISPObject('url')
        for key, relation in zip(('uri', 'domain'), ('url', 'domain')):
            url_object.add_attribute(relation, malware[key])
        attributes = tuple(f'{attribute.object_relation}_{attribute.value}'
                           for attribute in url_object.attributes)
        if attributes in self.objects['url']:
            del url_object
            return self.objects['url'][attributes]
        url_uuid = url_object.uuid
        self.misp_event.add_object(**url_object)
        self.objects['url'][attributes] = url_uuid
        return url_uuid

    def _fetch_types(self, value):
        if self.attribute.type in ('ip-src', 'ip-dst'):
            return 'ip', 'domain', self.attribute.value
        return 'domain', 'ip', value

    def _handle_file(self, malware, relationship):
        file_object = self._create_file(malware, relationship)
        attributes = tuple(f'{attribute.object_relation}_{attribute.value}'
                           for attribute in file_object.attributes)
        if attributes in self.objects['file']:
            self.objects['file'][attributes].add_reference(
                self._create_url(malware), 'dropped-by')
            del file_object
            return
        file_object.add_reference(self._create_url(malware), 'dropped-by')
        self.objects['file'][attributes] = file_object
        self.misp_event.add_object(**file_object)

    def _parse_dns(self, value):
        dns_result = self._api_call(f'{self.base_url}/resolve/{value}')
        if dns_result.get('Passive') and dns_result['Passive'].get('records'):
            itype, ftype, value = self._fetch_types(
                dns_result['Passive']['query'])
            misp_object = MISPObject('domain-ip')
            misp_object.add_attribute(itype, value)
            for record in dns_result['Passive']['records']:
                misp_object.add_attribute(ftype, record['value'])
            misp_object.add_reference(self.attribute.uuid, 'related-to')
            self.misp_event.add_object(**misp_object)

    def _parse_hash(self, value):
        malware_result = self._api_call(f'{self.base_url}/malware/{value}')
        if malware_result and malware_result.get('malware'):
            malware_report = malware_result['malware']
            for malware in malware_report.get('origins',
                                              {}).get('CnCServers',
                                                      {}).get('rows', []):
                self._handle_file(malware, 'related-to')

    def _parse_ip(self, value):
        self._parse_dns(value)
        self._parse_malware(value, 'ipr')

    def _parse_malware(self, value, feature):
        malware_result = self._api_call(
            f'{self.base_url}/{feature}/malware/{value}')
        if malware_result and malware_result.get('malware'):
            for malware in malware_result['malware']:
                self._handle_file(malware, 'associated-with')

    def _parse_url(self, value):
        self._parse_dns(value)
        self._parse_malware(value, 'url')

    def _parse_vulnerability(self, value):
        vulnerability_result = self._api_call(
            f'{self.base_url}/vulnerabilities/search/{value}')
        if vulnerability_result:
            for vulnerability in vulnerability_result:
                misp_object = MISPObject('vulnerability')
                for code in vulnerability['stdcode']:
                    misp_object.add_attribute('id', code)
                for feature, relation in zip(
                    ('title', 'description', 'temporal_score'),
                    ('summary', 'description', 'cvss-score')):
                    misp_object.add_attribute(relation, vulnerability[feature])
                for reference in vulnerability['references']:
                    misp_object.add_attribute('references',
                                              reference['link_target'])
                misp_object.add_reference(self.attribute.uuid, 'related-to')
                self.misp_event.add_object(**misp_object)
Пример #13
0
class VirusTotalParser():
    def __init__(self):
        super(VirusTotalParser, self).__init__()
        self.misp_event = MISPEvent()
        self.proxies = None

    def declare_variables(self, apikey, attribute):
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self.apikey = apikey

    def get_result(self):
        event = json.loads(self.misp_event.to_json())
        results = {
            key: event[key]
            for key in ('Attribute', 'Object') if (key in event and event[key])
        }
        return {'results': results}

    def parse_urls(self, query_result):
        for feature in ('detected_urls', 'undetected_urls'):
            if feature in query_result:
                for url in query_result[feature]:
                    value = url['url'] if isinstance(url, dict) else url[0]
                    self.misp_event.add_attribute('url', value)

    def parse_resolutions(self, resolutions, subdomains=None, uuids=None):
        domain_ip_object = MISPObject('domain-ip')
        if self.attribute.type in ('domain', 'hostname'):
            domain_ip_object.add_attribute('domain',
                                           type='domain',
                                           value=self.attribute.value)
            attribute_type, relation, key = ('ip-dst', 'ip', 'ip_address')
        else:
            domain_ip_object.add_attribute('ip',
                                           type='ip-dst',
                                           value=self.attribute.value)
            attribute_type, relation, key = ('domain', 'domain', 'hostname')
        for resolution in resolutions:
            domain_ip_object.add_attribute(relation,
                                           type=attribute_type,
                                           value=resolution[key])
        if subdomains:
            for subdomain in subdomains:
                attribute = MISPAttribute()
                attribute.from_dict(**dict(type='domain', value=subdomain))
                self.misp_event.add_attribute(**attribute)
                domain_ip_object.add_reference(attribute.uuid, 'subdomain')
        if uuids:
            for uuid in uuids:
                domain_ip_object.add_reference(uuid, 'sibling-of')
        self.misp_event.add_object(**domain_ip_object)

    def parse_vt_object(self, query_result):
        if query_result['response_code'] == 1:
            vt_object = MISPObject('virustotal-report')
            vt_object.add_attribute('permalink',
                                    type='link',
                                    value=query_result['permalink'])
            detection_ratio = '{}/{}'.format(query_result['positives'],
                                             query_result['total'])
            vt_object.add_attribute('detection-ratio',
                                    type='text',
                                    value=detection_ratio)
            self.misp_event.add_object(**vt_object)

    def get_query_result(self, query_type):
        params = {query_type: self.attribute.value, 'apikey': self.apikey}
        return requests.get(self.base_url, params=params, proxies=self.proxies)

    def set_proxy_settings(self, config: dict) -> dict:
        """Returns proxy settings in the requests format.
        If no proxy settings are set, return None."""
        proxies = None
        host = config.get('proxy_host')
        port = config.get('proxy_port')
        username = config.get('proxy_username')
        password = config.get('proxy_password')

        if host:
            if not port:
                misperrors['error'] = 'The virustotal_public_proxy_host config is set, ' \
                                    'please also set the virustotal_public_proxy_port.'
                raise KeyError
            parsed = urlparse(host)
            if 'http' in parsed.scheme:
                scheme = 'http'
            else:
                scheme = parsed.scheme
            netloc = parsed.netloc
            host = f'{netloc}:{port}'

            if username:
                if not password:
                    misperrors['error'] = 'The virustotal_public_proxy_username config is set, ' \
                                        'please also set the virustotal_public_proxy_password.'
                    raise KeyError
                auth = f'{username}:{password}'
                host = auth + '@' + host

            proxies = {
                'http': f'{scheme}://{host}',
                'https': f'{scheme}://{host}'
            }
        self.proxies = proxies
        return True
Пример #14
0
class TruSTARParser:
    ENTITY_TYPE_MAPPINGS = {
        'BITCOIN_ADDRESS': "btc",
        'CIDR_BLOCK': "ip-src",
        'CVE': "vulnerability",
        'URL': "url",
        'EMAIL_ADDRESS': "email-src",
        'SOFTWARE': "filename",
        'IP': "ip-src",
        'MALWARE': "malware-type",
        'MD5': "md5",
        'REGISTRY_KEY': "regkey",
        'SHA1': "sha1",
        'SHA256': "sha256"
    }

    REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}"

    CLIENT_METATAG = "MISP-{}".format(pymisp.__version__)

    def __init__(self, attribute, config):
        config['enclave_ids'] = config.get('enclave_ids',
                                           "").strip().split(',')
        config['client_metatag'] = self.CLIENT_METATAG
        self.ts_client = TruStar(config=config)

        self.misp_event = MISPEvent()
        self.misp_attribute = MISPAttribute()
        self.misp_attribute.from_dict(**attribute)
        self.misp_event.add_attribute(**self.misp_attribute)

    def get_results(self):
        """
        Returns the MISP Event enriched with TruSTAR indicator summary data.
        """
        event = json.loads(self.misp_event.to_json())
        results = {
            key: event[key]
            for key in ('Attribute', 'Object') if (key in event and event[key])
        }
        return {'results': results}

    def generate_trustar_links(self, entity_value):
        """
        Generates links to TruSTAR reports if they exist.

        :param entity_value: <str> Value of entity.
        """
        report_links = list()
        trustar_reports = self.ts_client.search_reports(entity_value)
        for report in trustar_reports:
            report_links.append(self.REPORT_BASE_URL.format(report.id))

        return report_links

    def parse_indicator_summary(self, summaries):
        """
        Converts a response from the TruSTAR /1.3/indicators/summaries endpoint
        a MISP trustar_report object and adds the summary data and links as attributes.

        :param summaries: <generator> A TruSTAR Python SDK Page.generator object for generating
                          indicator summaries pages.
        """

        for summary in summaries:
            trustar_obj = MISPObject('trustar_report')
            indicator_type = summary.indicator_type
            indicator_value = summary.value
            if indicator_type in self.ENTITY_TYPE_MAPPINGS:
                trustar_obj.add_attribute(
                    indicator_type,
                    attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type],
                    value=indicator_value)
                trustar_obj.add_attribute("INDICATOR_SUMMARY",
                                          attribute_type="text",
                                          value=json.dumps(summary.to_dict(),
                                                           sort_keys=True,
                                                           indent=4))
                report_links = self.generate_trustar_links(indicator_value)
                for link in report_links:
                    trustar_obj.add_attribute("REPORT_LINK",
                                              attribute_type="link",
                                              value=link)
                self.misp_event.add_object(**trustar_obj)
Пример #15
0
 def parse_siblings(self, domain):
     attribute = MISPAttribute()
     attribute.from_dict(**dict(type='domain', value=domain))
     self.misp_event.add_attribute(**attribute)
     return attribute.uuid
Пример #16
0
class TruSTARParser:
    ENTITY_TYPE_MAPPINGS = {
        'BITCOIN_ADDRESS': "btc",
        'CIDR_BLOCK': "ip-src",
        'CVE': "vulnerability",
        'URL': "url",
        'EMAIL_ADDRESS': "email-src",
        'SOFTWARE': "filename",
        'IP': "ip-src",
        'MALWARE': "malware-type",
        'MD5': "md5",
        'REGISTRY_KEY': "regkey",
        'SHA1': "sha1",
        'SHA256': "sha256"
    }

    # Relevant fields from each TruSTAR endpoint
    SUMMARY_FIELDS = ["severityLevel", "source", "score", "attributes"]
    METADATA_FIELDS = ["sightings", "firstSeen", "lastSeen", "tags"]

    REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}"

    CLIENT_METATAG = f"MISP-{pymisp.__version__}"

    def __init__(self, attribute, config):
        config['enclave_ids'] = config.get('enclave_ids',
                                           "").strip().split(',')
        config['client_metatag'] = self.CLIENT_METATAG
        self.ts_client = TruStar(config=config)

        self.misp_event = MISPEvent()
        self.misp_attribute = MISPAttribute()
        self.misp_attribute.from_dict(**attribute)
        self.misp_event.add_attribute(**self.misp_attribute)

    def get_results(self):
        """
        Returns the MISP Event enriched with TruSTAR indicator summary data.
        """
        try:
            event = json.loads(self.misp_event.to_json())
            results = {
                key: event[key]
                for key in ('Attribute', 'Object')
                if (key in event and event[key])
            }
            return {'results': results}
        except Exception as e:
            misperrors[
                'error'] += f" -- Encountered issue serializing enrichment data -- {e}"
            return misperrors

    def generate_trustar_link(self, entity_type, entity_value):
        """
        Generates link to TruSTAR report of entity.

        :param entity_type: <str> Type of entity.
        :param entity_value: <str> Value of entity.
        :return: <str> Link to indicator report in TruSTAR platform.
        """
        report_id = b64encode(
            quote(f"{entity_type}|{entity_value}").encode()).decode()

        return self.REPORT_BASE_URL.format(report_id)

    @staticmethod
    def extract_tags(enrichment_report):
        """
        Extracts tags from the enrichment report in order to add them
        to the TruSTAR MISP Object. Removes tags from report to avoid
        redundancy.

        :param: <OrderedDict> Enrichment data.
        :return: <list> List of tags.
        """
        if enrichment_report and enrichment_report.get('tags'):
            return [tag.get('name') for tag in enrichment_report.pop('tags')]
        return None

    def generate_enrichment_report(self, summary, metadata):
        """
        Extracts desired fields from summary and metadata reports and
        generates an enrichment report.

        :param summary: <trustar.IndicatorSummary> Indicator summary report.
        :param metadata: <trustar.Indicator> Indicator metadata report.
        :return: <str> Enrichment report.
        """
        # Preserve order of fields as they exist in SUMMARY_FIELDS and METADATA_FIELDS
        enrichment_report = OrderedDict()

        if summary:
            summary_dict = summary.to_dict()
            enrichment_report.update({
                field: summary_dict[field]
                for field in self.SUMMARY_FIELDS if summary_dict.get(field)
            })

        if metadata:
            metadata_dict = metadata.to_dict()
            enrichment_report.update({
                field: metadata_dict[field]
                for field in self.METADATA_FIELDS if metadata_dict.get(field)
            })

        return enrichment_report

    def parse_indicator_summary(self, indicator, summary, metadata):
        """
        Pulls enrichment data from the TruSTAR /indicators/summaries and /indicators/metadata endpoints
        and creates a MISP trustar_report.

        :param indicator: <str> Value of the attribute
        :summary: <trustar.IndicatorSummary> Indicator summary response object.
        :metadata: <trustar.Indicator> Indicator response object.
        """

        # Verify that the indicator type is supported by TruSTAR
        if summary and summary.indicator_type in self.ENTITY_TYPE_MAPPINGS:
            indicator_type = summary.indicator_type
        elif metadata and metadata.type in self.ENTITY_TYPE_MAPPINGS:
            indicator_type = metadata.type
        else:
            misperrors['error'] += " -- Attribute not found or not supported"
            raise Exception

        try:
            # Extract most relevant fields from indicator summary and metadata responses
            enrichment_report = self.generate_enrichment_report(
                summary, metadata)
            tags = self.extract_tags(enrichment_report)

            if enrichment_report:
                # Create MISP trustar_report object and populate it with enrichment data
                trustar_obj = MISPObject('trustar_report')
                trustar_obj.add_attribute(
                    indicator_type,
                    attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type],
                    value=indicator)
                trustar_obj.add_attribute("INDICATOR_SUMMARY",
                                          attribute_type="text",
                                          value=json.dumps(enrichment_report,
                                                           indent=4))

                report_link = self.generate_trustar_link(
                    indicator_type, indicator)
                trustar_obj.add_attribute("REPORT_LINK",
                                          attribute_type="link",
                                          value=report_link)

                self.misp_event.add_object(**trustar_obj)
            elif not tags:
                # If enrichment report is empty and there are no tags, nothing to add to attribute
                raise Exception("No relevant data found")

            if tags:
                for tag in tags:
                    self.misp_event.add_attribute_tag(tag, indicator)

        except Exception as e:
            misperrors[
                'error'] += f" -- Error enriching attribute {indicator} -- {e}"
            raise e
Пример #17
0
class APIVoidParser():
    def __init__(self, attribute):
        self.misp_event = MISPEvent()
        self.attribute = MISPAttribute()
        self.attribute.from_dict(**attribute)
        self.misp_event.add_attribute(**self.attribute)
        self.url = 'https://endpoint.apivoid.com/{}/v1/pay-as-you-go/?key={}&'

    def get_results(self):
        if hasattr(self, 'result'):
            return self.result
        event = json.loads(self.misp_event.to_json())
        results = {key: event[key] for key in ('Attribute', 'Object')}
        return {'results': results}

    def parse_domain(self, apikey):
        feature = 'dnslookup'
        if requests.get(f'{self.url.format(feature, apikey)}stats').json(
        )['credits_remained'] < 0.13:
            self.result = {
                'error':
                'You do not have enough APIVoid credits to proceed your request.'
            }
            return
        mapping = {
            'A': 'resolution-of',
            'MX': 'mail-server-of',
            'NS': 'server-name-of'
        }
        dnslookup = requests.get(
            f'{self.url.format(feature, apikey)}action=dns-any&host={self.attribute.value}'
        ).json()
        for item in dnslookup['data']['records']['items']:
            record_type = item['type']
            try:
                relationship = mapping[record_type]
            except KeyError:
                continue
            self._handle_dns_record(item, record_type, relationship)
        ssl = requests.get(
            f'{self.url.format("sslinfo", apikey)}host={self.attribute.value}'
        ).json()
        self._parse_ssl_certificate(ssl['data']['certificate'])

    def _handle_dns_record(self, item, record_type, relationship):
        dns_record = MISPObject('dns-record')
        dns_record.add_attribute('queried-domain',
                                 type='domain',
                                 value=item['host'])
        attribute_type, feature = ('ip-dst',
                                   'ip') if record_type == 'A' else ('domain',
                                                                     'target')
        dns_record.add_attribute(f'{record_type.lower()}-record',
                                 type=attribute_type,
                                 value=item[feature])
        dns_record.add_reference(self.attribute.uuid, relationship)
        self.misp_event.add_object(**dns_record)

    def _parse_ssl_certificate(self, certificate):
        x509 = MISPObject('x509')
        fingerprint = 'x509-fingerprint-sha1'
        x509.add_attribute(fingerprint,
                           type=fingerprint,
                           value=certificate['fingerprint'])
        x509_mapping = {
            'subject': {
                'name': ('text', 'subject')
            },
            'issuer': {
                'common_name': ('text', 'issuer')
            },
            'signature': {
                'serial': ('text', 'serial-number')
            },
            'validity': {
                'valid_from': ('datetime', 'validity-not-before'),
                'valid_to': ('datetime', 'validity-not-after')
            }
        }
        certificate = certificate['details']
        for feature, subfeatures in x509_mapping.items():
            for subfeature, mapping in subfeatures.items():
                attribute_type, relation = mapping
                x509.add_attribute(relation,
                                   type=attribute_type,
                                   value=certificate[feature][subfeature])
        x509.add_reference(self.attribute.uuid, 'seen-by')
        self.misp_event.add_object(**x509)