SIGMA Rules Pipelines - Parte 02

Por Davi Chaves

Table of Contents

  1. SIGMA Rules - Parte 02 - Pipelines
  2. Objetivo
    1. Exemplo Thomas Patzke
  3. The Field Naming Problem
  4. Pipelines, what are they?
  5. Specification of Pipelines
  6. Specification of Transformations 1. field_name_mapping 1. add_condition
  7. Specification of Conditions
    1. RuleProcessingCondition
      1. Exemplo: LogsourceCondition
    2. DetectionItemProcessingCondition
    3. FieldNameProcessingCondition
      1. Exemplo: ExcludeFieldCondition
  8. Mapping Process Creation Rules
    1. Forçar um mapeamento

O principal objetivo das SIGMAS Rules é descrever assinaturas de logs de maneira genérica, independente de fornecedor e ambiente. Fontes genéricas de logs (logsources) são uma camada adicional de abstração entre a lógica de detecção e a fonte de logs, permitindo que o usuário de uma regra utilize o mesmo conjunto de regras de criação de processos para eventos do Sysmon, assim como para eventos gerados por uma solução de Detecção e Resposta de Endpoint (EDR).

No entanto, as regras Sigma precisam, em última instância, se transformar em consultas específicas para o SIEM, EDR ou qualquer outra ferramenta utilizada para esse fim, levando em consideração suas convenções exclusivas de nomenclatura de campos e outros requisitos específicos. A abordagem do pySigma (e, portanto, também do Sigma CLI) para fazer isso são os pipelines de processamento. Muitos backends já incluem um ou vários pipelines, sendo que alguns são aplicados automaticamente pelo backend, como é o caso dos backends do QRadar e InsightIDR, devido a esses sistemas possuírem uma taxonomia fixa. Outros backends, como Elasticsearch e Splunk, exigem o uso explícito de pipelines de processamento, pois há muitas maneiras de inserir dados nessas plataformas.

Objetivo

Voltanto para nosso exemplo do artigo anterior, temos uma SIGMA rule que, se não for aplicada nenhuma pipeline, é gerado uma query com fields genéricos, que podem, ou não, serém os mesmos fields que o SIEM da sua companhia.

logsource:
  product: windows
detection:
  selection:
    Image|endswith: '\certutil.exe'
    CommandLine|contains:
      - '-encode'
      - '/encode'
  condition: selection

										 |
										 | Sem pipeline
										 V

Image="*\\certutil.exe" CommandLine IN ("*-encode*", "*/encode*")

										 |
										 | Com uma pipeline
										 V

index="company-windows" EventCode=4688 NewProcessName="*\\certutil.exe" CommandLine IN ("*-encode*", "*/encode*")

No exemplo acima, poderiamos utilizar uma pipeline para mapear o campo genérico Image para NewProcessName. Além disso, baseado no logsource (product: windows) podemos adicionar o campo index="company-windows" para focar a busca em apenas logs do ambiente Windows. Note que, caso sua empresa tivesse, por exemplo, vários indexes de logs windows, como “company-windows, windows-logs, windows-something”, você poderia utilizar a pipeline da mesma forma para adicionar todos esses indexes na query final.

Exemplo Thomas Patzke

Abaixo, um exemplo retirado do blog do Thomas Patzke: Na priveira conversão, não é utilizado nenhuma pipeline. Por conta disso, veja que o resultado contem campos bem genéricos. Na segunda conversão, a pipeline splunk é utilizada. Essa pipeline é bem genérica, ela apenas vai mapear os logsources da regras SIGMA em EventIds do Sysmon. No caso dessa regra em específico, se trata da categoria de “criação de processos”. Portanto, essa pipeline genérica apenas fez o mapeamento windows & process_creation --> EventID=1. Na terceira conversão, temos uma pipeline específica do ambiente SIEM dele. Ou seja, no ambiente splunk dele, os logs Windows estão armazenados no index="windows", o field Image se chama win.Image e, ao invés do field padrão EventID, foi utilizado dois fields com sintaxe ligeiramente diferente: event_id e evtid. O importante é entender que pipelines, além de fazerem mapeamentos, são como um funil: vão transformar regras genéricas em coisas mais específicas. Nesse artigo, vamos primeiramente nos focar em regras mais específicas, como a terceira conversão do Patzke.

