Exemplo n.º 1
0
	def virus_total_obj(api_key, indicators):
		'''Build our VirusTotal report object, File object, and AV signature objects
		   and link them appropriately.
		   indicator: Indicator hash to search in VT for

		'''
		vtr_report = VTReportObject(api_key["virustotal"]["key"], indicators)
		report_objects = []
		report_objects.append(vtr_report)
		av_report = vtr_report._report
		if vtr_report._resource_type == 'file':
			file_object = pymisp.MISPObject(name="file")
			file_object.add_attribute("md5", value=raw_report["md5"])
			file_object.add_attribute("sha1", value=raw_report["sha1"])
			file_object.add_attribute("sha256", value=raw_report["sha256"])
			vtr_report.add_reference(referenced_uuid=file_object.uuid, relationship_type="report of")
			report_objects.append(file_object)
		elif vtr_report._resource_type == "url":
			parsed = urlsplit(indicator)
			url_object = pymisp.MISPObject(name="url")
			url_object.add_attribute("url", value=parsed.geturl())
			url_object.add_attribute("host", value=parsed.hostname)
			url_object.add_attribute("scheme", value=parsed.scheme)
			url_object.add_attribute("port", value=parsed.port)
			vtr_report.add_reference(referenced_uuid=url_object.uuid, relationship_type="report of")
			report_objects.append(url_object)
		for antivirus in av_report["scans"]:
			if av_report["scans"][antivirus]["detected"]:
				av_object = pymisp.MISPObject(name="av-signature")
				av_object.add_attribute("software", value=antivirus)
				signature_name = raw_report["scans"][antivirus]["result"]
				av_object.add_attribute("signature", value=signature_name, disable_correlation=True)
				vtr_report.add_reference(referenced_uuid=av_object.uuid, relationship_type="included-in")
				report_objects.append(av_object)
Exemplo n.º 2
0
 def parse_object(self, o, labels):
     object_type = self.get_misp_type(labels)
     object_category = self.get_misp_category(labels)
     stix_type = o._type
     misp_object = pymisp.MISPObject(object_type)
     misp_object['meta-category'] = object_category
     if stix_type == 'indicator':
         pattern = o.get('pattern').replace('\\\\', '\\').split(' AND ')
         pattern[0] = pattern[0][2:]
         pattern[-1] = pattern[-1][:-2]
         attributes = objects_mapping[object_type]['pattern'](pattern)
     if stix_type == 'observed-data':
         observable = o.get('objects')
         attributes = objects_mapping[object_type]['observable'](observable)
     for attribute in attributes:
         misp_object.add_attribute(**attribute)
     misp_object.to_ids = bool(labels[1].split('=')[1])
     self.misp_event.add_object(**misp_object)
Exemplo n.º 3
0
 def parse_course_of_action(self, o):
     misp_object = pymisp.MISPObject('course-of-action')
     if 'name' in o:
         attribute = {
             'type': 'text',
             'object_relation': 'name',
             'value': o.get('name')
         }
         misp_object.add_attribute(**attribute)
     else:
         return
     if 'description' in o:
         attribute = {
             'type': 'text',
             'object_relation': 'description',
             'value': o.get('description')
         }
         misp_object.add_attribute(**attribute)
     self.misp_event.add_object(**misp_object)
