Melhores práticas de consulta de investigação avançada

Aplica-se a:

  • Microsoft Defender XDR

Aplique estas recomendações para obter resultados mais rapidamente e evitar tempos limite durante a execução de consultas complexas. Para obter mais orientações sobre como melhorar o desempenho das consultas, leia As melhores práticas de consulta do Kusto.

Compreender as quotas de recursos da CPU

Consoante o tamanho, cada inquilino tem acesso a uma quantidade definida de recursos da CPU alocados para executar consultas de investigação avançadas. Para obter informações detalhadas sobre vários parâmetros de utilização, leia sobre quotas de investigação avançadas e parâmetros de utilização.

Depois de executar a consulta, pode ver o tempo de execução e a respetiva utilização de recursos (Baixa, Média, Alta). Alto indica que a consulta precisou de mais recursos para ser executada e poderia ser melhorada para devolver resultados de forma mais eficiente.

Os detalhes da consulta no separador **Resultados** no portal do Microsoft Defender

Os clientes que executam várias consultas regularmente devem controlar o consumo e aplicar as orientações de otimização neste artigo para minimizar a interrupção resultante da exceção de quotas ou parâmetros de utilização.

Veja Otimizar consultas KQL para ver algumas das formas mais comuns de melhorar as consultas.

Sugestões de otimização geral

  • Dimensionar novas consultas — se suspeitar que uma consulta irá devolver um conjunto de resultados grande, avalie-o primeiro com o operador de contagem. Utilize o limite ou o sinónimo take para evitar grandes conjuntos de resultados.

  • Aplicar filtros antecipadamente — Aplique filtros de tempo e outros filtros para reduzir o conjunto de dados, especialmente antes de utilizar funções de transformação e análise, como subcadeia(), substituir(), cortar(), toupper()ou parse_json(). No exemplo abaixo, a função de análise extractjson() é utilizada depois de os operadores de filtragem terem reduzido o número de registos.

    DeviceEvents
    | where Timestamp > ago(1d)
    | where ActionType == "UsbDriveMount"
    | where DeviceName == "user-desktop.domain.com"
    | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
    
  • Tem batidas: para evitar procurar subcadeias dentro de palavras desnecessariamente, utilize o has operador em vez de contains. Saiba mais sobre os operadores de cadeias de carateres

  • Procurar em colunas específicas — Procure numa coluna específica em vez de executar pesquisas de texto completo em todas as colunas. Não utilize * para verificar todas as colunas.

  • Sensível às maiúsculas e minúsculas para a velocidade — as pesquisas sensíveis a maiúsculas e minúsculas são mais específicas e, geralmente, mais eficazes. Os nomes dos operadores de cadeias sensíveis a maiúsculas e minúsculas, tais como has_cs e contains_cs, geralmente terminam com _cs. Também pode utilizar o operador == de igual sensível às maiúsculas e minúsculas em vez de =~.

  • Analisar, não extrair — Sempre que possível, utilize o operador de análise ou uma função de análise como parse_json(). Evite o matches regex operador de cadeia ou a função extract(), ambas com expressão regular. Reserve a utilização da expressão regular para cenários mais complexos. Leia mais sobre as funções de análise

  • Filtrar tabelas e não expressões — não filtre numa coluna calculada se conseguir filtrar numa coluna de tabela.

  • Sem termos de três carateres — evite comparar ou filtrar com termos com três ou menos carateres. Estes termos não estão indexados e a respetiva correspondência exigirá mais recursos.

  • Projetar seletivamente – facilite a compreensão dos resultados ao projetar apenas as colunas de que precisa. Projetar colunas específicas antes de executar a associação ou operações semelhantes também ajuda a melhorar o desempenho.

Otimizar o join operador

