def map_to_cif(indicator: Indicator, confidence: int, tags: List[str], tlp: str, group: str, logger) -> Union[CIFIndicator, None]: """ Maps a STIX-2 Indicator to a CIFv3 compatible indicator format. @param indicator The STIX-2 Indicator to map @param confidence The confidence to use when building the CIF indicator @param tags The tags to use when building the CIF indicator @param tlp The tlp to use when building the CIF indicator @param group The group to use when building the CIF indicator @return the mapped intel item or None """ if not indicator or type(indicator) is not Indicator: logger.debug(f"Expected STIX-2 indicator, discarding {indicator}") return None if (ThreatBusSTIX2Constants.X_THREATBUS_UPDATE.value in indicator.object_properties()): logger.debug( f"CIFv3 only supports adding indicators, not deleting / editing. Discardig {indicator}" ) return None if not is_point_equality_ioc(indicator.pattern): logger.debug( f"CIFv3 only supports point indicators, discardig {indicator}") return None object_path, ioc_value = split_object_path_and_value(indicator.pattern) if object_path not in cif_supported_types: logger.debug( f"Discardig indicator with unsupported object-path {indicator}") return None # convert lasttime lasttime = indicator.created.strftime("%Y-%m-%dT%H:%M:%S.%fZ") ioc_dict = { "indicator": ioc_value, "confidence": confidence, "tags": tags, "tlp": tlp, "group": group, "lasttime": lasttime, } try: return CIFIndicator(**ioc_dict) except InvalidIndicator as e: logger.error(f"Invalid CIF indicator {e}") except Exception as e: logger.error(f"CIF indicator error: {e}")
def map_indicator_to_broker_event(indicator: Indicator, module_namespace: str, logger) -> Union[broker.zeek.Event, None]: """ Maps STIX-2 Indicators to Broker events using the Zeek Intel format @see https://docs.zeek.org/en/current/scripts/base/frameworks/intel/main.zeek.html#type-Intel::Type @param indicator The STIX-2 Indicator to convert @param module_namespace A Zeek namespace to use for sending the event @return The mapped broker event or None """ if type(indicator) is not Indicator: logger.debug( f"Discarding message, expected STIX-2 Indicator: {indicator}") return None if not is_point_equality_ioc(indicator.pattern): logger.debug( f"Zeek only supports point-IoCs. Cannot map compound pattern to a Zeek Intel item: {indicator.pattern}" ) return None object_path, ioc_value = split_object_path_and_value(indicator.pattern) # get matching Zeek intel type zeek_type = zeek_intel_type_map.get(object_path, None) if not zeek_type: logger.debug( f"No matching Zeek type found for STIX-2 indicator type '{object_path}'" ) return None if zeek_type == "URL": # remove leading protocol, if any parsed = urlparse(ioc_value) scheme = f"{parsed.scheme}://" ioc_value = parsed.geturl().replace(scheme, "", 1) elif zeek_type == "ADDR" and re.match(".+/.+", ioc_value): # elevate to subnet if possible zeek_type = "SUBNET" operation = "ADD" ## Zeek operation to add a new Intel item if (ThreatBusSTIX2Constants.X_THREATBUS_UPDATE.value in indicator.object_properties() and indicator.x_threatbus_update == Operation.REMOVE.value): operation = "REMOVE" return broker.zeek.Event( f"{module_namespace}::intel", (indicator.created, str( indicator.id), zeek_type, ioc_value, operation), )
def _handle_indicator(self, indicator: Indicator): """ Handles a STIX-2 Indicator update received via Threat Bus. Does nothing in case the indicator already exists and the new indicator does not add any new fields/values to the existing indicator. By doing so, this function effectively avoids double updates that otherwise would result in SSE events without a real change. @param indicator The STIX-2 Indicator received from Threat Bus """ if type(indicator) is not Indicator: self.opencti_helper.log_error( f"Error ingesting indicator from Threat Bus. Expected a STIX-2 Indicator: {indicator}" ) return if ( ThreatBusSTIX2Constants.X_THREATBUS_UPDATE.value in indicator.object_properties() and indicator.x_threatbus_update == Operation.REMOVE.value ): # OpenCTI does not support indicator removal via API calls (yet) return lookup_resp = self.opencti_helper.api.indicator.read(id=indicator.id) if not lookup_resp: # No indicator with that ID exists already. self._create_or_update_indicator(indicator) return lookup_resp["id"] = lookup_resp["standard_id"] lookup_indicator = Indicator(**lookup_resp, allow_custom=True) # We found an existing indicator. To avoid double updates in the SSE # stream we check if the indicator from Threat Bus adds anything new. for prop, new_value in indicator.items(): if prop == "id" or prop.startswith("x_"): continue existing_value = lookup_indicator.get(prop, None) if existing_value is None or new_value != existing_value: self._create_or_update_indicator(indicator) return