Olá a todos!
Meu trabalho envolve a análise de tráfego de rede e a escrita de regras de detecção para o Suricata. Quero compartilhar com vocês minha experiência com esta ferramenta. Em uma série de artigos, vamos mergulhar nas complexidades deste sistema IDS/IPS, aprender a escrever regras de assinatura e entender como criá-las para identificar atividades ilegítimas. Informações básicas sobre a ferramenta (como o Suricata analisa pacotes, suas vantagens sobre o Snort e quais módulos de captura ele pode usar) podem ser facilmente encontradas na internet. Também assumo que você possui conhecimentos básicos de redes e sabe usar o Wireshark.
Explicarei a teoria conforme avançamos no texto – vamos direto para a prática:
Recentemente, fui apresentado a uma ferramenta interessante e útil para testar o Suricata: o Dalton. Poderíamos instalar apenas o Suricata e executá-lo via terminal. O Dalton me atraiu pela sua interface web e pela conveniência de testar regras em arquivos pcap. Portanto, quero apresentá-lo a vocês também.
Instalação do Dalton
Instalaremos o Dalton em uma máquina virtual Ubuntu. Portanto, instale o Ubuntu e aloque dois interfaces para ele (o primeiro com tipo NAT e o segundo com tipo de acesso "Bridge Network"). Dedique 4 GB de RAM, 2 núcleos de processamento e 50 GB de armazenamento persistente.
Após instalar o Ubuntu, instale imediatamente os Guest Additions, Wireshark e Git, e siga as instruções abaixo:
bashroot@sam-VirtualBox:/home/sam/Desktop/# git clone https://github.com/secureworks/dalton.git
Instalaremos o Docker:
bashroot@sam-VirtualBox:/home/sam/Desktop/dalton# apt install ca-certificates curl root@sam-VirtualBox:/home/sam/Desktop/dalton# install -m 0755 -d /etc/apt/keyrings root@sam-VirtualBox:/home/sam/Desktop/dalton# sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc root@sam-VirtualBox:/home/sam/Desktop/dalton# chmod a+r /etc/apt/keyrings/docker.asc root@sam-VirtualBox:/home/sam/Desktop/dalton# echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null root@sam-VirtualBox:/home/sam/Desktop/dalton# apt-get update root@sam-VirtualBox:/home/sam/Desktop# sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verificamos se o Docker foi instalado corretamente:
bashroot@sam-VirtualBox:/home/sam/Desktop# sudo docker run hello-world root@sam-VirtualBox:/home/sam# curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/docker-compose root@sam-VirtualBox:/home/sam# chmod +x /usr/bin/docker-compose root@sam-VirtualBox:/home/sam# docker-compose --version
Em seguida, vamos ao arquivo docker-compose.yml e comentamos as linhas relacionadas à instalação do Snort (pois o Snort não pode ser baixado da Rússia).
Em seguida, executamos o script de acordo com as instruções do repositório do Dalton:
bashroot@sam-VirtualBox:/home/sam/Desktop# ./start-dalton.sh
Ao final, você verá algo assim:
Lembre-se que temos 2 interfaces na máquina virtual?! – Na interface com o tipo "Bridge Network", configure um endereço IP estático da sua rede local. Assim, você poderá acessar a utilidade Dalton simplesmente digitando o endereço http://192.168.1.150/dalton/ no navegador da sua máquina host. A máquina virtual pode ser minimizada (se necessário – nós a utilizaremos).
Neste ponto, não se esqueça de criar um snapshot do estado da máquina. Isso porque o Docker pode consumir muito espaço e, se não for possível limpá-lo adequadamente, você poderá simplesmente retornar ao estado inicial. Sei que pode surgir a dúvida: por que instalar a versão desktop do Ubuntu, não seria melhor usar a versão server CLI e trabalhar a partir do host local? Bem, eu decidi fazer assim. De qualquer forma, o guia está disponível e você pode fazer o mesmo com a versão Ubuntu Server.
No momento da escrita deste artigo, a versão instalada do Suricata era a 8.0.1.
Resultado de uma instalação bem-sucedida:
O que fazer com o Dalton
Vamos fazer uma breve introdução ao Dalton. Ao acessar a página principal, vamos para o Suricata clicando no botão "Go":

