rbacuri.dpdns.org — status: online

Detecção comportamental vs. por assinatura: testando os limites do meu SIEM

Depois de já ter trabalhado com detecção baseada em assinatura (bloquear um User-Agent conhecido, um IP malicioso listado), queria entender na prática os limites e as vantagens da detecção comportamental — analisar o padrão de uma requisição, não só se ela “bate” com algo já catalogado.

O setup

Montei um servidor Nginx isolado dentro de uma VM de laboratório, configurado pra logar cada requisição HTTP em formato JSON, capturando não só o User-Agent, mas o conjunto completo de headers que normalmente acompanham ele: Accept, Accept-Language, Accept-Encoding, Connection.

A ideia: um navegador de verdade nunca manda só o User-Agent sozinho — ele sempre acompanha de um conjunto previsível de outros headers. Se alguém está forjando o User-Agent (fingindo ser um Chrome, por exemplo) mas usando uma ferramenta de automação por trás, é bem provável que esses headers “de acompanhamento” estejam ausentes ou incompletos — porque poucas ferramentas de automação se dão ao trabalho de replicar o conjunto inteiro.

A regra

Com a baseline de uma requisição normal (Edge de verdade, headers completos) registrada no Wazuh, escrevi uma regra simples:

Se o User-Agent afirma ser um navegador conhecido (Chrome, Firefox, Safari), mas o header Accept-Language está ausente — sinalizar como possível automação mascarada.

O primeiro obstáculo (e uma lição sobre motores de regex)

A primeira versão da regra simplesmente não disparava — nem pra requisições que deveriam claramente bater nela. Depois de bastante investigação (incluindo confirmar, passo a passo, que o dado chegava certinho até o motor de análise), a causa era sutil: o motor de regras do Wazuh (OS_Regex) não suporta todos os operadores de um regex “completo” por padrão — inclusive o clássico + (um-ou-mais) simplesmente não funciona como esperado, sendo tratado como texto literal.

A correção: declarar explicitamente o motor de regex completo no campo da regra, usando o atributo type="pcre2". Depois disso, a regra passou a disparar exatamente como esperado.

É um detalhe pequeno, mas caro — o tipo de coisa que só se descobre testando de verdade, não lendo a documentação por cima.

O ataque

Usando o Burp Suite, montei uma requisição deliberadamente inconsistente: User-Agent completo de um Chrome moderno, mas sem Accept-Language, Accept-Encoding ou Connection. A regra disparou imediatamente, classificando a requisição como suspeita.

A evasão

Depois, ajustei a mesma requisição adicionando de volta os headers “esquecidos” — reproduzindo exatamente o conjunto que um Chrome real enviaria. Reenviei, e nenhum alerta disparou.

Isso não é falha da regra — é o limite esperado dela. Ela detecta ausência estrutural, e um atacante disposto a copiar fielmente o conjunto completo de headers passa despercebido. É uma lição concreta sobre por que nenhum sinal isolado é suficiente: uma regra assim pega reconhecimento automatizado feito às pressas, mas não detém alguém que fez o dever de casa.

O paralelo com um caso real

Isso me lembrou de uma situação real que já enfrentei protegendo sites WordPress com WP-Cerber: bots que chegavam com um conjunto de headers HTTP completo e absolutamente convincente — nada estruturalmente “errado” neles. A defesa baseada só em header não pegava nada.

O que funcionou de verdade foi olhar pra outro sinal: o destino das requisições. A esmagadora maioria dessas tentativas mirava um punhado previsível de caminhos sensíveis — /wp-admin, /admin, /.env, /wp-config.php — independente de quão “limpos” os headers estivessem. Filtrar por padrão de URI, não por header, foi o que realmente reduziu o ruído.

A lição final

Nenhum sinal sozinho é suficiente — nem headers, nem URI, nem User-Agent isolados. Detecção robusta de verdade combina múltiplos sinais fracos: presença/ausência de headers, o que está sendo acessado, a frequência entre requisições, e — algo que nem cheguei a explorar ainda — a ordem em que os headers chegam numa conexão TCP bruta, que ferramentas de automação raramente reproduzem fielmente. Cada camada adicional de sinal reduz o espaço que um atacante paciente consegue explorar pra passar despercebido.