03 - Consultas e Linguagens (KQL, SPL, Sigma)
video: https://youtu.be/... no frontmatter da nota.Consultas e Linguagens (KQL, SPL, Sigma)
Nota 03 do capítulo 09 (Ferramentas e Plataformas), que formaliza a gramática de consulta e de detecção usada nas plataformas das notas 01 e 02.
Cobre KQL e SPL com exemplos completos e apresenta Sigma como representação neutra que documenta e portabiliza a lógica de caça.
Dialoga com a nota 04 (TIPs), com os controles DAT.4 e TH.8 do capítulo 15 e com o capítulo 18, onde a caça confirmada vira regra Sigma.
1. Contexto e Posicionamento na Biblioteca
As notas 01 e 02 mostraram consultas soltas em Splunk, QRadar, Elastic e nas consoles de EDR; esta nota organiza a gramática por trás delas. Três representações resolvem quase todo o cotidiano do hunter: KQL onde existe o ecossistema Microsoft (Defender e Sentinel), SPL onde existe Splunk e Sigma como formato neutro que documenta, compartilha e portabiliza a lógica de detecção. O AQL do QRadar, apresentado na nota 01, segue a mesma lógica declarativa do SQL e fica de fora para dar profundidade às três representações mais usadas, e o EQL do Elastic aparece na nota 01 como caso particular de correlação sequencial.
A tese da nota é que o hunter escreve lógica uma vez e a traduz com disciplina, em vez de reescrever do zero a cada plataforma. A tradução malfeita é uma das fontes silenciosas de falso negativo: a regra muda de sintaxe e muda de semântica junto, sem que ninguém perceba. Por isso as seções finais tratam da tradução entre linguagens como habilidade técnica própria, com procedimento e teste, e não como tarefa mecânica de substituição de tokens.
2. KQL: Sintaxe, Operadores e Séries Temporais
KQL (Kusto Query Language) é uma linguagem de pipeline tabular: cada linha é uma transformação sobre o resultado da anterior, encadeada pelo pipe. Os operadores de uso diário na caça são where para filtro, project e extend para moldar colunas, summarize para agregação, bin para baldes de tempo, dcount para contagem distinta, join e lookup para enriquecimento, mv-expand para explodir listas e make-series para séries temporais com preenchimento de zeros. O exemplo abaixo expõe password spraying (T1110.003) com janelas de uma hora sobre falhas de logon do Defender:
DeviceLogonEvents
| where Timestamp > ago(14d) and ActionType == "LogonFailed"
| summarize Tentativas = count(), Contas = dcount(AccountName)
by RemoteIP, bin(Timestamp, 1h)
| where Contas >= 10
| order by Contas desc
O diferencial do KQL para caça é a família de funções de série temporal, que transforma agregação em análise estatística dentro da própria consulta, sem exportar para notebook. O exemplo abaixo detecta horas anômalas de conexões externas por host com decomposição de série, técnica aplicável a beaconing e a mudanças de comportamento:
DeviceNetworkEvents
| where Timestamp > ago(30d) and RemoteIPType == "Public"
| make-series Conexoes = count() default = 0
on Timestamp in range(ago(30d)..now(), 1h) by DeviceName
| extend (Anomalia, Pontuacao) = series_decompose_anomalies(Conexoes, 3.0)
| mv-expand Timestamp to typeof(datetime), Conexoes to typeof(long), Anomalia, Pontuacao
| where Anomalia != 0
| project DeviceName, Timestamp, Conexoes, Pontuacao
Duas regras de engenharia mantêm consultas KQL saudáveis em produção. Primeira, sempre limitar a janela com ago, porque consulta sem janela varre o período de retenção inteiro e concorre com a operação. Segunda, preferir comparadores insensíveis a caixa (=~, in~) quando o campo vem de telemetria heterogênea, e documentar quando o comparador sensível é intencional, porque essa escolha muda o resultado em silêncio.
3. SPL: Pipelines, stats e transaction
O SPL (Search Processing Language) também é um pipeline, mas organiza a busca em dois estágios com papéis distintos: a busca indexada, que filtra o mais cedo possível para reduzir o volume, e os comandos de transformação, como stats, bin, dedup, eval e transaction. A regra de ouro de desempenho é empurrar o filtro para a busca indexada e deixar para stats apenas o que precisa ser calculado. O exemplo abaixo repete o stack counting de execução via WMI, hipótese contra T1047, no dialeto do Splunk:
index=sysmon EventCode=1 ParentImage="*\\wbem\\wmiprvse.exe"
| stats count AS execucoes, dc(host) AS hosts, values(Image) AS filhos BY CommandLine
| where hosts < 3
| sort - execucoes
O comando transaction agrupa eventos em transações com identificador comum, duração máxima e pausa máxima, e é a ferramenta nativa para cadeias com estado, como rajadas de upload ou sessões de movimento lateral. O custo é alto em memória e tempo, e por isso o uso disciplinado exige maxspan e filtro prévio: o exemplo abaixo agrupa POSTs de upload por origem em janelas de dez minutos e marca as rajadas com cinco eventos ou mais:
index=proxy src_ip=10.20.7.0/24 http_method=POST uri_path="*upload*"
| transaction src_ip maxspan=10m maxpause=2m
| where eventcount >= 5
| table _time, src_ip, eventcount, duration, uri_path
Para janelas longas sobre volume alto, o par tstats e data model acelerado é a resposta do Splunk, como mostrado na nota 01. A disciplina de caça em SPL é a mesma de qualquer pipeline: comentário no início da consulta com a hipótese, filtro temporal sempre presente e preferência por stats sobre transaction quando a agregação simples resolve, porque cada transaction desnecessário rouba capacidade do cluster inteiro.
4. Sigma: A Lógica de Detecção Neutra de Plataforma
Sigma descreve a lógica de detecção em YAML e deixa a sintaxe específica para o conversor, o que a torna o formato de documentação e de portabilidade do catálogo de caças. A estrutura fixa inclui título, identificador UUID, status, descrição, logsource com categoria e produto, bloco detection com seleções nomeadas e condition, além de falsepositives, level e tags do ATT&CK. O exemplo completo abaixo deriva a regra permanente da caça HUNT-2025-014 do capítulo 07, com filtro de ruído do cliente SCCM documentado no baseline:
title: Interpretador de Comandos Filho do Servico WMI
id: 7b8d4a21-3c9e-4f61-b0a2-9e51c3f8d2b4
status: stable
description: >
Processo filho de interpretador nascido do serviço WMI, padrão de execução
remota por ferramentas administrativas abusadas. Derivado da caça
HUNT-2025-014 do capítulo 07.
logsource:
category: process_creation
product: windows
detection:
selecao_pai:
ParentImage|endswith: '\wbem\wmiprvse.exe'
selecao_filho:
Image|endswith:
- '\powershell.exe'
- '\cmd.exe'
- '\mshta.exe'
filtro_ruido:
ParentCommandLine|contains: 'SC_ConfigMgr'
condition: selecao_pai and selecao_filho and not filtro_ruido
falsepositives:
- administração remota legítima via SCCM e RunBooks aprovados
level: high
tags:
- attack.execution
- attack.t1047
A conversão é feita pela ferramenta sigma-cli, que gera consulta pronta para cada backend com pipeline de normalização de campos:
sigma convert -t splunk -p sysmon regras/wmi_spawn_shell.yml
sigma convert -t lucene -p ecs regras/wmi_spawn_shell.yml
O valor operacional do Sigma aparece em três situações: documentar o catálogo de caças em formato revisável por qualquer analista, compartilhar regras com ISACs e com a comunidade sem amarrar a plataforma e migrar de SIEM levando o catálogo junto. O limite também é claro: Sigma descreve lógica de seleção pontual, e as análises estatísticas e sequenciais das seções 2 e 3 continuam dependendo da linguagem da plataforma, ou de um notebook.
5. Quando Usar Cada Uma
A tabela abaixo organiza a decisão por situação de trabalho, e não por preferência de ferramenta. A mesma hipótese frequentemente passa pelas três representações ao longo da vida: nasce como exploração em KQL ou SPL, se confirma como caça documentada e se estabiliza como regra Sigma convertida para o backend de produção.
| Situação de trabalho | Escolha | Justificativa prática |
|---|---|---|
| Exploração interativa no Sentinel ou no Defender | KQL | Funções de série temporal e dcount nativas |
| Exploração interativa no Splunk | SPL | stats, tstats e ecossistema de apps maduro |
| Correlação sequencial por host no Elastic | EQL | sequence com janela e case-insensitivity |
| Detecção permanente multiplataforma | Sigma | Portabilidade, versionamento e revisão |
| Compartilhamento com ISAC e comunidade | Sigma | Formato aberto com conversores mantidos |
| Caça programática sobre data lake | SQL e pandas | Agregação e estatística fora do SIEM |
A regra prática é documentar em Sigma qualquer lógica que valha a pena rodar duas vezes. O catálogo Sigma da organização, versionado em repositório com revisão por pares, é ao mesmo tempo a biblioteca de detecções e o espelho das caças confirmadas, e alimenta diretamente o controle MAP.4 de lacunas de cobertura do capítulo 15.
6. Traduzindo Lógica Entre Linguagens
A tradução entre linguagens é habilidade própria porque os operadores não têm correspondência exata: contains em SPL casa com substring em qualquer posição, endswith em Sigma ancora no fim da string e has em KQL casa com token inteiro dentro do campo. Traduzir por substituição de tokens produz regras que compilam e detectam outra coisa. O procedimento recomendado tem quatro passos: extrair a lógica semântica da consulta original, mapear cada seleção para o operador equivalente no destino, escolher os campos pelo esquema do destino e testar com casos positivos e negativos conhecidos antes de publicar.
O mesmo padrão da regra Sigma da seção 4 fica assim em KQL, com os operadores de fim de string e o filtro de ruído preservados:
DeviceProcessEvents
| where InitiatingProcessFileName =~ "wmiprvse.exe"
and FileName in~ ("powershell.exe", "cmd.exe", "mshta.exe")
and InitiatingProcessCommandLine !contains "SC_ConfigMgr"
E assim em SPL, com a seleção ancorada pelo caminho completo do pai:
index=sysmon EventCode=1 ParentImage="*\\wbem\\wmiprvse.exe"
(Image="*\\powershell.exe" OR Image="*\\cmd.exe" OR Image="*\\mshta.exe")
NOT ParentCommandLine="*SC_ConfigMgr*"
Duas armadilhas merecem verificação em toda tradução. A primeira é a sensibilidade a caixa, que varia por operador e por plataforma, e que em nomes de executável costuma ser irrelevante, mas em linha de comando às vezes é sinal. A segunda é a normalização de caminho: barra invertida no Windows sobrevive de forma diferente em cada esquema, e o padrão que casa no Sysmon cru pode não casar no campo normalizado do ECS. O teste com caso positivo conhecido, como uma execução registrada deliberadamente, fecha essa lacuna antes que a regra entre em produção cega.
7. Erros Comuns e Pontos de Atenção
- Traduzir por substituição de tokens; efeito: regra equivalente na sintaxe e diferente na semântica, com falso negativo silencioso; correção: traduzir a lógica semântica e testar com casos positivos e negativos conhecidos.
- Usar wildcard inicial sem necessidade; efeito: busca que varre o índice inteiro e degrada a plataforma; correção: ancorar o padrão no início do token e reservar wildcard livre para investigação pontual.
- Escrever Sigma sem logsource coerente; efeito: conversão gera consulta vazia ou consulta sobre fonte que não existe no ambiente; correção: validar categoria e produto contra o inventário de dados antes de converter.
- Condition ambígua no Sigma; efeito: união indesejada de seleções amplia demais o alarme; correção: nomear cada seleção com função clara e relembrar que seleções sem referência na condition se juntam por AND implícito.
- Usar transaction sem maxspan; efeito: transação que engole o dia inteiro, custo alto e resultado sem interpretação; correção: delimitar maxspan e maxpause e preferir stats quando possível.
- Deixar consulta confirmada sem virar Sigma; efeito: conhecimento preso na sintaxe de uma plataforma e perdido na troca de produto; correção: registrar a versão Sigma como fonte canônica e gerar as consultas por conversão.
8. Integração com o Restante da Biblioteca
Esta nota é a camada de linguagem do capítulo 09: as plataformas das notas 01 e 02 fornecem o lugar onde as consultas rodam, e a nota 05 fornece o ambiente onde a estatística além da consulta acontece. As técnicas de stack counting e clustering dos controles TH.8 e TH.9 se expressam exatamente nas consultas aqui exemplificadas, o controle DAT.4 decide quais esquemas normalizam os campos que as regras consomem, e o capítulo 18 consome o catálogo Sigma como matéria-prima do detection engineering. A tradução de lógica entre linguagens também é o que permite que a mesma hipótese rode na console do EDR da nota 02 e no SIEM da nota 01 com comportamento comparável.
Um hábito final fecha a nota com ganho permanente: toda consulta de caça que sobreviver a duas execuções merece versão Sigma no repositório, com testes de caso positivo e negativo anexados. Esse hábito simples converte exploração individual em ativo institucional, reduz a reescrita em cada troca de plataforma e dá ao capítulo 11 uma métrica honesta de catálogo vivo, porque regra sem execução e sem teste é papel morto dentro do repositório.