The Field Naming Problem

No pior dos casos, caso cada regra SIGMA utilizasse campos totalmente diferentes uns dos outros, precisariamos de uma pipeline para cada regra! Isso tiraria um grande poder das SIGMA rules.

Entretanto, como os autores das SIGMAs rules seguem uma certa convensão de campos, uma única pipeline pode, muitas vezes, ser aplicada para várias regras. Como exemplo prático, o repositório oficial de SIGMA rules possui milhares de regras da categoria process_creation. Dessa forma, com uma única pipeline, poderíamos utilizar todas essas regras.

Pipelines, what are they?

Um pipeline de processamento define uma sequência de transformações aplicadas a uma regra Sigma antes de convertê-la na linguagem de consulta alvo. Essas transformações incluem mapeamentos de campos, adição de sufixos aos nomes dos campos e muito mais.

Os pipelines podem ser representados tanto como arquivos YAML, destinados a usuários finais criando pipelines específicos para o ambiente das regras Sigma, quanto como código Python. Este último é o método preferido para implementar pipelines específicos para o backend e oferece algumas vantagens. Nesse artigo, vamos nós focar em pipelines no formato YAML.

Os itens de processamento dentro de um pipeline são executados na ordem definida pela prioridade. Uma cadeia típica de processamento envolve:

  1. Traduzir fontes genéricas de logs em fontes específicas, como converter regras Sigma de criação de processos em Sysmon EventID 1.
  2. Transformar assinaturas de logs na taxonomia usada pelo backend.
  3. Aplicar transformações específicas do ambiente.

Os pipelines podem ser encadeados com base em sua prioridade, criando um fluxo contínuo de transformações para as regras Sigma.

Specification of Pipelines

Uma pipeline, também chamada de ProcessingPipeline, pode conter uma variedade de campos responsáveis por fazerem diversos processamentos nas regras SIGMA. Abaixo, um exemplo de pipeline contendo alguns desses campos. Apesar disso, nesse artigo iremos apenas tratar das “transformations”.

# Exemplo de pipeline retirado do repositório pySigma
name: Test
priority: 10
allowed_backends:
  - test-a
  - test-b
transformations: # Vamos abordar apenas esse campo
  - id: test
    field_name_mapping:
      EventID: EventCode
    rule_conditions:
      - type: "true"
      dummy: test-true
      - type: "false"
      dummy: test-false
    rule_cond_op: or
    detection_item_conditions:
      - type: "true"
      dummy: test-true
      - type: "false"
      dummy: test-false
    detection_item_cond_op: or
postprocessing:
  - id: test
    type: embed
    prefix: "[ "
    suffix: " ]"
    rule_conditions:
      - type: "true"
      dummy: test-true
      - type: "false"
      dummy: test-false
    rule_cond_op: or
finalizers:
  - type: concat
    prefix: "('"
    separator: "', '"
    suffix: "')"
vars:
  test_string: abc
  test_number: 123

Specification of Transformations

O campo “transformations” de pipelines no formato YAML contém uma ou mais ProcessingItem.
Um ProcessingItem é composto por uma ProcessingCondition^[ProcessingCondition não tem relação com o campo “condition” de uma regra SIGMA] opcional e uma Transformation^[Perceba que o campo “transformations” de pipelines no formato YAML não contem uma lista de Transformation, mas sim uma lista de ProcessingItem] que é aplicada no caso de a condição ser avaliada como verdadeira em relação à regra SIGMA fornecida ou se a condição não estiver presente. Como exemplo, antes mesmo de entendermos o que cada campo significa, abaixo está uma pipeline contendo três ProcessingItem, com cada um contendo uma Transformation, e duas ProcessingCondition.