Exemplo n.º 4
0
    def misp_send(self, strMISPEventID, strInput, strInfo, strUsername):
        # Establish communication with MISP

        # The main processing block.
        try:
            objects = []
            #get comments and tags from string input
            str_comment, tags = self.get_comm_and_tags(strInput)
            if tags == None:
                self.misp_logger.info('Irate not in Tags: %s equals None' %
                                      tags)
                response = None
                return response
            #setup misp objects
            mispobj_email = pymisp.MISPObject(name="email")
            mispobj_file = pymisp.MISPObject(name="file")
            mispobj_files = {}
            mispobj_domainip = pymisp.MISPObject(name="domain-ip")
            url_no = 0
            file_no = 0
            mispobj_urls = {}

            #process input
            for line in strInput.splitlines():
                if ("domain:" in line.lower()
                    ):  #Catch domain and add to domain/IP object
                    mispobj_domainip = pymisp.MISPObject(name="domain-ip")
                    vals = line.split(":", 1)
                    mispobj_domainip.add_attribute("domain",
                                                   value=vals[1].strip(),
                                                   comment=str_comment)
                    objects.append(mispobj_domainip)
                elif ("ip:" in line.lower()) or ("ip-dst:" in line.lower(
                )) or ("ip-src:"
                       in line.lower()):  #Catch IP and add to domain/IP object
                    if "domain:" in strInput.splitlines():
                        mispobj_domainip = pymisp.MISPObject(name="domain-ip")
                        vals = line.split(":", 1)
                        mispobj_domainip.add_attribute("ip",
                                                       value=vals[1].strip(),
                                                       comment=str_comment)
                        objects.append(mispobj_domainip)
                    else:
                        mispobj_network_connection = pymisp.MISPObject(
                            name="network-connection")
                        vals = line.split(":", 1)
                        if ("ip:" in line.lower()) or ("ip-dst:"
                                                       in line.lower()):
                            mispobj_network_connection.add_attribute(
                                "ip-dst",
                                type="ip-dst",
                                value=vals[1].strip(),
                                comment=str_comment)
                        else:
                            mispobj_network_connection.add_attribute(
                                "ip-src",
                                type="ip-src",
                                value=vals[1].strip(),
                                comment=str_comment)
                        objects.append(mispobj_network_connection)

                elif ("source-email:"
                      in line.lower()) or ("email-source" in line.lower()) or (
                          "from:" in line.lower()
                      ):  #Catch email and add to email object
                    vals = line.split(":", 1)
                    mispobj_email.add_attribute("from",
                                                value=vals[1].strip(),
                                                comment=str_comment)
                elif ("url:" in line.lower()) or (
                    ('kit:' in line.lower() or ('creds:' in line.lower())) and
                    (('hxxp' in line.lower()) or ('http' in line.lower()))
                ):  #Catch URL and add to URL object
                    vals = line.split(":", 1)
                    url = vals[1].strip()
                    url = refang(url)
                    parsed = urlparse(url)
                    mispobj_url = pymisp.MISPObject(name="url")
                    mispobj_url.add_attribute("url",
                                              value=parsed.geturl(),
                                              category="Payload delivery",
                                              comment=str_comment)
                    if parsed.hostname:
                        mispobj_url.add_attribute("host",
                                                  value=parsed.hostname,
                                                  comment=str_comment)
                    if parsed.scheme:
                        mispobj_url.add_attribute("scheme",
                                                  value=parsed.scheme,
                                                  comment=str_comment)
                    if parsed.port:
                        mispobj_url.add_attribute("port",
                                                  value=parsed.port,
                                                  comment=str_comment)
                    mispobj_urls[url_no] = mispobj_url
                    url_no += 1

                #Catch different hashes and add to file object
                elif ("sha1:" in line.lower()) or ("SHA1:" in line):
                    vals = line.split(":", 1)
                    mispobj_file.add_attribute("sha1",
                                               value=vals[1].strip(),
                                               comment=str_comment)
                elif ("sha256:" in line.lower()) or ("SHA256:" in line):
                    vals = line.split(":", 1)
                    mispobj_file.add_attribute("sha256",
                                               value=vals[1].strip(),
                                               comment=str_comment)
                elif ("md5:" in line.lower()) or ("MD5:" in line):
                    vals = line.split(":", 1)
                    mispobj_file.add_attribute("md5",
                                               value=vals[1].strip(),
                                               comment=str_comment)
                elif (
                        "subject:" in line.lower()
                ):  #or ("subject:" in line): #Catch subject and add to email object
                    self.misp_logger.info('adding subject')
                    vals = line.split(":", 1)
                    mispobj_email.add_attribute("subject",
                                                value=vals[1].strip(),
                                                comment=str_comment)
                elif ("hash|filename:" in line.lower()
                      ):  #catch hash|filename pair and add to file object
                    vals = line.split(":", 1)
                    val = vals[1].split("|")
                    l_hash = val[0]
                    l_filename = val[1]
                    l_mispobj_file = pymisp.MISPObject(name="file")
                    if len(re.findall(r"\b[a-fA-F\d]{32}\b", l_hash)) > 0:
                        l_mispobj_file.add_attribute("md5",
                                                     value=l_hash.strip(),
                                                     comment=str_comment)
                        l_mispobj_file.add_attribute("filename",
                                                     value=l_filename.strip(),
                                                     comment=str_comment)
                        mispobj_files[file_no] = l_mispobj_file
                    elif len(re.findall(r'\b[0-9a-f]{40}\b', l_hash)) > 0:
                        l_mispobj_file.add_attribute("sha1",
                                                     value=l_hash.strip(),
                                                     comment=str_comment)
                        l_mispobj_file.add_attribute("filename",
                                                     value=l_filename.strip(),
                                                     comment=str_comment)
                        mispobj_files[file_no] = l_mispobj_file
                    elif len(re.findall(r'\b[A-Fa-f0-9]{64}\b', l_hash)) > 0:
                        l_mispobj_file.add_attribute("sha256",
                                                     value=l_hash.strip(),
                                                     comment=str_comment)
                        l_mispobj_file.add_attribute("filename",
                                                     value=l_filename.strip(),
                                                     comment=str_comment)
                        mispobj_files[file_no] = l_mispobj_file
                    file_no += 1

            #add all misp objects to List to be processed and submitted to MISP server as one.
            if len(mispobj_file.attributes) > 0:
                objects.append(mispobj_file)
            if len(mispobj_email.attributes) > 0:
                objects.append(mispobj_email)

            for u_key, u_value in mispobj_urls.items():
                if len(u_value.attributes) > 0:
                    objects.append(u_value)
            for f_key, f_value in mispobj_files.items():
                if len(f_value.attributes) > 0:
                    objects.append(f_value)
            # Update timestamp and event

        except Exception as e:
            error = traceback.format_exc()
            response = "Error occured when converting string to misp objects:\n %s" % error
            self.misp_logger.error(response)
            return response

        if self.check_object_length(objects) != True:
            self.misp_logger.error(
                'Input from %s did not contain accepted tags.\n Input: \n%s' %
                (strUsername, strInput))
            return "Error in the tags you entered. Please see the guide for accepted tags."

        try:
            event = self.misp.new_event(info=strInfo,
                                        distribution='0',
                                        analysis='2',
                                        threat_level_id='3',
                                        published=True)
            misp_event = MISPEvent()
            misp_event.load(event)
            a, b = self.submit_to_misp(self.misp, misp_event, objects)
            for tag in tags:
                self.misp.tag(misp_event.uuid, tag)
            self.misp.add_internal_comment(misp_event.id,
                                           reference="Author: " + strUsername,
                                           comment=str_comment)
            self.misp.fast_publish(misp_event.id, alert=False)
            misp_event = self.misp.get_event(misp_event.id)
            response = misp_event
            #for response in misp_event:
            if ('errors' in response and response['errors'] != None):
                return ("Submission error: " + repr(response['errors']))
            else:
                if response['Event']['RelatedEvent']:
                    e_related = ""
                    for each in response['Event']['RelatedEvent']:
                        e_related = e_related + each['Event']['id'] + ", "
                    return "Created ID: " + str(
                        response['Event']
                        ['id']) + "\nRelated Events: " + ''.join(e_related)
                else:
                    return "Created ID: " + str(response['Event']['id'])

        except Exception as e:
            error = traceback.format_exc()
            response = "Error occured when submitting to misp:\n %s" % error
            self.misp_logger.error(response)
            return response
