Criar um analisador de ASIM

Concluído

Os usuários do ASIM (Modelo de Informações de Segurança Avançado) usam analisadores unificados em vez de nomes de tabela em suas consultas, para exibir dados em um formato normalizado e incluir todos os dados relevantes para o esquema na consulta. A unificação de analisadores, por sua vez, usa analisadores específicos da fonte para lidar com os detalhes específicos de cada fonte.

O Microsoft Sentinel fornece analisadores integrados e específicos para muitas fontes de dados. Talvez você queira modificar, ou desenvolver, esses analisadores específicos da fonte nas seguintes situações:

Quando o dispositivo fornece eventos que se ajustam a um esquema de ASIM, mas um analisador específico da origem para o dispositivo e o esquema relevante não estão disponíveis no Microsoft Sentinel.

Quando os analisadores específicos da fonte do ASIM estão disponíveis para seu dispositivo, mas seu dispositivo envia eventos em um método ou formato diferente do esperado pelos analisadores do ASIM. Por exemplo:

Seu dispositivo de origem pode ser configurado para enviar eventos de maneira não padrão.

Seu dispositivo pode ter uma versão diferente da que é compatível com o analisador ASIM.

Os eventos podem ser coletados, modificados e encaminhados por um sistema intermediário.

Processo de desenvolvimento de analisador personalizado

O fluxo de trabalho a seguir descreve as etapas de alto nível no desenvolvimento de um analisador personalizado do ASIM, específico da fonte:

  1. Coletar logs de exemplo.

  2. Identifique o esquema ou esquemas que os eventos enviados da fonte representam.

  3. Mapeie os campos de evento de origem para o esquema ou esquemas identificados.

  4. Desenvolva um ou mais analisadores do ASIM para sua fonte. Você precisará desenvolver um analisador de filtragem e um analisador sem parâmetros para cada esquema relevante para a fonte.

  5. Teste seu analisador.

  6. Implante os analisadores nos workspaces do Microsoft Sentinel.

  7. Atualize o analisador de unificação do ASIM relevante para referenciar o novo analisador personalizado.

  8. Talvez você também queira contribuir com seus analisadores para a distribuição primária do ASIM. Os analisadores colaboradores também podem ser disponibilizados em todos os espaço de trabalho como analisadores internos.

Coletar logs de exemplo

Para criar analisadores do ASIM eficazes, você precisa de um conjunto representativo de logs, o que, na maioria das vezes, exigirá a configuração do sistema de origem e a conexão com o Microsoft Sentinel. Se você não tiver o dispositivo de origem disponível, os serviços de pagamento em nuvem conforme o uso permitirão implantar muitos dispositivos para o desenvolvimento e teste.

Além disso, encontrar a documentação e os exemplos do fornecedor para os logs pode ajudar a acelerar o desenvolvimento e reduzir erros, garantindo ampla cobertura de formato de log.

Um conjunto representativo de logs deve incluir:

  • Eventos com resultados de eventos diferentes.
  • Eventos com diferentes ações de resposta.
  • Formatos diferentes para nome de usuário, nome de host e IDs e outros campos que exigem normalização de valor.

Mapeamento

Antes de desenvolver um analisador, mapeie as informações disponíveis no evento ou nos eventos de origem para o esquema que você identificou:

  • Mapeie todos os campos obrigatórios e, preferencialmente, também os campos recomendados.
  • Tente mapear todas as informações disponíveis da origem para campos normalizados. Se não estiver disponível como parte do esquema selecionado, considere o mapeamento para campos disponíveis em outros esquemas.
  • Mapeie valores para campos na origem para os valores normalizados permitidos pelo o ASIM. O valor original é armazenado em um campo separado, como EventOriginalResultDetails.

Desenvolvendo analisadores

Desenvolva uma filtragem e um analisador sem parâmetros para cada esquema relevante.

Um analisador personalizado é uma consulta KQL desenvolvida na página Logs do Microsoft Sentinel. A consulta do analisador tem três partes:

Filtrar > Analisar > Preparar campos

Filtragem de registros relevantes

Em muitos casos, uma tabela no Microsoft Sentinel inclui vários tipos de eventos. Por exemplo:

  • A tabela Syslog tem dados de várias fontes.
  • As tabelas personalizadas podem incluir informações de uma única fonte que fornece mais de um tipo de evento e podem se ajustar a vários esquemas.

Portanto, um analisador deve primeiro filtrar somente os registros relevantes para o esquema de destino.