name: My cool pipeline
transformations:
# ProcessingItem 1
- id: cool_id_1
  # Transformation 1
  type: field_name_mapping
  mapping:
    EventID:
    - event_id
# ProcessingItem 2
- id: cool_id_2
  # Transformation 2
  type: field_name_prefix
  prefix: "win."
  # ProcessingCondition 2
  field_name_conditions:
  - type: include_fields
    fields:
      - Image  
# ProcessingItem 3
- id: cool_id_3
  # Transformation 3
  type: add_condition
  conditions:
    index: windows
  # ProcessingCondition 3
  rule_conditions:
  - type: logsource
    product: windows

# Note: no "ProcessingItem 3", apesar dos campos "conditions:" e "rule_conditions" possuirem o mesmo termo "conditions", eles são coisas completamente diferentes. O primeiro é um campo do "Transformation 3", já o segundo é o campo "rule_conditions" do "ProcessingItem 3".

Abaixo, uma tabela retirado da documentação oficial contendo todos os tipos de transformações atualmente suportadas.

Identifier Parameters Meaning
field_name_mapping mapping Map a field name to one or multiple different.
field_name_prefix_mapping mapping Map a field name prefix to one or multiple different prefixes.
field_name_suffix suffix Add field name suffix.
field_name_prefix prefix Add field name prefix.
drop_detection_item Deletes detection items. This should only used in combination with a detection item condition.
wildcard_placeholders include, exclude Replaces placeholders with wildcards. This transformation is useful if remaining placeholders should be replaced with something meaningful to make conversion of rules possible without defining the placeholders content.
value_placeholders include, exclude Replaces placeholders with values contained in variables defined in the configuration.
query_expression_placeholders include, exclude, expression, mapping Replaces a placeholder with a plain query containing the placeholder or an identifier mapped from the placeholder name. The main purpose is the generation of arbitrary list lookup expressions which are passed to the resulting query.
add_condition conditions, name, template Add a condition expression to rule conditions.
change_logsource category, product, service Replace log source as defined in transformation parameters.
replace_string regex, replacement Replace string part matched by regular expresssion with replacement string that can reference capture groups. It operates on the plain string representation of the SigmaString value.
map_string mapping Map static string value to one or multiple other strings.
set_state key, val Set pipeline state key to value.
rule_failure message Raise a SigmaTransformationError with the provided message. This enables transformation pipelines to signalize that a certain situation can’t be handled, e.g. only a subset of values is allowed because the target data model doesn’t offers all possibilities.
detection_item_failure message Raise a SigmaTransformationError with the provided message. This enables transformation pipelines to signalize that a certain situation can’t be handled, e.g. only a subset of values is allowed because the target data model doesn’t offers all possibilities.
Por enquanto, vamos deixar o tema de ProcessingCondition de lado para entendermos as Transformation. Como mostrado acima, existem muitas transformações possíveis. Diante disso, não irei explicar cada uma delas, irei apenas dar uma intuição sobre o porquê de usar transformações

field_name_mapping

Suponha que em nossos logs de criação de processo o campo contendo o nome do processo não fosso Image. Dessa forma, fazer um simples mapeamento do campo Image para NewProcessName, por exemplo, já seria o suficiente. Estamos assumindo, por simplicidade, que essa pipeline só seria aplicada por nós em logs de criação de processos, pois, caso ela fosse aplicada em qualquer outro log que também contesse o campo Image, o mapeamento também seria feito. Veremos mais a frente como utilizar “condições” para garantir que essa transformação só seja aplicada em casos específicos.

transformations:
# ProcessingItem
- id: some_cool_id
  # Transformation
  type: field_name_mapping
  mapping:
    Image: NewProcessName

add_condition

Suponha, agora, que queiramos procurar por logs apénas com o index: company-windows e com o EventCode: 4688. Para isso, vamos utilizar a Transformation “add_condition” para adicionar um novo campo.

transformations:
  ...