O operador de associação intercala linhas de duas tabelas ao corresponder valores em colunas especificadas. Aplique estas sugestões para otimizar as consultas que utilizam este operador.

  • Tabela mais pequena à esquerda — o join operador corresponde aos registos na tabela no lado esquerdo da instrução de associação aos registos à direita. Ao ter a tabela mais pequena à esquerda, terá de corresponder menos registos, acelerando assim a consulta.

    Na tabela abaixo, reduzimos a tabela DeviceLogonEvents esquerda para abranger apenas três dispositivos específicos antes de os associar IdentityLogonEvents por SIDs de conta.

    DeviceLogonEvents
    | where DeviceName in ("device-1.domain.com", "device-2.domain.com", "device-3.domain.com")
    | where ActionType == "LogonFailed"
    | join
        (IdentityLogonEvents
        | where ActionType == "LogonFailed"
        | where Protocol == "Kerberos")
    on AccountSid
    
  • Utilize o sabor de associação interna — o sabor de associação predefinido ou a associação innerunique-join elimina duplicados linhas na tabela esquerda pela tecla de associação antes de devolver uma linha para cada correspondência à tabela direita. Se a tabela à esquerda tiver múltiplas linhas com o mesmo valor para a join chave, essas linhas serão duplicadas para deixar uma única linha aleatória para cada valor exclusivo.

    Este comportamento predefinido pode deixar de fora informações importantes da tabela esquerda que podem fornecer informações úteis. Por exemplo, a consulta abaixo mostrará apenas um e-mail com um anexo específico, mesmo que esse mesmo anexo tenha sido enviado através de várias mensagens de e-mail:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    

    Para resolver esta limitação, aplicamos o sabor de associaçãokind=inner interna ao especificar para mostrar todas as linhas na tabela esquerda com valores correspondentes à direita:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Associar registos a partir de um período de tempo – ao investigar eventos de segurança, os analistas procuram eventos relacionados que ocorram por volta do mesmo período de tempo. Aplicar a mesma abordagem ao utilizar join também beneficia o desempenho ao reduzir o número de registos a verificar.

    A consulta abaixo verifica a existência de eventos de início de sessão no prazo de 30 minutos após a receção de um ficheiro malicioso:

    EmailEvents
    | where Timestamp > ago(7d)
    | where ThreatTypes has "Malware"
    | project EmailReceivedTime = Timestamp, Subject, SenderFromAddress, AccountName = tostring(split(RecipientEmailAddress, "@")[0])
    | join (
    DeviceLogonEvents
    | where Timestamp > ago(7d)
    | project LogonTime = Timestamp, AccountName, DeviceName
    ) on AccountName
    | where (LogonTime - EmailReceivedTime) between (0min .. 30min)
    
  • Aplicar filtros de tempo em ambos os lados — Mesmo que não esteja a investigar um período de tempo específico, aplicar filtros de tempo nas tabelas esquerda e direita pode reduzir o número de registos para verificar e melhorar join o desempenho. A consulta abaixo aplica-se a ambas as tabelas Timestamp > ago(1h) para que associe apenas registos da última hora:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Utilizar sugestões para o desempenho — utilize sugestões com o join operador para instruir o back-end a distribuir a carga ao executar operações com muitos recursos. Saiba mais sobre sugestões de associação

    Por exemplo, a sugestão de aleatorização ajuda a melhorar o desempenho das consultas ao associar tabelas com uma chave com cardinalidade elevada ( uma chave com muitos valores exclusivos), tal como na AccountObjectId consulta abaixo:

    IdentityInfo
    | where JobTitle == "CONSULTANT"
    | join hint.shufflekey = AccountObjectId
    (IdentityDirectoryEvents
        | where Application == "Active Directory"
        | where ActionType == "Private data retrieval")
    on AccountObjectId
    

    A sugestão de difusão ajuda quando a tabela esquerda é pequena (até 100 000 registos) e a tabela direita é extremamente grande. Por exemplo, a consulta abaixo está a tentar associar alguns e-mails que têm assuntos específicos com todas as mensagens que contêm ligações na EmailUrlInfo tabela:

    EmailEvents
    | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now")
    | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
    

