CreateSigmaRuleIndicator
python · Sigma
Source
import re import zipfile from sigma.rule import SigmaRule from sigma.exceptions import SigmaError from sigma.modifiers import reverse_modifier_mapping import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 MITRE_TECHNIQUE_CACHE: dict[str, str] = {} def get_mitre_technique_name(mitre_id: str, indicator_type: str) -> str: """ Searches XSOAR TIM for an 'Attack Pattern' indicator matching the given MITRE ID and returns the indicator value. Args: mitre_id (str): The MITRE ATT&CK ID to search for (e.g., T1562). indicator_type (str): The XSOAR indicator type (e.g. Attack Pattern) Returns: str: The indicator value if found, else an empty string. """ if mitre_id in MITRE_TECHNIQUE_CACHE: return MITRE_TECHNIQUE_CACHE[mitre_id] try: query = f'type:"{indicator_type}" and {mitre_id}' demisto.debug(f"Querying for {query} in TIM") success, response = execute_command(command="SearchIndicator", args={"query": query}, fail_on_error=False) if not success: demisto.debug(f"Failed to execute findIndicators command: {get_error(response)}") return "" if response: indicator = response[0].get("value", "") MITRE_TECHNIQUE_CACHE[mitre_id] = indicator demisto.debug(f"Found attack-pattern - {indicator}") else: demisto.debug(f"Could not find the attack-pattern - {mitre_id}") indicator = "" return indicator except Exception as e: demisto.debug(f"Error searching for Attack Pattern indicator: {e!s}") return "" def create_indicator_relationships(indicator: str, product: str, relationships: list[dict[str, str]]) -> list[EntityRelationship]: """ Create relationships between the Sigma rule indicator and its Product, CVEs and MITRE techniques Args: indicator (str): The value of the Sigma rule indicator. product (str): The name of the product the rule relates to. relationships (list of dicts): All values that are about to be related to the Sigma rule. """ final_relationships = [] if product: demisto.debug(f"Creating a relationship to {product}") final_relationships.append(create_relationship(indicator, product.capitalize(), "Software", relation_type="related-to")) for relationship in relationships: demisto.debug(f"Creating a new relationship to {relationship['value']} ({relationship['type']})") if relationship["type"] in ("Attack Pattern", "CVE", "Tool"): final_relationships.append( create_relationship(indicator, relationship["value"], relationship["type"], relation_type="detects") ) return final_relationships def create_relationship(indicator_value: str, entity_b: str, entity_b_type: str, relation_type: str) -> EntityRelationship: """ Creates a relationship in XSOAR between the Sigma rule indicator and the product. Args: indicator_value (str): The value of the Sigma rule indicator. entity_b (str): the value of entity b for the relationship. entity_b_type (str): entity b indicator type. relation_type (str): the type of the relationship (for example - "related-to"). """ try: relationship = EntityRelationship( entity_a=indicator_value, entity_a_type="Sigma Rule Indicator", name=relation_type, entity_b=entity_b, entity_b_type=entity_b_type, ) except Exception as e: demisto.debug(f"Failed to create relationship: {e!s}") return relationship def parse_detection_field(rule: SigmaRule) -> list: """ Parses the detection field from the Sigma rule and maps it into a grid field. Args: rule (SigmaRule): The parsed Sigma rule. Returns: list: A list of dictionaries representing the detection grid. """ grid = [] row = {} def build_row(selection, data): row["selection"] = selection row["key"] = data.field or "" row["modifiers"] = ",".join([reverse_modifier_mapping[modifier.__name__] for modifier in data.modifiers]) row["values"] = "\n".join([f"({index}){value.to_plain()}" for index, value in enumerate(data.original_value, 1)]) return row for selection, value in rule.detection.detections.items(): for fields in value.detection_items: try: # Check if fields has detection_items attribute (SigmaDetection case) if hasattr(fields, "detection_items"): for field in fields.detection_items: row = build_row(selection, field) grid.append(row) row = {} else: # Handle SigmaDetectionItem case row = build_row(selection, fields) grid.append(row) row = {} except AttributeError: row = build_row(selection, fields) grid.append(row) row = {} return grid def parse_tags(tags: list) -> tuple[list[dict[str, str]], list[str], str]: relationships = [] processed_tags = [] tlp = "CLEAR" for tag in tags.copy(): if tag.namespace == "attack" and re.match(r"[ts]\d{4}", tag.name): if tag.name.lower().startswith("t"): indicator_type = "Attack Pattern" mitre_name = get_mitre_technique_name(tag.name, indicator_type) else: indicator_type = "Tool" mitre_name = get_mitre_technique_name(tag.name, indicator_type) if mitre_name: relationships.append({"value": mitre_name, "type": indicator_type}) processed_tags.append(f"{tag.name.upper()} - {mitre_name}") else: processed_tags.append(f"{tag.name.upper()}") elif tag.namespace == "attack": processed_tags.append(tag.name.replace("_", " ").replace("-", " ").title()) elif tag.namespace == "cve": demisto.debug(f"Found a CVE tag - {tag}") cve = tag.name.replace(".", "-").upper() if not cve.startswith("CVE-"): cve = f"CVE-{cve}" relationships.append({"value": cve, "type": "CVE"}) processed_tags.append(cve) elif tag.namespace == "tlp": tlp = tag.name.upper() return relationships, processed_tags, tlp def parse_and_create_indicator(rule: SigmaRule, raw_rule: str) -> dict[str, Any]: """ Parses the Sigma rule dictionary and creates an indicator in XSOAR. Args: rule (SigmaRule): The Sigma rule parsed. raw_rule (str): The rule in its raw YAML form. """ # Create fields mapping indicator = { "type": "Sigma Rule", "value": rule.title, "creationdate": f"{rule.date}", "sigmaruleid": f"{rule.id}", "sigmarulestatus": rule.status.name if rule.status else None, "author": rule.author, "sigmarulelevel": rule.level.name if rule.level else None, "description": rule.description, "category": rule.logsource.category, "product": rule.logsource.product, "service": rule.logsource.service, "sigmaruleraw": raw_rule, "sigmacondition": [{"condition": condition} for condition in rule.detection.condition], "sigmadetection": parse_detection_field(rule), "sigmafalsepositives": [{"reason": fp} for fp in rule.falsepositives], "publications": [ {"link": ref, "source": "Sigma Rule", "title": rule.title, "date": f"{rule.date}"} for ref in rule.references ], } if hasattr(rule.logsource, "custom_attributes") and rule.logsource.custom_attributes: indicator["definition"] = rule.logsource.custom_attributes.get("definition") if rule.custom_attributes: indicator["sigmarulelicense"] = rule.custom_attributes.get("license", None) relationships, tags, tlp = parse_tags(rule.tags) indicator["tags"] = tags indicator["tlp"] = tlp level = indicator.get("sigmarulelevel") if level and isinstance(level, str) and level.lower() in ("high", "critical"): indicator["verdict"] = "Malicious" indicator = {key: value for key, value in indicator.items() if value is not None} return {"indicator": indicator, "relationships": relationships} def extract_rules_from_zip(file_path: str) -> list[dict[str, Any]]: indicators = [] # Extract zip file to the temp directory with zipfile.ZipFile(file_path, "r") as zip_ref: start = time.time() demisto.debug(f"SGM: Attempting to unzip {file_path} and extract files") file_list = [f for f in zip_ref.namelist() if f.endswith(".yml") and not f.startswith(("__", "."))] total_files = len(file_list) for file_name in file_list: with zip_ref.open(file_name) as file: file_contents = file.read().decode("utf-8") try: rule = SigmaRule.from_yaml(file_contents) indicator_data = parse_and_create_indicator(rule, file_contents) indicators.append(indicator_data) except Exception as e: demisto.error(f'SGM: Error parsing Sigma rule from file "{file_name}": {str(e)}') continue demisto.debug(f"Extraction took {time.time() - start:.2f} seconds for {total_files} files") return indicators def tim_create_indicators(indicators: list[dict[str, Any]]) -> CommandResults: """Creates indicators in Cortex Threat Intelligence Management (TIM) module. This function takes a list of indicator dictionaries and creates them in XSOAR. It also creates relationships between the indicators and other entities as specified in the indicators data. Args: indicators (list[dict[str, Any]]): List of indicator dictionaries, each containing an "indicator" key with the indicator data and a "relationships" key with relationship data. Returns: CommandResults: Command results containing a readable output with the number of indicators and relationships created, and the relationships data. """ start = time.time() relationships = [] for indicator in indicators: xsoar_indicator = indicator["indicator"] execute_command("createNewIndicator", xsoar_indicator) relationships += create_indicator_relationships( xsoar_indicator["value"], xsoar_indicator.get("product", ""), indicator["relationships"] ) demisto.debug(f"{len(indicators)} indicators created. in {time.time() - start} seconds") md = f"{str(len(indicators))} Sigma Rule(s) Created.\n" md += f"{str(len(relationships))} Relationship(s) Created." return CommandResults(readable_output=md, relationships=relationships) def main() -> None: """ Main function that handles the Sigma rule import process and creates indicators and relationships in XSOAR. """ indicators = [] try: # Get the arguments args = demisto.args() sigma_rule_str = args.get("sigma_rule_str", "") sigma_rule_entry_id = args.get("entry_id", "") create_indicators = argToBoolean(args.get("create_indicators", "True")) # Check if both arguments are empty if not sigma_rule_str and not sigma_rule_entry_id: return_error("Either 'sigma_rule_str' or 'entry_id' must be provided.") if sigma_rule_str: sigma_rule = SigmaRule.from_yaml(sigma_rule_str) indicators.append(parse_and_create_indicator(sigma_rule, sigma_rule_str)) elif sigma_rule_entry_id: # Get the file contents using entry_id res = demisto.getFilePath(sigma_rule_entry_id) if not res: return_error(f"File entry {sigma_rule_entry_id} not found") file_path = res["path"] if res.get("name", "").endswith("zip"): indicators = extract_rules_from_zip(file_path) else: with open(file_path) as file: sigma_rule_str = file.read() # Parse the sigma rule sigma_rule = SigmaRule.from_yaml(sigma_rule_str) indicators.append(parse_and_create_indicator(sigma_rule, sigma_rule_str)) if create_indicators: return_results(tim_create_indicators(indicators)) else: for indicator in indicators: return_results(f'{indicator["indicator"]}') except SigmaError as e: return_error(f"SigmaError. Failed to parse Sigma rule: {str(e)}") except Exception as e: return_error(f"Exception. Failed to import Sigma rule: {str(e)}") if __name__ in ("__main__", "__builtin__", "builtins"): # pragma: no cover main()
README
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
Inputs
| Argument Name | Description |
|---|---|
| sigma_rule_str | A string containing the Sigma rule in its original format. |
| entry_id | The entry ID of the file that contains the Sigma rule. The script will read the Sigma rule from this file if provided. |
Outputs
There are no outputs for this script.