Exemplo n.º 5
0
    def parse(self, analysis_link, result):
        """
        Parse the analysis result into a MISP event.

        :param str analysis_link: the analysis link
        :param dict[str, any] result: the JSON returned by the analysis client.
        :rtype: MISPEvent
        :return: some results that can be consumed by MIPS.
        """
        self.misp_event = pymisp.MISPEvent()

        # Add analysis subject info
        if "url" in result["analysis_subject"]:
            o = pymisp.MISPObject("url")
            o.add_attribute("url", result["analysis_subject"]["url"])
        else:
            o = pymisp.MISPObject("file")
            o.add_attribute("md5", type="md5", value=result["analysis_subject"]["md5"])
            o.add_attribute("sha1", type="sha1", value=result["analysis_subject"]["sha1"])
            o.add_attribute("sha256", type="sha256", value=result["analysis_subject"]["sha256"])
            o.add_attribute(
                "mimetype",
                type="mime-type",
                value=result["analysis_subject"]["mime_type"]
            )
        self.misp_event.add_object(o)

        # Add HTTP requests from url analyses
        network_dict = result.get("report", {}).get("analysis", {}).get("network", {})
        for request in network_dict.get("requests", []):
            parsed_uri = parse.urlparse(request["url"])
            o = pymisp.MISPObject(name='http-request')
            o.add_attribute('host', parsed_uri.netloc)
            o.add_attribute('method', "GET")
            o.add_attribute('uri', request["url"])
            o.add_attribute("ip", request["ip"])
            self.misp_event.add_object(o)

        # Add network behaviors from files
        for subject in result.get("report", {}).get("analysis_subjects", []):

            # Add DNS requests
            for dns_query in subject.get("dns_queries", []):
                hostname = dns_query.get("hostname")
                # Skip if it is an IP address
                try:
                    if hostname == "wpad":
                        continue
                    _ = ipaddress.ip_address(hostname)
                    continue
                except ValueError:
                    pass

                o = pymisp.MISPObject(name='dns-record')
                o.add_attribute('queried-domain', hostname)
                self.misp_event.add_object(o)

            # Add HTTP conversations (as network connection and as http request)
            for http_conversation in subject.get("http_conversations", []):
                o = pymisp.MISPObject(name="network-connection")
                o.add_attribute("ip-src", http_conversation["src_ip"])
                o.add_attribute("ip-dst", http_conversation["dst_ip"])
                o.add_attribute("src-port", http_conversation["src_port"])
                o.add_attribute("dst-port", http_conversation["dst_port"])
                o.add_attribute("hostname-dst", http_conversation["dst_host"])
                o.add_attribute("layer3-protocol", "IP")
                o.add_attribute("layer4-protocol", "TCP")
                o.add_attribute("layer7-protocol", "HTTP")
                self.misp_event.add_object(o)

                method, path, http_version = http_conversation["url"].split(" ")
                if http_conversation["dst_port"] == 80:
                    uri = "http://{}{}".format(http_conversation["dst_host"], path)
                else:
                    uri = "http://{}:{}{}".format(
                        http_conversation["dst_host"],
                        http_conversation["dst_port"],
                        path
                    )
                o = pymisp.MISPObject(name='http-request')
                o.add_attribute('host', http_conversation["dst_host"])
                o.add_attribute('method', method)
                o.add_attribute('uri', uri)
                o.add_attribute('ip', http_conversation["dst_ip"])
                self.misp_event.add_object(o)

        # Add sandbox info like score and sandbox type
        o = pymisp.MISPObject(name="sandbox-report")
        sandbox_type = "saas" if is_analysis_hosted(analysis_link) else "on-premise"
        o.add_attribute("score", result["score"])
        o.add_attribute("sandbox-type", sandbox_type)
        o.add_attribute("{}-sandbox".format(sandbox_type), "lastline")
        o.add_attribute("permalink", analysis_link)
        self.misp_event.add_object(o)

        # Add behaviors
        o = pymisp.MISPObject(name="sb-signature")
        o.add_attribute("software", "Lastline")
        for activity in result.get("malicious_activity", []):
            a = pymisp.MISPAttribute()
            a.from_dict(type="text", value=activity)
            o.add_attribute("signature", **a)
        self.misp_event.add_object(o)

        # Add mitre techniques
        for technique in self._get_mitre_techniques(result):
            self.misp_event.add_tag(technique)