A filtragem em KQL é feita usando o operador where. Por exemplo, o evento Sysmon 1 relata a criação do processo e, portanto, deve ser normalizado para o esquema ProcessEvent. O Evento Sysmon 1 faz parte da tabela de Eventos, logo o seguinte filtro seria usado:

Event | where Source == "Microsoft-Windows-Sysmon" and EventID == 1

Importante

Um analisador não deve filtrar por tempo. A consulta que usa o analisador aplicará um intervalo de tempo.

Filtragem por tipo de origem usando uma Watchlist

Em alguns casos, o próprio evento não contém informações que permitiriam a filtragem por tipos de origem específicos.

Por exemplo, os eventos DNS do Infoblox são enviados como mensagens do Syslog e são difíceis de distinguir das mensagens do Syslog enviadas de outras fontes. Nesses casos, o analisador depende de uma lista de origens que definem os eventos relevantes. Essa lista é mantida na watchlist ASimSourceType.

Para usar a watchlist ASimSourceType nos analisadores:

  • Inclua a seguinte linha no início do analisador:
let Sources_by_SourceType=(sourcetype:string){_GetWatchlist('ASimSourceType') | where SearchKey == tostring(sourcetype) | extend Source=column_ifexists('Source','') | where isnotempty(Source)| distinct Source };
  • Adicione um filtro que usa a watchlist na seção de filtragem do analisador. Por exemplo, o analisador DNS do Infoblox inclui o seguinte na seção de filtragem:
| where Computer in (Sources_by_SourceType('InfobloxNIOS'))

Para usar este exemplo no analisador:

  • Substitua Computador pelo nome do campo que inclui as informações de sua origem. Você pode mantê-lo como Computador para qualquer analisador baseado no Syslog.

  • Substitua o token InfobloxNIOS por um valor de sua escolha para o analisador. Informe os usuários do analisador que eles devem atualizar a watchlist ASimSourceType usando o valor selecionado, bem como a lista de origens que enviam eventos desse tipo.

Filtragem com base em parâmetros do analisador

Ao desenvolver analisadores de filtragem, o analisador deve aceitar os parâmetros de filtragem para o esquema relevante, conforme documentado no artigo de referência desse esquema. O uso de um analisador existente como ponto de partida garante que o analisador inclua a assinatura de função correta. Na maioria dos casos, o código de filtragem real também é semelhante para filtrar analisadores para o mesmo esquema.

Ao filtrar, lembre-se de:

  • Filtrar antes de analisar usando campos físicos. Se os resultados filtrados não forem precisos o suficiente, repita o teste após a análise para ajustar os resultados. Para obter mais informações, confira otimização de filtragem.
  • Não filtre se o parâmetro não estiver definido e ainda tiver o valor padrão.

Os exemplos a seguir mostram como implementar a filtragem em um parâmetro de cadeia de caracteres, em que o valor padrão é geralmente '*', e em um parâmetro de lista, em que o valor padrão é geralmente uma lista vazia.

srcipaddr=='*' or ClientIP==srcipaddr
array_length(domain_has_any) == 0 or Name has_any (domain_has_any)

Otimização da filtragem

Para garantir o desempenho do analisador, observe as seguintes recomendações de filtragem:

  • Sempre filtre por campos integrados em vez de analisados. Embora às vezes seja mais fácil filtrar usando campos analisados, isso afeta drasticamente o desempenho.
  • Use operadores que forneçam desempenho otimizado. Em particular, ==, has e startswith. O uso de operadores como contains ou matches regex também afeta bastante o desempenho.

Nem sempre é fácil seguir as recomendações de filtragem para melhorar o desempenho. Por exemplo, usar has é menos preciso do que contains. Em outros casos, corresponder o campo integrado, como SyslogMessage, é menos preciso do que comparar um campo extraído, como DvcAction. Nesses casos, recomendamos fazer uma filtragem prévia usando um operador de otimização de desempenho em um campo integrado e repetir o filtro usando condições mais precisas após a análise.

Para obter um exemplo, confira o snippet de analisador DNS Infoblox a seguir. Primeiro, o analisador verifica se o campo SyslogMessage tem a palavra client. No entanto, o termo pode ser usado em um local diferente na mensagem, portanto, depois de analisar o campo Log_Type, o analisador verifica novamente se a palavra client era realmente o valor do campo.

Syslog | where ProcessName == "named" and SyslogMessage has "client"
…
      | extend Log_Type = tostring(Parser[1]),
      | where Log_Type == "client"