Otimizar o summarize operador

O operador summarize agrega os conteúdos de uma tabela. Aplique estas sugestões para otimizar as consultas que utilizam este operador.

  • Localizar valores distintos — Em geral, utilize summarize para localizar valores distintos que possam ser repetitivos. Pode ser desnecessário utilizá-lo para agregar colunas que não têm valores repetitivos.

    Embora um único e-mail possa fazer parte de vários eventos, o exemplo abaixo não é uma utilização eficiente de porque um ID de summarize mensagem de rede para um e-mail individual vem sempre com um endereço de remetente exclusivo.

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize by NetworkMessageId, SenderFromAddress
    

    O summarize operador pode ser facilmente substituído projectpor , o que produz potencialmente os mesmos resultados enquanto consome menos recursos:

    EmailEvents
    | where Timestamp > ago(1h)
    | project NetworkMessageId, SenderFromAddress
    

    O exemplo seguinte é uma utilização mais eficiente porque summarize pode haver várias instâncias distintas de um endereço de remetente a enviar e-mails para o mesmo endereço de destinatário. Estas combinações são menos distintas e são susceptíveis de ter duplicados.

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize by SenderFromAddress, RecipientEmailAddress
    
  • Misturar a consulta – embora summarize seja mais utilizada em colunas com valores repetitivos, as mesmas colunas também podem ter uma cardinalidade elevada ou um grande número de valores exclusivos. Tal como o join operador, também pode aplicar a sugestão aleatória com summarize para distribuir a carga de processamento e melhorar potencialmente o desempenho ao operar em colunas com cardinalidade elevada.

    A consulta abaixo utiliza summarize para contar o endereço de e-mail de destinatário distinto, que pode ser executado em centenas de milhares em grandes organizações. Para melhorar o desempenho, incorpora hint.shufflekey:

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
    

Cenários de consulta

Identificar processos exclusivos com IDs de processo

Os IDs de Processo (PIDs) são reciclados no Windows e reutilizados para novos processos. Por si só, não podem servir como identificadores exclusivos para processos específicos.

Para obter um identificador exclusivo para um processo num computador específico, utilize o ID do processo juntamente com a hora de criação do processo. Quando associa ou resume dados em torno de processos, inclua colunas para o identificador do computador (ou DeviceNameDeviceId ), o ID do processo (ProcessId ou InitiatingProcessId), e a hora de criação do processo (ProcessCreationTime ou InitiatingProcessCreationTime)

A consulta de exemplo seguinte localiza processos que acedem a mais de 10 endereços IP através da porta 445 (SMB), possivelmente à procura de partilhas de ficheiros.

Consulta de exemplo:

DeviceNetworkEvents
| where RemotePort == 445 and Timestamp > ago(12h) and InitiatingProcessId !in (0, 4)
| summarize RemoteIPCount=dcount(RemoteIP) by DeviceName, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessFileName
| where RemoteIPCount > 10

A consulta resume por ambos InitiatingProcessId e InitiatingProcessCreationTime para que analise um único processo, sem misturar vários processos com o mesmo ID de processo.

Linhas de comandos de consulta

Existem várias formas de construir uma linha de comandos para realizar uma tarefa. Por exemplo, um atacante pode referenciar um ficheiro de imagem sem um caminho, sem uma extensão de ficheiro, com variáveis de ambiente ou com aspas. O atacante também pode alterar a ordem dos parâmetros ou adicionar múltiplas aspas e espaços.