Exemplo n.º 6
0
def main(args):
    logging.info('Passing through misp_exporter')
    with open(args.fingerprint) as file:
        try:
            data = json.load(file)
        except Exception:
            logging.error('Fingerprint not compatible.')
            return
    misp = pymisp.ExpandedPyMISP(args.misp_url,
                                 args.misp_key,
                                 ssl=False,
                                 debug=True)
    event = pymisp.MISPEvent(strict_validation=True)
    event.info = args.event_info
    event.distribution = args.distribution
    event.threat_level_id = args.threat_level
    event.analysis = args.analysis_level
    event.add_tag(tag='validated')
    ddos = pymisp.MISPObject('ddos', strict=True)
    logging.info('Fingerprint processed: {}'.format(args.fingerprint))
    if 'attack_vector' in data:
        av = data['attack_vector'][0]
        snort = {
            'protocol': [],
            'src_ip': [],
            'src_port': [],
            'dst_port': [],
            'options': [],
        }
        df = find_ips(av)
        logging.info('IPs found: {}'.format(len(df['ip'])))
        subnets = utils.smart_aggregate(df)
        logging.info('The IPs were summarized in: {} subnets'.format(
            len(subnets)))
        if args.subnets:
            add_attributes(event, subnets, 'ip-src', 'attacker subnet')
            ddos.add_attributes('ip-src', *subnets)
            snort['src_ip'] += subnets
        else:
            add_attributes(event, df['ip'], 'ip-src', 'attacker ip')
            ddos.add_attributes('ip-src', *df['ip'])
            snort['src_ip'] += df['ip'].tolist()
        if 'srcport' in av:
            sport = find_attr(av['srcport'], 'srcport')
            add_attributes(event, sport['srcport'], 'port',
                           'source port of attack')
            ddos.add_attributes('src-port', *sport['srcport'])
            snort['src_port'] += sport['srcport'].tolist()
        if 'dstport' in av:
            dport = find_attr(av['dstport'], 'dstport')
            add_attributes(event, dport['dstport'], 'port',
                           'destination port of attack')
            ddos.add_attributes('dst-port', *sport['dstport'])
            snort['dst_port'] += sport['dstport']
        if 'ip_proto' in av:
            proto4 = find_attr(av['ip_proto'], 'ip_proto')
            add_attributes(event, proto4['ip_proto'], 'other',
                           '4 level protocol of attack')
            ddos.add_attributes('protocol', *proto4['ip_proto'])
            snort['protocol'] += proto4['ip_proto'].tolist()
        if 'dns_qry_name' in av:
            snort['options'] += snort_content(av['dns_qry_name'])
        if 'http_request' in av:
            snort['options'] += snort_content(av['http_request'])
        if 'http_response' in av:
            snort['options'] += snort_content(av['http_response'])
        if 'http_user_agent' in av:
            snort['options'] += snort_content(av['http_user_agent'])
        if 'icmp_type' in av:
            snort['options'] += snort_itype(av['icmp_type'])
        if 'icmp_code' in av:
            snort['options'] += snort_icode(av['icmp_code'])
        if 'ip_ttl' in av:
            snort['options'] += snort_ttl(av['ip_ttl'])
        if 'ntp_priv_reqcode' in av:
            snort['options'] += snort_content(av['ntp_priv_reqcode'])
        if 'tcp_flags' in av:
            snort['options'] += snort_flags(av['tcp_flags'])
        if 'tags' in av:
            for tag in av['tags']:
                event.add_tag(tag=tag)
        if 'duration_sec' in av:
            add_attribute_comment(event, 'duration_sec', av['duration_sec'])
        if 'total_dst_ports' in av:
            add_attribute_comment(event, 'total_dst_ports',
                                  av['total_dst_ports'])
        if 'total_ips' in av:
            add_attribute_comment(event, 'total_ips', av['total_ips'])
        if 'total_packets' in av:
            add_attribute_comment(event, 'total_packets', av['total_packets'])
        add_attribute_snort(event, snort)
        event.add_tag(tag='ddos attack')
    if 'amplifiers' in data:
        amp = data['amplifiers'][0]
        dfa = find_ips(amp)
        logging.info('Amplifier IPs found: {}'.format(len(dfa['ip'])))
        subnetsa = utils.smart_aggregate(dfa)
        logging.info('The IPs were summarized in: {} subnets'.format(
            len(subnetsa)))
        if args.subnets:
            add_attributes(event, subnetsa, 'ip-src', 'amplifier subnet')
            for elem in subnetsa:
                ddos.add_attribute('ip-src', elem, to_ids=0)
        else:
            add_attributes(event, dfa['ip'], 'ip-src', 'amplifier ip')
            for elem in dfa['ip']:
                ddos.add_attribute('ip-src', elem, to_ids=0)
        event.add_tag(tag='ddos amplification attack')
    p = pathlib.Path(args.fingerprint)
    comment = 'DDoS fingerprint json file generated by dissector'
    event.add_attribute(type='attachment',
                        value=p.name,
                        data=p,
                        comment=comment)
    event.add_object(ddos, pythonify=True)
    event = misp.add_event(event, pythonify=True)