Análise

Depois que a consulta seleciona os registros relevantes, talvez seja necessário analisá-los. Normalmente, a análise é necessária se vários campos de evento são transmitidos em um único campo de texto.

Os operadores KQL que executam a análise são listados abaixo, ordenados por otimização de desempenho. O primeiro fornece o desempenho mais otimizado, e o último fornece o desempenho menos otimizado.

Operador Descrição
split Analisar uma cadeia de caracteres de valores delimitados.
parse_csv Analisar uma cadeia de caracteres de valores formatados como uma linha CSV (valores separados por vírgula).
analisar Analisar vários valores de uma cadeia de caracteres arbitrária usando um padrão, que pode ser um padrão simplificado com melhor desempenho ou uma expressão regular.
extract_all Analisar valores individuais de uma cadeia de caracteres arbitrária usando uma expressão regular. extract_all terá um desempenho semelhante a parse se o último usar uma expressão regular.
extract Extrair um único valor de uma cadeia de caracteres arbitrária usando uma expressão regular. O uso de extract proporcionará um melhor desempenho do que parse ou extract_all se um só valor for necessário. No entanto, usar várias ativações de over na mesma cadeia de caracteres de origem é menos eficiente do que uma só parte ou extract_all. O ideal é evitar essa prática.
parse_json Analisar os valores de uma cadeia de caracteres formatada como JSON. Quando apenas alguns valores de JSON são necessários, usar parse, extract ou extract_all aprimora o desempenho.
parse_xml Analisar os valores em uma cadeia de caracteres formatada como XML. Quando apenas alguns valores de XML são necessários, usar parse, extract ou extract_all aprimora o desempenho.

Além da cadeia de caracteres de análise, a fase de análise pode exigir mais processamento dos valores originais, incluindo:

  • Formatação e conversão de tipo. O campo de origem, depois de extraído, pode precisar ser formatado para se ajustar ao campo de esquema de destino. Por exemplo, talvez seja necessário converter uma cadeia de caracteres que representa a data e a hora em um campo datetime. Funções como todatetime e tohex são úteis nesses casos.

  • Valor de pesquisa. O valor do campo de origem, depois de extraído, pode precisar ser associado ao conjunto de valores especificado para o campo de esquema de destino. Por exemplo, algumas fontes relatam códigos de resposta DNS numéricos, enquanto o esquema determina os códigos de resposta de texto mais comuns. As funções iff e case podem ser úteis para mapear alguns valores.

    Por exemplo, o analisador de DNS da Microsoft atribui o campo EventResult com base na ID do evento e no código de resposta usando uma instrução iff, da seguinte forma:

    extend EventResult = iff(EventId==257 and ResponseCode==0 ,'Success','Failure')
    

    Para vários valores, use datatable e lookup, conforme demonstrado no mesmo analisador de DNS:

    let RCodeTable = datatable(ResponseCode:int,ResponseCodeName:string) [ 0, 'NOERROR', 1, 'FORMERR'....];
    ...
     | lookup RCodeTable on ResponseCode
     | extend EventResultDetails = case (
     isnotempty(ResponseCodeName), ResponseCodeName,
     ResponseCode between (3841 .. 4095), 'Reserved for Private Use',
     'Unassigned')
    

Valores de mapeamento

Em muitos casos, o valor original extraído precisa ser normalizado. Por exemplo, no ASIM, um endereço MAC usa dois-pontos como separador, enquanto a origem pode enviar um endereço MAC delimitado por hífen. O operador principal para transformar valores é extend, ao lado de um amplo conjunto de funções de cadeia de caracteres, numéricas e de data de KQL, conforme demonstrado na seção de Análise acima.

Use as instruções case, iff e lookup quando houver a necessidade de mapear um conjunto de valores para os valores permitidos pelo campo de destino.

Quando cada valor de origem for mapeado para um valor de destino, defina o mapeamento usando o operador datatable e lookup para mapear. Por exemplo

let NetworkProtocolLookup = datatable(Proto:real, NetworkProtocol:string)[
        6, 'TCP',
        17, 'UDP'
   ];
    let DnsResponseCodeLookup=datatable(DnsResponseCode:int,DnsResponseCodeName:string)[
      0,'NOERROR',
      1,'FORMERR',
      2,'SERVFAIL',
      3,'NXDOMAIN',
      ...
   ];
   ...
   | lookup DnsResponseCodeLookup on DnsResponseCode
   | lookup NetworkProtocolLookup on Proto