Para criar consultas mais duráveis em torno das linhas de comandos, aplique as seguintes práticas:

  • Identifique os processos conhecidos (como net.exe ou psexec.exe) ao corresponder nos campos de nome de ficheiro, em vez de filtrar na própria linha de comandos.
  • Analisar secções da linha de comandos com a função parse_command_line()
  • Ao consultar argumentos da linha de comandos, não procure uma correspondência exata em vários argumentos não relacionados numa determinada ordem. Em vez disso, utilize expressões regulares ou utilize vários operadores separados.
  • Utilizar correspondências não sensíveis a maiúsculas e minúsculas. Por exemplo, utilize =~, in~e contains em vez de ==, ine contains_cs.
  • Para mitigar técnicas de obfuscation da linha de comandos, considere remover aspas, substituir vírgulas por espaços e substituir múltiplos espaços consecutivos por um único espaço. Existem técnicas de obfuscation mais complexas que requerem outras abordagens, mas estes ajustes podem ajudar a lidar com as mais comuns.

Os exemplos seguintes mostram várias formas de construir uma consulta que procura o ficheiro net.exe para parar o serviço de firewall "MpsSvc":

// Non-durable query - do not use
DeviceProcessEvents
| where ProcessCommandLine == "net stop MpsSvc"
| limit 10

// Better query - filters on file name, does case-insensitive matches
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe") and ProcessCommandLine contains "stop" and ProcessCommandLine contains "MpsSvc"

// Best query also ignores quotes
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe")
| extend CanonicalCommandLine=replace("\"", "", ProcessCommandLine)
| where CanonicalCommandLine contains "stop" and CanonicalCommandLine contains "MpsSvc"

Ingerir dados de origens externas

Para incorporar listas longas ou tabelas grandes na consulta, utilize o operador externaldata para ingerir dados de um URI especificado. Pode obter dados de ficheiros em formatos TXT, CSV, JSON ou outros. O exemplo abaixo mostra como pode utilizar a extensa lista de hashes SHA-256 de software maligno fornecidos pelo MalwareBazaar (abuse.ch) para verificar anexos em e-mails:

let abuse_sha256 = (externaldata(sha256_hash: string)
[@"https://bazaar.abuse.ch/export/txt/sha256/recent/"]
with (format="txt"))
| where sha256_hash !startswith "#"
| project sha256_hash;
abuse_sha256
| join (EmailAttachmentInfo
| where Timestamp > ago(1d)
) on $left.sha256_hash == $right.SHA256
| project Timestamp,SenderFromAddress,RecipientEmailAddress,FileName,FileType,
SHA256,ThreatTypes,DetectionMethods

Analisar cadeias

Existem várias funções que pode utilizar para processar eficientemente cadeias de carateres que precisam de análise ou conversão.

Cadeia Função Exemplo de utilização
Linhas de comandos parse_command_line() Extraia o comando e todos os argumentos.
Caminhos parse_path() Extraia as secções de um caminho de ficheiro ou pasta.
Números de versão parse_version() Desconstrua um número de versão com até quatro secções e até oito carateres por secção. Utilize os dados analisados para comparar a idade da versão.
Endereços IPv4 parse_ipv4() Converta um endereço IPv4 num número inteiro longo. Para comparar endereços IPv4 sem os converter, utilize ipv4_compare().
Endereços IPv6 parse_ipv6() Converta um endereço IPv4 ou IPv6 para a notação IPv6 canónica. Para comparar endereços IPv6, utilize ipv6_compare().

Para saber mais sobre todas as funções de análise suportadas, leia sobre as funções de cadeia kusto.

Nota

Algumas tabelas neste artigo poderão não estar disponíveis no Microsoft Defender para Endpoint. Ative Microsoft Defender XDR para procurar ameaças através de mais origens de dados. Pode mover os fluxos de trabalho de investigação avançados de Microsoft Defender para Endpoint para Microsoft Defender XDR ao seguir os passos em Migrar consultas de investigação avançadas de Microsoft Defender para Endpoint.

Sugestão

Quer saber mais? Interaja com a comunidade do Microsoft Security na nossa Tech Community: Microsoft Defender XDR Tech Community.