# ProcessingItem
- id: some_cool_id2
  # Transformation
  type: add_condition
  conditions:
    index: company-windows
    EventCode: 4688

É importante notar que, adicionar os campos index: company-windows e EventCode: 4688 para todos os logs, provavelmente, não é uma boa ideia. O ideal seria adicionar esse campo apenas quando a regra SIGMA conter o logsource index: windows, category: processes_creation e service: windows_audit. Veremos como alcançar isso usando as ProcessingCondition mais a diante.

Specification of Conditions

As ProcessingCondition de um ProcessingItem servem apenas para uma coisa: dizer se a Transformation será aplicada ou não. Além disso, é importante frisar que qualquer ProcessingCondition pode ser utilizada para qualquer ProcessingItem. Diante disso, é obvio a importância de condições, pois vão surgir situações nas quais queremos aplicar certas trasnformações apenas sob certas condições.

Existem três grupos de ProcessingCondition:

  1. RuleProcessingCondition
  2. DetectionItemProcessingCondition
  3. FieldNameProcessingCondition Todas as três são ProcessingCondition, mas possuem algumas pequenas diferenças. É importante que fique avisado que eu não compreendi totalmente logo de início o motivo dessa divisão, então não fique surpreso se você também achar um pouco desnecessário esses três agrupamentos.

RuleProcessingCondition

Atualmente, existem cinco condições nesse grupo, estas são avaliadas para a regra SIGMA como um todo.

Type Parameters Meaning
logsource category, product, service Matches log source on rule. Not specified log source fields are ignored.
contains_detection_item field, value Returns True if rule contains a detection item that matches the given field name and value.
processing_item_applied processing_item_id Checks if processing item was applied to rule.
is_sigma_rule Checks if rule is a SigmaRule.
processing_item_applied Checks if rule is a SigmaCorrelationRule.

Exemplo: LogsourceCondition

Como explicado no exemplo da transformação “add_condition”, só faz sentido adicionar o index: company-windows se a regra SIGMA conter no logsource product: windows. Portanto, podemos utilizar a LogsourceCondition para aplicar essa transformação apenas nesse caso específico.

name: My cool pipeline
transformations:
# ProcessingItem
- id: cool_id
  # Transformation
  type: add_condition
  conditions:
    index: company-windows
  rule_conditions:
  # RuleProcessingCondition (LogsourceCondition)
  - type: logsource
    product: windows

DetectionItemProcessingCondition

Atualmente, existem duas condições nesse grupo, estas são avaliadas para a regra SIGMA como um todo, estas são avaliadas para cada item de detecção de uma regra SIGMA.

Type Parameters Meaning
match_string cond, pattern Match string values with a regular expression ‘pattern’. The parameter ‘cond’ determines for detection items with multiple values if any or all strings must match.
processing_item_applied processing_item_id Checks if processing item was applied to detection item.

FieldNameProcessingCondition

Atualmente, existem três condições nesse grupo, estas são avaliadas para nomes de campos em itens de detecção, campos da regra SIGMA ou em outros casos que é requerido fazer um “matching” em nomes de campos sem um contexto de um item de detecção.

Type Parameters Meaning
include_fields fields, type Matches on field name if it is contained in fields list.
exclude_fields fields, type Matches on field name if it is not contained in fields list.
processing_item_applied processing_item_id Checks if processing item was applied to a field name.

Exemplo: ExcludeFieldCondition

Suponha que todos os campos dos logs que você deseja rodar as regras SIGMA possuem o prefíxo “Process.”, com a excessão do campo Image. Nesse caso, podemos utilizar essa condição para excluir os campos desejados e fazer com que a transformação seja aplicada apenas nos campos corretos.

name: My cool pipeline
transformations:
# ProcessingItem
- id: cool_id
  # Transformation
  type: field_name_prefix
  prefix: "Process."
  field_name_conditions:
  # FieldNameProcessingCondition (ExcludeFieldCondition)
  - type: exclude_fields
    fields:
      - Image