def test_multiple_qualifiers(): exp_and = stix2.AndBooleanExpression([ stix2.EqualityComparisonExpression( "network-traffic:dst_ref.type", "domain-name", ), stix2.EqualityComparisonExpression( "network-traffic:dst_ref.value", "example.com", ), ]) exp_ob = stix2.ObservationExpression(exp_and) qual_rep = stix2.RepeatQualifier(5) qual_within = stix2.WithinQualifier(stix2.IntegerConstant(1800)) exp = stix2.QualifiedObservationExpression( stix2.QualifiedObservationExpression(exp_ob, qual_rep), qual_within) assert str( exp ) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS" # noqa
def process_reports(self, reports): if reports is None: printer.error("No results") return for report in reports: name = report["name"] id = report["id"] stix2_objects = [] stix2_object_refs = [] # FFS AV, consistency! if 'tlp' in report: tlp_id = REF_TLPS[report['tlp'].upper()] elif 'TLP' in report: tlp_id = REF_TLPS[report['TLP'].upper()] else: tlp_id = REF_TLPS['WHITE'] sectors = report['industries'] if sectors: unmatched_sectors = [] added_sector = False for sector in [html.unescape(x.upper()) for x in sectors]: sector_name = None sector_id = None if sector in SECTOR_MAPPINGS: # sector_ids.append(self.octi_sectors[SECTOR_MAPPINGS[sector]]) sector_name = SECTOR_MAPPINGS[sector] try: sector_id = self.octi_sectors[ SECTOR_MAPPINGS[sector]] except Exception as e: printer.error(e) continue else: printer.debug(f"Looking for sector {sector}") match = difflib.get_close_matches( sector, self.octi_sectors.keys(), 1) if not len(match): printer.error( f"Unable to determine a matching sector for {sector}" ) unmatched_sectors.append(sector) continue # sector_ids.append(self.octi_sectors[match[0]]) sector_name = match[0] sector_id = self.octi_sectors[match[0]] if sector_name is not None: s = stix2.Identity(id=sector_id, name=sector_name, identity_class='class', custom_properties={ 'x_opencti_identity_type': 'sector' }) printer.debug(f"Adding sector {sector_name}") stix2_objects.append(s) stix2_object_refs.append(s) added_sector = True if not added_sector: printer.warn("Adding 'UNKNOWN' placeholder sector") s = stix2.Identity(id=self.octi_sectors["UNKNOWN"], name="Unknown", identity_class='class', custom_properties={ 'x_opencti_identity_type': 'sector' }) stix2_objects.append(s) stix2_object_refs.append(s) description = report['description'] if len(unmatched_sectors): description = description + "\n\n###\nUnable to find a match for the following sectors, " \ "please review manually:\n - " + '\n - '.join(unmatched_sectors) printer.info(f"Generating STIX2 for {name} ({id})") author = stix2.Identity(name=report['author_name'], identity_class='organization') stix2_objects.append(author) adversary = None if report['adversary']: printer.debug("Adding adversary {}".format( report['adversary'])) adversary = stix2.IntrusionSet(name=report['adversary']) stix2_object_refs.append(adversary) stix2_objects.append(adversary) if report['targeted_countries']: for country in report['targeted_countries']: printer.debug(f"Adding country {country}") c = stix2.Identity(name=country, identity_class='organization', custom_properties={ 'x_opencti_identity_type': 'country' }) stix2_objects.append(c) stix2_object_refs.append(c) external_refs = [] for eref in report['references']: external_refs.append( stix2.ExternalReference(source_name=tldextract.extract( eref).registered_domain, url=eref)) indicators = report["indicators"] if indicators: for indicator in indicators: resolved_type = self.resolve_type( indicator["type"].lower()) if resolved_type != None and indicator["is_active"]: observable_type = resolved_type observable_value = indicator["indicator"] pattern_type = 'stix' try: if observable_type in PATTERNTYPES: pattern_type = observable_type elif observable_type not in OPENCTISTIX2: printer.info("Not in stix2 dict") else: if 'transform' in OPENCTISTIX2[ observable_type]: if OPENCTISTIX2[observable_type][ 'transform'][ 'operation'] == 'remove_string': observable_value = observable_value.replace( OPENCTISTIX2[observable_type] ['transform']['value'], '') lhs = stix2.ObjectPath( OPENCTISTIX2[observable_type]['type'], OPENCTISTIX2[observable_type]['path']) observable_value = stix2.ObservationExpression( stix2.EqualityComparisonExpression( lhs, observable_value)) except Exception as e: printer.error(e) printer.info( "Could not determine suitable pattern") try: indicator_obj = stix2.Indicator( name=indicator["indicator"], description=indicator["description"], pattern=str(observable_value), valid_from=indicator["created"], labels=['malicious-activity'], created_by_ref=author, object_marking_refs=[tlp_id], custom_properties={ 'x_opencti_observable_type': resolved_type, 'x_opencti_observable_value': indicator["indicator"], 'x_opencti_pattern_type': pattern_type }) stix2_object_refs.append(indicator_obj) stix2_objects.append(indicator_obj) except Exception as e: printer.error(e) printer.info("Couldn't fetch indicator") else: printer.error("No indicators") report = stix2.Report(name=name, description=description, created_by_ref=author, labels=['threat-report'], published=report['created'], created=report['created'], modified=report['modified'], object_refs=stix2_object_refs, object_marking_refs=[tlp_id], external_references=external_refs) stix2_objects.append(report) bundle = stix2.Bundle(stix2_objects).serialize() if not self.dryrun: self.opencti_connector_helper.send_stix2_bundle( bundle, None, True, False) printer.info("Sending to OpenCTI") #printer.debug(str(bundle)) else: printer.debug(f"No sectors, disregarding '{name}'")
def test_greater_than_python_constant(): exp1 = stix2.GreaterThanComparisonExpression("file:extensions.'windows-pebinary-ext'.sections[*].entropy", 7.0) exp = stix2.ObservationExpression(exp1) assert str(exp) == "[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.0]"
def test_set_op(): exp = stix2.ObservationExpression(stix2.IsSubsetComparisonExpression("network-traffic:dst_ref.value", "2001:0db8:dead:beef:0000:0000:0000:0000/64")) assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
def test_greater_than(): exp1 = stix2.GreaterThanComparisonExpression("file:extensions.windows-pebinary-ext.sections[*].entropy", stix2.FloatConstant(7.0)) exp = stix2.ObservationExpression(exp1) assert str(exp) == "[file:extensions.windows-pebinary-ext.sections[*].entropy > 7.0]"
def __generate_observation_expression(self, size): """ Generate a random complex observation expression, which may consist of sub-expressions and qualifiers. :param size: The size of the desired observation expression, in terms of the number of simple comparison expressions it must contain :return: The observation expression AST """ assert size > 0 # The generation strategy is similar to that for comparison expressions # (see __generate_complex_comparison_expression()). It is generated in # two parts of random size; one side is constructed as a sub-expression. if size == 1: obs_expr = stix2.ObservationExpression( self.__generate_complex_comparison_expression(1)) else: lsize = random.randint(0, size) rsize = size - lsize if random.random() < 0.5: # Parenthesize right case obs_exprs = [ stix2.ObservationExpression( self.__generate_complex_comparison_expression(sz)) for sz in _rand_series(lsize) ] if rsize > 0: obs_exprs.append( stix2.ParentheticalExpression( self.__generate_observation_expression(rsize))) else: # Parenthesize left case if lsize == 0: obs_exprs = [] else: obs_exprs = [ stix2.ParentheticalExpression( self.__generate_observation_expression(lsize)) ] obs_exprs.extend( stix2.ObservationExpression( self.__generate_complex_comparison_expression(sz)) for sz in _rand_series(rsize)) ast_class = random.choice( (stix2.AndObservationExpression, stix2.OrObservationExpression, stix2.FollowedByObservationExpression)) obs_expr = ast_class(obs_exprs) if random.random() < self.__config.probability_qualifier: qualifier = self.__generate_random_qualifier() obs_expr = stix2.QualifiedObservationExpression( obs_expr, qualifier) return obs_expr