Ossec – Escrevendo Regras – HIDS parte 10
Fala galera! Dando continuidade a nossa série de posts sobre o ossec, hoje vamos dar continuação ao post anterior sobre decoders e falar sobre as regras, como disse antes, eles andam meio que juntos, mas não é obrigatoriedade. Essa é uma das partes chave do ossec que dão a ele o poder que ele tem. Quando trabalhamos com os decoders e as regras somos capazes de configurar e tunar qualquer alerta que o ossec gere.
Cada regra do ossec fica armazenada dentro do diretório /var/ossec/rules, as regras são agrupadas em arquivos XML diferentes e nomeadas de acordo com sua categoria. Cada arquivo de regra contém diversas regras para uma ou mais aplicações, são mais de 1500 regras para várias aplicações como Apache, ProFTP, Snort, SSH, Squid e muitas outras. Cada regra tem seu grupo e sua classificação (severidade), a lista de cada classificação e grupo pode ser encontrada mais abaixo do post.
Cada regra tem um ID único que é dado no momento de sua criação, para cada tipo de log temos um range de IDs pré-designados para garantir que o ossec não sobrescreva os decoders erroneamente. Existe um range de IDs dedicados para serem usados pelas regras criadas pelos usuários, o range é de 100.000 até 119.999, essas regras precisam estar no arquivo local_rules.xml localizado em /var/ossec/rules, assim você não corre o risco de suas regras serem sobrescritas.
O ossec avalia as regras para ver se o evento recebido deve ser escalado a um alerta ou se deve ser parte de um alerta composto com múltiplos eventos. Basicamente existem dois tipos de regras, simples e compostas. As regras simples são baseadas em um único evento, sem nenhuma correlação, por exemplo um tentativa de login. Já as regras compostas são baseadas em múltiplos eventos, por exemplo, 5 tentativas de login falhas de uma mesma origem.
Sintaxe – Expressão regular
Antes de continuar com os exemplos de regras precisamos entender a sintaxe da expressão regular que é utilizada no desenvolvimento das regras e nos decoders. O ossec usa uma biblioteca em C simples para expressão regular que cobre a maioria das expressões comuns, podemos ver a sintaxe abaixo:
Expressões suportadas
\w -> Caracteres de A-Z, a-z, 0-9, '-', '@'
\d -> Caracteres de 0-9
\s -> Para espaços " "
\t -> Para tabs.
\p -> ()*+,-.:;<=>?[]!"'#$%&|{} (Caracteres de pontuação)
\W -> Para qualquer coisa, menos \w
\D -> Para qualquer coisa, menos \d
\S -> Para qualquer coisa, menos \s
\. -> Para qualquer coisa, menos
Modificadores
+ -> Corresponder uma ou mais vezes (eg \w+ or \d+)
* -> Corresponder nenhuma ou mais vezes (eg \w* or \p*)
Caracteres especiais
^ -> Para especificar o começo do texto.
$ -> Para especificar o fim do texto.
| -> Para criar um "OR" entre múltiplos padrões.
Escaping de caracteres
$ -> \$
( -> \(
) -> \)
\ -> \\
| -> \|
Entendendo as regras
Vamos começar com as regras simples, as regras que são baseadas em um evento único, depois passamos para as regras compostas. Cada regra ou grupo de regras deve ser definida dentro de uma chave ‘group’ e seu atributo ‘name’ deve conter as categorias que você deseja que façam parte desse grupo. Iremos utilizar como exemplo o arquivo de regras sshd_rules.xml, nesse exemplo é indicado que esse grupo irá conter regras para ssh e syslog:
<group name="syslog,sshd,">
....
</group>
Um grupo pode conter quantas regras você desejar, as regras são declaradas utilizando a chave ‘rule’ e deve conter pelo menos dois atributos, ‘id’ e ‘level’. É importante que cada regra tenha uma descrição que é declarada utilizando a chave ‘description’, desse modo fica mais fácil na hora de identificar os alertas. Outra chave importante é a ‘decoded_as’, ela indica que a regra vai ser lida se o decoder especificado dela decodificar o evento (log). A regra exibida abaixo é a primeira regra no arquivo sshd_rules.xml que será utilizado como base para as demais regras:
<rule id="5700" level="0" noalert="1">
<decoded_as>sshd</decoded_as>
<description>SSHD messages grouped.</description>
</rule>
Essa regra é bem simples pois ela será utilizada como referência para outras regras utilizando a chave ‘if_sid’, dessa maneira não será necessário repetir a chave ‘decoded_as’ a cada regra. Para que a regra possa analisar o conteúdo do log e buscar por padrões pré-definidos a regra precisa conter pelo menos a chave ‘match’ e em alguns casos também a chave ‘regex’. No exemplo abaixo vemos uma regra que verifica se o log indica uma tentativa de login sem sucesso:
<rule id="5716" level="5">
<if_sid>5700</if_sid>
<match>^Failed|^error: PAM: Authentication</match>
<description>SSHD authentication failed.</description>
<group>authentication_failed,</group>
</rule>
Perceba que a regra faz menção a regra anterior (id=5700), essa regra só vai ser lida caso a anterior seja correspondente ao evento, assim funciona a lógica da chave ‘if_sid’. Na segunda instrução a chave ‘match’ irá buscar as palavras ‘Failed’ ou ‘error: PAM: Authentication’. Essa regra irá gerar a mensagem “SSHD authentication failed” no alerta. Por último temos a chave ‘group’ que indica que essa regra faz parte do grupo “authentication_failed”. Caso o ossec leia o log abaixo essa regra irá ser executada:
Ago 09 18:33:09 server-server sshd[1231]: Failed password for invalid user fulano from 192.168.0.10 port 2299 ssh2
No exemplo abaixo temos outra regra, porém essa utiliza a chave ‘regex’. Essa chave é utilizada quando aquela parte do log não é padronizada ou não é sempre o mesmo valor, por exemplo um endereço IP, uma porta ou o nome de um host:
<rule id="5730" level="4">
<if_sid>5700</if_sid>
<regex>error: connect to \S+ port \d+ failed: Connection refused</regex>
<description>SSHD is not accepting connections.</description>
</rule>
Agora que já entendemos as regras simples vamos ver alguns exemplos de regras compostas, a lógica é bem simples, basicamente a regra precisa analisar um tipo de evento específico quantas vezes for definido e dentro de um intervalo de tempo pré-estipulado. Esse tipo de regra é utilizada para identificar ataques de força bruta ou scans de serviços/aplicação. No exemplo abaixo iremos ver esse tipo de regra:
<rule id="5704" level="4">
<if_sid>5700</if_sid>
<match>fatal: Timeout before authentication for</match>
<description>Timeout while logging in (sshd).</description>
</rule>
<rule id="5705" level="10" frequency="4" timeframe="360">
<if_matched_sid>5704</if_matched_sid>
<description>Possible scan or breakin attempt </description>
<description>(high number of login timeouts).</description>
</rule>
Basicamente a “inteligência” da regra fica por conta dos atributos ‘frequency’ e ‘timeframe’ da chave ‘rule’, são esses atributos que irão definir o intervalo de tempo e a quantidade de vezes que o evento precisa ocorrer, no exemplo acima o evento com id 5704 precisa ocorrer 8 vezes (sempre multiplicamos o valor por 2) dentro de 360 segundos. Abaixo segue mais um exemplo de regra composta, dessa vez utilizando mais uma chave:
<rule id="5710" level="5">
<if_sid>5700</if_sid>
<match>illegal user|invalid user</match>
<description>Attempt to login using a non-existent user</description>
<group>invalid_login,authentication_failed,</group>
</rule>
<rule id="5712" level="10" frequency="6" timeframe="120" ignore="60">
<if_matched_sid>5710</if_matched_sid>
<description>SSHD brute force trying to get access to </description>
<description>the system.</description>
<same_source_ip />
<group>authentication_failures,</group>
</rule>
Nesse exemplo temos uma regra para identificar ataques de brute force, ela irá buscar pelo evento com id 5710 12 vezes dentro de 120 segundos vindo do mesmo endereço IP. Além disso ela irá adicionar a regra ao grupo ‘authentication_failures’. Essa regra tem uma particularidade, que é o atributo ‘ignore’ na chave ‘rule’, ele indica que o ossec irá ignorar esse evento nos próximos 60 segundos depois que a regra for executada, isso é feito para evitar floods do mesmo alerta.
Escrevendo uma regra personalizada
Aqui iremos dar continuidade ao último post, pois iremos aproveitar todos os decoders que criamos e o conhecimento adquirido, que vai ser fundamental aqui. Então, se você não conferiu o último post e seguir o passo a passo, eu recomendo fortemente que dê um passo atrás 😉 . Bem, os logs que iremos utilizar são:
Aug 11 17:05:50 router routerdaemon: Success Web login to Administrator from 192.168.0.11
Aug 11 17:05:50 router routerdaemon: Failed Web login to Administrator from 192.168.0.55
Nós já criamos os decoders que irão mostrar ao ossec como ler e identificar os campos chaves desses logs no post anterior, então o que precisamos fazer agora é criar as regras necessárias para que o ossec-logtest complete a fase 3. Nós iremos criar as seguintes regras, uma para identificar um login bem sucedido, uma para um login com falha, uma para identificar tentativa de brute force e uma regra extra (brinde) que irei explicar mais abaixo.
Bom, como mencionei acima, todas as regras que iremos criar precisa estar no aquivo local_rules.xml, por padrão esse arquivo contem alguns exemplos, recomendo você apagá-los antes de continuarmos. Como no exemplo do SSH acima, a primeira regra que iremos criar é a que irá servir de base para todas as outras regras, ela irá dizer pro ossec que essas regras estão correlacionadas aos decoders que criamos antes. Adicione o conteúdo abaixo no arquivo:
<rule id="100001" level="0" noalert="1">
<decoded_as>router_xx</decoded_as>
<description>Router messages grouped.</description>
</rule>
Agora precisamos criar nossa primeira regra na verdade, a regra que irá identificar e alertar um login bem sucedido, adicione a regra abaixo no arquivo. Ela é bem simples, se o log começar com a palavra ‘Success’ ele irá alertar:
<rule id="100002" level="3">
<if_sid>100001</if_sid>
<match>^Success</match>
<description>User router web login successfully.</description>
</rule>
A segunda regra será a que vai identificar uma tentativa de login mal sucedida, assim como a anterior ela é bem simples, ela irá buscar pela string ‘Failed’ no início do log:
<rule id="100003" level="3">
<if_sid>100001</if_sid>
<match>^Failed</match>
<description>Failed router web login.</description>
</rule>
Uma vez que temos a regra que identifica uma tentativa de login mal sucedida, basta ser criada uma outra regra para identificar a ocorrência desse evento X vezes:
<rule id="100004" level="10" frequency="3" timeframe="1800">
<if_matched_sid>100003</if_matched_sid>
<same_source_ip />
<description>Possible brute force.</description>
</rule>
Essa regra irá verificar o evento anterior 6 vezes dentro de um intervalo de 30 minutos vindos do mesmo IP. Como essa regra tem level 10, caso ela ocorra será executada o active response firewall-drop que irá bloquear o IP de origem. Depois de adicionar as regras no local_rules.xml, reinicie o seu ossec server e faça alguns testes adicionando as entradas de log no arquivo /var/log/auth.log utilizando o comando echo.
Bem como prometido, aqui vai uma última regra pro nosso lab, é muito comum termos um mesmo usuário sendo utilizado em mais de um lugar, isso pode indicar que alguém está compartilhando senha ou até mesmo que um atacante comprometeu a credencial de alguém, em qualquer um dos casos vale muito a pena checar esse tipo de evento, mas para isso precisamos ensinar ao ossec como fazer isso. Adicione a regra abaixo no arquivo:
<rule id="100005" level="5" frequency="1" timeframe="7200">
<if_matched_sid>100002</if_matched_sid>
<same_user />
<description>Same user logged in in a short space time.</description>
</rule>
Essa regra irá buscar pelo evento de login bem sucedido 2 vezes dentro de um período de 2 horas utilizando o mesmo usuário, ela é bem simples mas bem interessante, vocês vão ficar impressionado com o que podem descobrir no ambiente de vocês com ela. Como se trata de uma regra bem simples ela pode acabar causando alguns falsos positivos, porém dependendo do ambiente ela atende. Caso contrário é só dar uma tunada nela.
Obs: Não se esqueçam para adequar a regra para o serviço que desejam monitorar, aqui eu usei para os logs que estamos trabalhando 😉
Severidade das regras
O range de severidade do ossec vai do 0 ao 15, ele é dado no momento da escrita da regra. A severidade pode ser alterada caso seja necessário, porém raramente isso é preciso. Abaixo segue uma descrição da indicação padrão para cada nível de severidade disponível no ossec:
-
- 00 – Ignored – Nenhuma ação a ser tomada. É usado para evitar falso positivos.
-
- 01 – None –
-
- 02 – System low priority notification – Notificação do sistema ou mensagens de status, elas não tem criticidade relevante.
-
- 03 – Successful/Authorized events – Elas incluem tentativas de login legítimas, eventos allow do firewall e etc.
-
- 04 – System low priority error – Erros relacionados a erros de configuração ou dispositivos e aplicações não utilizados.
-
- 05 – User generated error – Elas incluem problemas com senhas, ações negadas e etc.
-
- 06 – Low relevance attack – Elas indicam um worm ou um vírus que não tem efeito sobre o sistema.
-
- 07 – “Bad word” matching – Elas incluem palavras como ‘bad’, ‘error’ e etc.
-
- 08 – First time seen – Incluem eventos FTS, eventos FTS como primeiros eventos de IDS ou a primeira vez que um usuário logou.
-
- 09 – Error from invalid source – Incluem tentativas de login não sucedidas, elas também incluem erros com a conta admin do sistema.
-
- 10 – Multiple user generated errors – Elas incluem múltiplas senhas ruins, múltiplas tentativas de login sem sucesso. Elas podem indicar um ataque ou indicar um usuário que esqueceu a senha.
-
- 11 – Integrity checking warning – Elas incluem modificação de binários ou presença de rootkits. Elas podem indicar um ataque bem sucedido.
-
- 12 – High importancy event – Elas incluem erros ou mensagens de alerta do sistema, kernel e etc. Elas podem indicar um ataque contra uma aplicação específica.
-
- 13 – Unusual error (high importance) – Na maioria das vezes elas correspondem a padrões de ataques.
-
- 14 – High importance security event – Na maioria das vezes tem correlação com outros eventos e pode indicar um ataque.
-
- 15 – Severe attack – Nenhuma chance de falso positivos. Uma ação imediata é necessária.
Grupos
- invalid_login
- authentication_success
- authentication_failed
- connection_attempt
- attacks
- adduser
- sshd
- ids
- firewall
- squid
- apache
- syslog
Opções de configuração
Abaixo segue a descrição das principais chaves que são utilizadas na hora de criar nossas regras, existem mais opções disponíveis, porém para não deixar o post mais longo do que ele já está eu deixei só as que achei mais importantes, no final tem um link para o manual oficial com todas as opções.
<rule>
- Define uma nova regra.
- Atributos:
- level
- Especifica o grau de criticidade da regra. Alertas e respostas usam esse valor.
- Permitido: Qualquer número de 0 até 16.
- id
- Especifica o ID da regra.
- Permitido: Qualquer número de 100 até 99999.
- maxsize
- Especifica o tamanho máximo do evento.
- Permitido: Qualquer número de 1 até 99999.
- frequency
- Especifica o número de vezes que uma regra deve ocorrer antes de ser executada.
- Permitido: Qualquer número de 1 até 999.
- timeframe
- Especifica o intervalo de tempo em segundos.
- Permitido: Qualquer número de 1 aé 9999.
- ignore
- Especifica o tempo em segundos que a regra será ignorada depois dela ser executada, assim floods são evitados.
- Permitido: Qualquer número de 1 até 9999.
- level
<match>
- Qualquer string para “bater” com os logs.
- Permitido: Qualquer regex de expressão regular da lista no início do post.
<regex>
- Qualquer regex para “bater” com os logs.
- Permitido: Qualquer regex de expressão regular da lista no início do post.
<decoded_as>
- Qualquer nome de decoder já declarado antes.
- Permitido: Qualquer nome de decoder.
<category>
- Qualquer categoria de decoder que dê match (ids, syslog, firewall, web-log, squid or windows).
- Permitido: Qualquer categoria.
<if_sid>
- Checa se o ID descrito deu match.
- Permitido: Qualquer ID de regra.
<same_source_ip>
- Especifica que o IP de origem deve ser o mesmo. Essa opção é utilizada em conjunto com os atributos ‘frequency’ e ‘timeframe’.
<same_source_port>
- Especifica que a porta de origem deve ser a mesma. Essa opção é utilizada em conjunto com os atributos ‘frequency’ e ‘timeframe’.
<same_dst_port>
- Especifica que a porta de destino deve ser a mesma. Essa opção é utilizada em conjunto com os atributos ‘frequency’ e ‘timeframe’.
<same_location>
- Especifica que a localização deve ser a mesma. Essa opção é utilizada em conjunto com os atributos ‘frequency’ e ‘timeframe’.
<same_user>
- Especifica que o usuário deve ser o mesmo. Essa opção é utilizada em conjunto com os atributos ‘frequency’ e ‘timeframe’.
<description>
- Especifica a descrição da regra.
- Permitido: Qualquer string.
<options>
- Opções adicionais da regra.
- Permitido:
- alert_by_email
- Sempre alerta por email.
- no_email_alert
- Nunca alerta por email.
- no_log
- Não “loga” o alerta.
- alert_by_email
<check_diff>
- Usado para determinar quando o output de um comando muda.
<group>
- Adiciona um grupo adicional ao alerta.
Para ver mais opções acesse a página do manual sobre opções das regras.
Bem pessoal é isso ai, espero que tenham curtido. Em breve irei postar outros artigos da série Ossec mostrando mais configurações avançadas. Não esqueçam de curtir nossas páginas nas redes sociais, Facebook, G+ e seguir o Guia do Ti no Twitter. Compartilhem e comentem esse artigo, isso é muito importante para divulgação do nosso trabalho.
- Metasploit Framework de cabo a rabo – Parte 6 - 4 de junho de 2018
- Metasploit Framework de cabo a rabo – Parte 5 - 28 de maio de 2018
- CEH – Scanning Networks – Parte 2 - 24 de maio de 2018