Observe que a pesquisa é útil e eficiente também quando o mapeamento tem apenas dois valores possíveis.

Quando as condições de mapeamento forem mais complexas, use as funções iff ou case. A função iff habilita o mapeamento de dois valores:

| extend EventResult = 
      iff(EventId==257 and ResponseCode==0,'Success','Failure’)

A função case dá suporte a mais de dois valores de destino. O exemplo abaixo mostra como combinar lookup e case. O exemplo de lookup acima retornará um valor vazio no campo DnsResponseCodeName se o valor da pesquisa não for encontrado. O exemplo de case a seguir o aumenta usando o resultado da operação lookup, se disponível, e especificando outras condições caso contrário.

| extend DnsResponseCodeName = 
      case (
        DnsResponseCodeName != "", DnsResponseCodeName,
        DnsResponseCode between (3841 .. 4095), 'Reserved for Private Use',
        'Unassigned'
      )

Preparar campos no conjunto de resultados

O analisador deve preparar os campos no conjunto de resultados para garantir que os campos normalizados sejam usados.

Os seguintes operadores KQL são usados para preparar campos em seu conjunto de resultados:

Operador Descrição Quando usar em um analisador
project-rename Renomeia campos. Se houver um campo no evento real que precisar somente ser renomeado, use project-rename. O campo renomeado ainda se comporta como um campo integrado, e as operações do campo têm um desempenho muito melhor.
project-away Remove campos. Use project-away para campos específicos que você deseja remover do conjunto de resultados. É recomendável não remover os campos originais que não são normalizados do conjunto de resultados, a menos que eles criem confusão ou sejam muito grandes e possam ter implicações de desempenho.
project Seleciona os campos que existiam antes ou que foram criados como parte da instrução e remove todos os outros campos. Não recomendado para uso em um analisador, porque ele não removerá os outros campo que não estiverem normalizados. Se você precisar remover campos específicos, como valores temporários usados durante a análise, use project-away para removê-los dos resultados.
extend Adicionar aliases. Além de sua função na geração de campos calculados, o operador extend também é usado para criar aliases.

Tratar variantes de análise

Em muitos casos, os eventos de um fluxo de eventos incluem variantes que exigem lógica de análise diferente. Para analisar diferentes variantes em um único analisador, use instruções condicionais como iff e case ou use uma estrutura de união.

Para usar union para lidar com várias variantes, crie uma função separada para cada variante e use a instrução união para combinar os resultados:

let AzureFirewallNetworkRuleLogs = AzureDiagnostics
    | where Category == "AzureFirewallNetworkRule"
    | where isnotempty(msg_s);
let parseLogs = AzureFirewallNetworkRuleLogs
    | where msg_s has_any("TCP", "UDP")
    | parse-where
        msg_s with           networkProtocol:string 
        " request from "     srcIpAddr:string
        ":"                  srcPortNumber:int
    …
    | project-away msg_s;
let parseLogsWithUrls = AzureFirewallNetworkRuleLogs
    | where msg_s has_all ("Url:","ThreatIntel:")
    | parse-where
        msg_s with           networkProtocol:string 
        " request from "     srcIpAddr:string
        " to "               dstIpAddr:string
    …
union parseLogs,  parseLogsWithUrls…

Para evitar eventos duplicados e processamento excessivo, verifique se cada função começa pela filtragem, usando campos nativos, apenas os eventos que se destina a analisar. Além disso, se necessário, use o project-away em cada filial, antes da união.

Implantar analisadores

Implante analisadores manualmente copiando-os na página de Log do Azure Monitor. Depois, salve a consulta como uma função. Este método é útil para testes. Para obter mais informações, confira Criar uma função.

Para implantar um grande número de analisadores, é recomendável usar modelos ARM do analisador, da seguinte forma:

  1. Crie um arquivo YAML com base no modelo relevante para cada esquema e inclua sua consulta nele. Comece com o modelo YAML relevante para o tipo de esquema e analisador, filtragem ou sem parâmetro.

  2. Use o conversor de ASIM YAML para modelo ARM para converter o arquivo YAML em um modelo ARM.

  3. Se estiver implantando uma atualização, exclua as versões mais antigas das funções usando o portal ou a ferramenta de exclusão de função do PowerShell.

  4. Implante seu modelo usando o portal do Azure ou o PowerShell.

Você também pode combinar vários modelos em um único processo de implantação usando modelos vinculados.