Exemplo n.º 7
0
    def parse(self, analysis_link: str, result: Dict[str,
                                                     Any]) -> pymisp.MISPEvent:
        """
        Parse the analysis result into a MISP event.

        :param str analysis_link: the analysis link
        :param dict[str, any] result: the JSON returned by the analysis client.
        :rtype: pymisp.MISPEvent
        :return: a MISP event
        """
        misp_event = pymisp.MISPEvent()

        # Add analysis subject info
        if "url" in result["analysis_subject"]:
            o = pymisp.MISPObject("url")
            o.add_attribute("url", result["analysis_subject"]["url"])
        else:
            o = pymisp.MISPObject("file")
            o.add_attribute("md5",
                            type="md5",
                            value=result["analysis_subject"]["md5"])
            o.add_attribute("sha1",
                            type="sha1",
                            value=result["analysis_subject"]["sha1"])
            o.add_attribute("sha256",
                            type="sha256",
                            value=result["analysis_subject"]["sha256"])
            o.add_attribute("mimetype",
                            category="Payload delivery",
                            type="mime-type",
                            value=result["analysis_subject"]["mime_type"])
        misp_event.add_object(o)

        # Add HTTP requests from url analyses
        network_dict = result.get("report", {}).get("analysis",
                                                    {}).get("network", {})
        for request in network_dict.get("requests", []):
            if not request["url"] and not request["ip"]:
                continue
            o = pymisp.MISPObject(name="http-request")
            o.add_attribute("method", "GET")
            if request["url"]:
                parsed_uri = parse.urlparse(request["url"])
                o.add_attribute("host", parsed_uri.netloc)
                o.add_attribute("uri", request["url"])
            if request["ip"]:
                o.add_attribute("ip-dst", request["ip"])
            misp_event.add_object(o)

        # Add network behaviors from files
        for subject in result.get("report", {}).get("analysis_subjects", []):

            # Add DNS requests
            for dns_query in subject.get("dns_queries", []):
                hostname = dns_query.get("hostname")
                # Skip if it is an IP address
                try:
                    if hostname == "wpad" or hostname == "localhost":
                        continue
                    # Invalid hostname, e.g., hostname: ZLKKJRPY or 2.2.0.10.in-addr.arpa.
                    if "." not in hostname or hostname[-1] == ".":
                        continue
                    _ = ipaddress.ip_address(hostname)
                    continue
                except ValueError:
                    pass

                o = pymisp.MISPObject(name="domain-ip")
                o.add_attribute("hostname", type="hostname", value=hostname)
                for ip in dns_query.get("results", []):
                    o.add_attribute("ip", type="ip-dst", value=ip)

                misp_event.add_object(o)

            # Add HTTP conversations (as network connection and as http request)
            for http_conversation in subject.get("http_conversations", []):
                o = pymisp.MISPObject(name="network-connection")
                o.add_attribute("ip-src", http_conversation["src_ip"])
                o.add_attribute("ip-dst", http_conversation["dst_ip"])
                o.add_attribute("src-port", http_conversation["src_port"])
                o.add_attribute("dst-port", http_conversation["dst_port"])
                o.add_attribute("hostname-dst", http_conversation["dst_host"])
                o.add_attribute("layer3-protocol", "IP")
                o.add_attribute("layer4-protocol", "TCP")
                o.add_attribute("layer7-protocol", "HTTP")
                misp_event.add_object(o)

                method, path, http_version = http_conversation["url"].split(
                    " ")
                if http_conversation["dst_port"] == 80:
                    uri = "http://{}{}".format(http_conversation["dst_host"],
                                               path)
                else:
                    uri = "http://{}:{}{}".format(
                        http_conversation["dst_host"],
                        http_conversation["dst_port"], path)
                o = pymisp.MISPObject(name="http-request")
                o.add_attribute("host", http_conversation["dst_host"])
                o.add_attribute("method", method)
                o.add_attribute("uri", uri)
                o.add_attribute("ip-dst", http_conversation["dst_ip"])
                misp_event.add_object(o)

        # Add sandbox info like score and sandbox type
        o = pymisp.MISPObject(name="sandbox-report")
        sandbox_type = "saas" if tau_clients.is_task_hosted(
            analysis_link) else "on-premise"
        o.add_attribute("score", result["score"])
        o.add_attribute("sandbox-type", sandbox_type)
        o.add_attribute("{}-sandbox".format(sandbox_type),
                        "vmware-nsx-defender")
        o.add_attribute("permalink", analysis_link)
        misp_event.add_object(o)

        # Add behaviors
        # Check if its not empty first, as at least one attribute has to be set for sb-signature object
        if result.get("malicious_activity", []):
            o = pymisp.MISPObject(name="sb-signature")
            o.add_attribute("software", "VMware NSX Defender")
            for activity in result.get("malicious_activity", []):
                a = pymisp.MISPAttribute()
                a.from_dict(type="text", value=activity)
                o.add_attribute("signature", **a)
            misp_event.add_object(o)

        # Add mitre techniques
        for techniques in result.get("activity_to_mitre_techniques",
                                     {}).values():
            for technique in techniques:
                for misp_technique_id, misp_technique_name in self.techniques_galaxy.items(
                ):
                    if technique["id"].casefold(
                    ) in misp_technique_id.casefold():
                        # If report details a sub-technique, trust the match
                        # Otherwise trust it only if the MISP technique is not a sub-technique
                        if "." in technique[
                                "id"] or "." not in misp_technique_id:
                            misp_event.add_tag(misp_technique_name)
                            break
        return misp_event