Aqui vemos: há a opção de selecionar um arquivo pcap para upload e teste. Você pode selecionar vários pcaps. Deixaremos a versão do Suricata como 8. "Use a defined ruleset" – vamos desativar. Para testar suas próprias regras, você precisará marcar a caixa "Use custom rules", uma janela se abrirá onde inseriremos as regras. Não alteraremos as outras configurações. Ao iniciar o Suricata, precisaremos selecionar o pcap, escrever a regra e clicar no botão "Submit". Na aba "Config Files", você pode encontrar o arquivo de configuração do Suricata:
Todo o aprendizado será baseado na documentação.
Nosso caminho de aprendizado do Suricata começará no item 8, subitem 1 da documentação acima.
Formato da Regra
A cor vermelha destaca a ação que o Suricata deve tomar se um fragmento de tráfego corresponder às condições da regra. As ações possíveis são:
alert– gera um alerta.pass– interrompe a análise posterior da sessão/pacote.drop– descarta os pacotes e gera um alerta.reject– envia um erro de indisponibilidade RST/ICMP para o destinatário do pacote que acionou a regra.rejectsrc– o mesmo quereject.rejectdst– envia um erro de indisponibilidade RST/ICMP para o remetente do pacote que acionou a regra.rejectboth– envia um erro de indisponibilidade RST/ICMP para ambos os participantes da comunicação de rede.
Quero chamar sua atenção para o fato de que, se o Suricata NÃO estiver configurado para operar no modo IPS, apenas alert e pass funcionarão. Meu treinamento se concentrará principalmente na função alert.
Em seguida, analisaremos o que está destacado em verde: http $HOME_NET any → $EXTERNAL_NET any. Primeiro, o protocolo é especificado (não listarei aqui os possíveis protocolos que o Suricata pode detectar, consulte a documentação), e em seguida, as informações sobre os participantes da conexão são fornecidas. O esquema é o seguinte: endereço_origem porta_origem → endereço_destino porta_destino. Tanto os endereços quanto as portas podem ser especificados individualmente, em listas ou através de variáveis.
Exemplos:
$HOME_NET any → $EXTERNAL_NET 123!1.1.1.1 any → 2.2.2.2 $ORACLE_PORTS[1.1.1.1, 1.1.1.2] any -> $EXTERNAL_NET [80, 81, 82]
As variáveis são definidas no arquivo de configuração:

Você mesmo pode criar suas variáveis e inserir nelas endereços IP, sub-redes e portas.
E agora, um símbolo importante – a direção →. Vamos parar aqui e falar um pouco mais sobre a direção da conexão. Na rede, sempre temos um iniciador e um receptor. O sinal de direção -> estabelece a correspondência da assinatura com a direção do fluxo de pacotes. É importante distinguir entre "pacotes" e "sessão" e entender a diferença:
- Sessão – a definição de uma conexão estabelecida entre dois nós de rede. Refere-se principalmente ao protocolo TCP, que garante a entrega de dados.
- Pacotes – um bloco de dados estruturado em que a informação é dividida para transmissão pela rede.
Neste caso, estamos falando da direção do fluxo de pacotes. Consequentemente, se definirmos a condição na regra 1.2.3.4 -> 5.6.7.8, a assinatura identificará correspondências apenas para pacotes que vão do cliente 1.2.3.4 para o servidor 5.6.7.8.
Também é preciso entender que, dentro de uma sessão entre dois participantes da rede, os pacotes podem fluir em ambas as direções. Vamos esclarecer, voltando ao esquema acima onde o cliente 1.2.3.4 inicia a conexão com o servidor 5.6.7.8. Se considerarmos uma conexão via protocolo UDP, os pacotes dentro de uma única conexão irão apenas da direção do cliente para o servidor. Se considerarmos a situação em que o cliente inicia uma conexão com o servidor via protocolo TCP, uma sessão TCP é estabelecida entre eles. E dentro de uma sessão TCP estabelecida, os hosts enviam pacotes um para o outro para confirmação de dados. Consequentemente, ao iniciar uma conexão do cliente para o servidor via protocolo TCP, a direção dos pacotes pode ser tanto do cliente para o servidor quanto do servidor para o cliente, dentro de sua sessão TCP. A direção do pacote é determinada por seus cabeçalhos, que indicam o remetente e o destinatário. O remetente e o destinatário podem ser qualquer participante da comunicação de rede, seja um cliente ou um servidor dentro de uma sessão TCP. Com a prática, consolidaremos esse conhecimento.
Observação importante: o Suricata não suporta a notação de direção inversa <-.
Opções possíveis:
source -> destination– pacotes com essa direção corresponderão.source => destination– uma funcionalidade relativamente nova no momento da escrita deste artigo. Funciona da mesma forma que a anterior, mas pode funcionar com conexões bidirecionais.source <> destination– haverá correspondências bidirecionais.
Em seguida, após a especificação dos participantes da rede, vêm as condições da regra (destacadas em azul).
Vamos ver como as regras são escritas, como especificar corretamente o destinatário e o remetente da comunicação de rede, como definir a direção e assim por diante… Vamos entender os conceitos básicos, por assim dizer. Acesse este link e baixe o http.cap. Abra-o no Wireshark, vá para a seção Suricata no Dalton, selecione este pcap através do botão "Select File".
Examine o pcap cuidadosamente:

Temos dois participantes na rede: 145.254.160.237 e 65.208.228.223. Vamos escrever uma regra para uma requisição HTTP:
suricataalert tcp 145.254.160.237 any -> 65.208.228.223 80 (msg:"Test Http request"; content:"GET"; content:"download.html"; sid:1;)
Breve explicação do funcionamento da regra: a assinatura gera um alerta para uma conexão TCP de 145.254.160.237 para 65.208.228.223 na porta 80. Como sabemos, o protocolo de transporte de nível 4 do modelo OSI é usado para a transmissão garantida de dados do remetente para o destinatário. Consequentemente, as requisições HTTP (como em nosso exemplo) são transmitidas na carga útil dos pacotes TCP. E nós, usando o modificador content, procuramos por "GET" e "download.html" na carga útil. Assim, pedimos ao Suricata para gerar um alerta se encontrar a sequência de caracteres GET e “download.html” na carga útil, do host 145.254.160.237 para o host 65.208.228.223 na porta 80.
Vamos executar a regra e ver os acionamentos. Copiamos a regra acima e a colamos na janela sob a frase "Use custom rules". E não se esqueça de marcar a caixa. Clicamos em "submit".
Temos 1 alerta:

Vamos para a aba "Alert Debug". Lá podemos ver os logs analisados e os acionamentos.
Aqui, muitas informações úteis estão disponíveis. Descreverei os campos principais:
- PCAP PKT NUM: número do pacote no pcap que acionou a assinatura. Este valor está disponível quando o Suricata é executado para verificar um arquivo pcap com tráfego, para que ele possa pesquisar no dump de tráfego no Wireshark. Pelo menos, é o que diz a documentação, mas se olharmos para o frame 7 no Wireshark, veremos que ele não contém uma carga útil com a requisição HTTP. No entanto, o frame 7 (pacote) pertence à sessão TCP na qual a requisição HTTP foi feita e contém um fragmento do acionamento da nossa assinatura. O número do pacote acionado nos dará o ID da sessão, onde posteriormente poderemos estudá-la completamente.
- SRC IP, DST IP: IP de origem e destino do pacote, respectivamente.
- SRC PORT, DST PORT: Porta de origem e destino do pacote, respectivamente.
- TCP SEQ, TCP ACK: Números de sequência e confirmação TCP no pacote que acionou a assinatura.
- FLOW: Campo que contém informações sobre a direção do pacote: do servidor para o cliente ou do cliente para o servidor. No nosso caso, do cliente para o servidor (
to_server: TRUE). - ALERT CNT: Número de alertas ou regras acionadas por pacote/sessão.
- ALERT SID: SID da regra acionada.
- ALERT MSG: Mensagem da regra acionada.
- PACKET: Aqui o pacote é apresentado em ASCII. Como se você estivesse olhando no Wireshark a área de representação byte a byte do pacote.
- STREAM DATA: Aqui é apresentada a parte do fluxo TCP que acionou o evento. Como visualizá-la? Abra o Wireshark, clique com o botão direito no frame 7 e selecione "Follow" -> "TCP Stream". Abaixo na imagem, destaquei qual parte do STREAM (fluxo) é apresentada na saída do Dalton.
Assim, nossa regra Suricata alert tcp 145.254.160.237 any -> 65.208.228.223 80 (msg:"Test Http request"; content:"GET"; content:"download.html"; sid:1;), encontrou no dump de tráfego uma requisição HTTP que possui o método GET e também contém o URI “download.html” na requisição. A saída do Dalton confirma isso com os valores correspondentes nos campos mencionados acima.
E também, quero dizer que o Suricata é capaz de trabalhar com regras para protocolos de aplicação. O próprio Suricata possui mecanismos internos que permitem identificar os protocolos nos cabeçalhos dos pacotes. No Wireshark, por exemplo, existem dissectors que nos permitem analisar os cabeçalhos dos pacotes. Esses dissectors também podem ser usados por outros softwares de análise de tráfego. No Suricata, você pode adicionar código de dissector para seus protocolos customizados, por exemplo. Então, se você tiver comunicação de rede via protocolo HTTP, mas não pela porta 80, e sim pela porta 3380, o Suricata também a identificará. Podemos complementar a regra acima e especificá-la da seguinte forma:
suricataalert http 145.254.160.237 any -> 65.208.228.223 80 (msg:"Test Http request"; flow:established,to_server; content:"GET"; content:"download.html"; sid:1;)
Você pode executá-la no Dalton por conta própria. A saída da regra não será muito diferente da anterior. Vamos falar melhor sobre o modificador adicionado flow:established,to_server;. Este modificador especifica a busca por pacotes em conexões estabelecidas (falamos de conexões TCP) e que os pacotes sejam direcionados do cliente para o servidor.
Listarei todas as variantes possíveis de flow:
to_client– correspondências em pacotes direcionados ao cliente.to_server– correspondências em pacotes direcionados ao servidor.from_client– correspondências em pacotes originados do cliente (análogo ato_server).from_server– correspondências em pacotes originados do servidor (análogo ato_client).established– correspondências em pacotes de conexões estabelecidas.not_established– correspondências em pacotes não relacionados a conexões estabelecidas.stateless– correspondências em pacotes de fluxo (fluxo TCP, fluxos UDP, etc.). Pacotes não relacionados a um fluxo não atenderão às condições.only_stream– correspondências em pacotes remontados (reassembled). Você pode ver tais pacotes no Wireshark no canto inferior direito, onde a representação byte a byte do pacote é exibida. Haverá uma aba "reassembled". Pacotes remontados aparecem quando, durante a transmissão de dados, houve interferências e os dados chegaram incompletos – eles precisam ser remontados. Ou, por exemplo, vários fragmentos de dados foram transmitidos, após os quais eles precisam ser remontados.no_stream– correspondências em pacotes não relacionados a pacotes remontados.only_frag– correspondências em pacotes que foram montados a partir de fragmentos.no_frag– correspondências em pacotes que não foram montados a partir de fragmentos.
Também é possível o uso composto das variáveis acima:
flow:to_client, establishedflow:to_server, established, only_streamflow:to_server, not_established, no_frag
Veja como eles funcionam. Você pode experimentar com outras regras. Tente, erre, tente novamente.
Observações importantes:
- Os valores de
msgpodem ser os mesmos. O importante é que os valores desidsejam diferentes.Sidé o identificador da regra. Se houver duas ou mais regras comsidiguais no arquivo de regras, o Suricata se recusará a iniciar. - Tráfego montado incorretamente, pcap dividido incorretamente, ausência de pacotes SYN, ACK no pcap não permitirão a execução das regras do Suricata em um pcap.
- Se você quiser usar os caracteres ";" ou
", eles precisam ser escapados. Os caracteres podem ser especificados no modificadormsgoucontent, por exemplo. No modificadormsg, pode ser escapado da seguinte forma:msg:"Test Http \; request";. Você também pode fazer isso nocontent, mas eu recomendaria usar sua representação em bytes em vez dos métodos descritos acima – essa opção é mais confiável e correta.
Esta é a primeira parte. Obrigado a todos pela atenção! Críticas são bem-vindas. Serei muito grato por elas.





