Integrando o AbuseIPDB ao Wazuh com cache de informações

O cenário é o seguinte:

  • Debian 12.4 com kernel 6.1.0-18-amd64 virtualizado em VirtualBox 6.1.50
  • Wazuh versão 4.7.2 (Manager, Indexer e Dashboard no mesmo servidor)

..::| Principais características dessa integração

Essa integração é diferente da que está no blog do Wazuh (link).

  • Evita realizar múltiplas consultas seguidas do mesmo IP em um curto intervalo de tempo, isso ajudará a prevenir que o limite diário de consultas seja atingido prematuramente.
  • Faz cache local das consultas, salvando-as em um banco da dados SQLite 3.
  • Informações dos IPs salvos em cache expiram após um tempo configurável, obrigando a uma nova consulta no API do AbuseIPDB após expirar.
  • Tem gerenciador próprio de chaves de API, podendo usar mais de uma.
  • Quando uma chave de API tem seu limite diário excedido, evita usá-la novamente e tentará outra, caso você tenha cadastrado mais de uma.
  • Volta a usar uma chave de API, que excedeu o limite diário de uso, após a próxima meia-noite (UTC).
  • Baixa uma blacklist do AbuseIPDB.
  • Verifica na blacklist se já existe o IP a ser pesquisado antes de consultá-lo na API do AbuseIPDB (essa verificação na blacklist tem que ser ativada).
  • No alerta do AbuseIPDB, várias informações do alerta original são exibidas para facilitar a análise.
  • Procura por IPv4 ou IPv6 em vários campos possíveis, não apenas o “srcip”.
  • Possibilidade de adicionar mais campos do alerta onde é possível ter um IP público.
  • Não faz consultas de IPs que não sejam públicos.

..::| Pré-requisitos

  • Ter o Python 3 instalado. Normalmente ele vem com o Wazuh e fica em /var/ossec/framework/python/bin/python3. Se precisar alterar o caminho do executável “python3“, altere o valor da variável “PYTHON_EXEC” do arquivo “custom-abuseipdb” e o shebang do “custom-abuseipdb.py“.
  • Pacotes do Python necessários: sys, json, time, os, fcntl, re, random, datetime, traceback, subprocess, requests, sqlite3 e ipaddress.

..::| Instalação

Faça o download do script principal de integração:

sudo wget https://raw.githubusercontent.com/marciuscosta/abuseipdb-wazuh-integration/main/custom-abuseipdb.py -O /var/ossec/integrations/custom-abuseipdb.py

Agora do script de facilitação de uso:

sudo wget https://raw.githubusercontent.com/marciuscosta/abuseipdb-wazuh-integration/main/custom-abuseipdb -O /var/ossec/integrations/custom-abuseipdb

Esse script só irá facilitar o uso do “custom-abuseipdb.py” na hora de administrar chaves de API e baixar blacklist, mais adiante.

No Wazuh, para um script customizado de integração ser aceito pelo sistema, o seu nome de arquivo precisa começar com “custom-” e ele deve estar dentro do diretório /var/ossec/integrations/.

Altere suas permissões com os seguintes comandos:

sudo chmod 750 /var/ossec/integrations/custom-abuseipdb
sudo chown root:wazuh /var/ossec/integrations/custom-abuseipdb
sudo chmod 750 /var/ossec/integrations/custom-abuseipdb.py
sudo chown root:wazuh /var/ossec/integrations/custom-abuseipdb.py

Para configurar a integração desse script Python no Wazuh, antes de tudo você precisará definir quais regras, ou grupo de regras, serão usadas para chamar essa integração.

No exemplo que citarei abaixo, irei usar um grupo de regras específico para essa integração. Ou seja, sempre que uma regra estiver nesse grupo de regras, que chamarei de “abuseipdb_integration“, e ela for usada em algum alerta, o conteúdo completo desse alerta será enviado para o “custom-abuseipdb.py” processar. O resultado do processamento desse script será outro alerta, que veremos mais adiante.

Abra o /var/ossec/etc/ossec.conf e inclua essa integração abaixo dentro de “<ossec_config> … </ossec_config>“:

<integration>
    <name>custom-abuseipdb.py</name>
    <group>abuseipdb_integration,</group>
    <alert_format>json</alert_format>
</integration>

<localfile>
    <location>/var/ossec/logs/integrations.log</location>
    <log_format>syslog</log_format>
</localfile>

Note que nessa integração, diferente da que foi publicada no blog do Wazuh, não há os campos “<hook_url>” e “<api_key>“, pois a URL da API do AbuseIPDB e a(s) chave(s) de API que você irá usar não ficarão aqui, mas dentro do próprio script (no caso da URL da API) e no banco de dados de cache de informações dessa integração (no caso da(s) chave(s) de API), que veremos mais adiante em detalhes. Toda a administração de chave(s) de API será feita pelo script “custom-abuseipdb.py“.

Você também poderá citar regras específicas para integração, ao invés do grupo de regras. Basta tirar o “<group>abuseipdb_integration,</group>” e colocar “<rule_id>ID_DA_REGRA</rule_id>” com as IDs separadas por vírgula. Ambos não funcionam juntos na mesma integração, a menos que um ID citado em “<rule_id>” faça parte de um grupo de regras que está citado em “<group>” também na mesma integração.

O Wazuh irá monitorar o log /var/ossec/logs/integrations.log, que é o local onde ficam salvos os resultados da integração. Se quiser, você poderá alterar esse local, abrindo o “custom-abuseipdb.py” e informando o novo arquivo de log na variável “log_file“. Porém, lembre-se de alterar a “<location>” das configurações de integração no ossec.conf (que fizemos acima) para esse novo local.

Antes de criarmos a primeira regra do novo grupo “abuseipdb_integration“, vamos adicionar a chave API do AbuseIPDB que temos.

Caso você não tenha uma chave de API ainda, precisará criar uma agora. Primeiro, crie uma conta (caso já não tenha) no https://www.abuseipdb.com/pricing que é muito simples. Quando você já tiver a sua conta, acesse-a e vá no menu com o nome que você deu a essa conta, que fica no canto superior direito da tela, clique nele e depois em “Account“. Na página inicial que irá abrir, clique na aba “API” e depois em “Create Key” (caso você já não tenha uma chave API):

Dê um nome para a sua chave, depois clique em “Create“. Copie a chave que irá aparecer no campo “Key” e a salve em local seguro.

Perceba que o limite de “checks” por dia é de 1000 na conta gratuíta. Se você tiver 2 ou mais contas no AbuseIPDB, por exemplo, poderá criar 2 ou mais chaves de API e, com isso, terá um limite diário de 2000 (ou mais) consultas a essa API. O script de integração fará a administração automaticamente dessas chaves. Ou seja, quando uma exceder o limite diário, ele marcará ela para não ser usada até a próxima meia-noite (UTC – o que dá 21:00 no horário de Brasília), passando a usar a outra chave de API disponível. Na próxima meia-noite (UTC) as contagens de uso dessas chaves serão zeradas pelo próprio AbuseIPDB. Não tenho informações sobre se há limites de consultas por um IP de origem, mas em meus testes eu consegui fazer 2 mil consultas com 2 chaves de API diferentes, usando um mesmo IP de origem, e não fui bloqueado e nem a minha conta foi invalidada de alguma forma.

Adicione a chave de API criada ao banco de dados de cache local do script, com o seguinte comando:

sudo /var/ossec/integrations/custom-abuseipdb apikey add COLE_SUA_CHAVE_AQUI

Para listar as chaves de API salvas no banco de dados de cache local, digite o comando:

sudo /var/ossec/integrations/custom-abuseipdb apikey list

Perceba que na direita há o campo “Usable after“. Significa que essa chave de API estará apta para uso após esse dia e horário (com base no relógio desse servidor). Se a data estiver a frente do momento atual, significa que ela excedeu o limite diário de consultas na API, e poderá ser usada novamente somente após a próxima meia-noite do fuso horário UTC, o que será às 21:00 no fuso-horário de Brasília. No entanto, para evitar problemas de dessincronização do horário atual do servidor com o de Brasília, coloquei 2 minutos além da meia-noite (que, no horário de Brasília, é 21:02).

Se a chave de API for considerada inválida pelo AbuseIPDB, o campo “Usable after” estará com o valor “INVALID” logo após o script de integração tentar usá-la. Em nenhum momento essa chave será usada novamente na integração.

Para remover uma chave de API do banco de dados de cache local, use o comando (mas não a remova agora pois iremos usá-la nos testes mais adiante):

sudo /var/ossec/integrations/custom-abuseipdb apikey remove CHAVE_API_A_SER_EXCLUÍDA

Agora vamos instalar os decoders que irão extrair as informações oriundas da API do AbuseIPDB, possibilitando criar regras com base nessas informações. Execute essa linha de comandos:

sudo wget https://raw.githubusercontent.com/marciuscosta/abuseipdb-wazuh-integration/main/1600-abuseipdb-integration_decoders.xml -O /var/ossec/etc/decoders/1600-abuseipdb-integration_decoders.xml && sudo chmod 660 /var/ossec/etc/decoders/1600-abuseipdb-integration_decoders.xml && sudo chown wazuh: /var/ossec/etc/decoders/1600-abuseipdb-integration_decoders.xml

Agora vamos criar algumas regras que irão gerar alertas com base nos resultados da integração com o AbuseIPDB. Para ficar organizado, vamos criar o arquivo /var/ossec/etc/rules/1600-abuseipdb-integration_rules.xml onde ficarão essas regras, com o seguinte conteúdo:

<group name="abuseipdb,">

    <rule id="110000" level="3">
            <decoded_as>abuseipdb</decoded_as>
            <description>AbuseIPDB: Integration message.</description>
    </rule>


    <rule id="110001" level="3">
            <if_sid>110000</if_sid>
            <field name="abuseipdb.ip.score">\d+</field>
            <description>AbuseIPDB: Informations about IP $(srcip).</description>
    </rule>

    <!-- Caso o IP tenha um confidence score igual ou maior a 70 -->
    <rule id="110002" level="12">
            <if_sid>110001</if_sid>
            <field name="abuseipdb.ip.score" type="pcre2">^((7|8|9)\d|100)</field>
            <description>AbuseIPDB: IP $(srcip) have $(abuseipdb.ip.score) confidence score.</description>
    </rule>

    <rule id="110003" level="7">
            <if_sid>110000</if_sid>
            <field name="abuseipdb.error_message">\.+</field>
            <description>AbuseIPDB: Integration error message.</description>
    </rule>

    <rule id="110004" level="7">
            <if_sid>110003</if_sid>
            <field name="abuseipdb.error_message">All API key\(s\) stored in local cache database has been exceeded daily limit or invalid</field>
            <description>AbuseIPDB: All saved API key(s) are invalid or have exceeded the daily limit.</description>
    </rule>

</group>

Ajuste as permissões desse arquivo:

sudo chmod 660 /var/ossec/etc/rules/1600-abuseipdb-integration_rules.xml && sudo chown wazuh: /var/ossec/etc/rules/1600-abuseipdb-integration_rules.xml

Agora vamos criar algumas regras que irão chamar essa integração.

Apesar do script conseguir identificar um IP público, e com isso ele evita fazer pesquisas na API do AbuseIPDB quando não é um IP público, é bom evitar que esse script de integração seja executado em situações que não há necessidade de fazer consulta de um IP através dessa integração. Vamos criar regras que filtrem os IPs, identificando apenas os públicos. Isso evitará consumo desnecessário de recursos de processamento do servidor.

Vou usar o arquivo /var/ossec/etc/rules/local_rules.xml para criar algumas regras de exemplo. Logo abaixo da regra 100001, insira:

  <var name="filter_non-public_ip">10\.\d+\.\d+\.\d+|192\.168\.|172.(1[6-9]|2[0-9]|3[0-1])\.\d+\.\d+|127\.\d+\.\d+\.\d+|169.254.\d+\.\d+|244\.\d+\.\d+\.\d+|0{0,3}(|:){0,6}0{0,3}(::|:)0{0,3}1|fe8(0|):\S+|fc(00|0|):\S+|fd(00|0|):\S+</var>

  <rule id="100002" level="5">
    <if_sid>5760</if_sid>
    <match negate="yes" type="pcre2">(?i)Failed password for \S+ from $filter_non-public_ip port \d+</match>
    <description>sshd: Authentication failed from a public IP address $(srcip).</description>
    <group>authentication_failed,authentication_success,pci_dss_10.2.4,pci_dss_10.2.5,abuseipdb_integration,</group>
  </rule>

  <rule id="100003" level="5">
    <if_sid>5715</if_sid>
    <match negate="yes" type="pcre2">(?i)Accepted password for \S+ from $filter_non-public_ip port \d+</match>
    <description>sshd: Authentication succeeded from a public IP address $(srcip).</description>
    <group>authentication_failed,authentication_success,pci_dss_10.2.4,pci_dss_10.2.5,abuseipdb_integration,</group>
  </rule>

Observe que as duas regras estão no grupo “abuseipdb_integration“. Então, quando usadas em um alerta, chamará a integração “custom-abuseipdb.py“, que procurará informações desse IP localmente no banco de dados de cache dessa integração e também na blacklist (se habilitada – veremos mais adiante como ela funcionará). Se as informações sobre o IP do alerta não forem encontradas localmente, ou se as suas informações em cache estiverem muito antigas (esse tempo de expiração de informação no cache é configurável), fará uma consulta na API do AbuseIPDB e o seu resultado ficará salvo no banco de dados de cache da integração para evitar consultas repetidas do mesmo IP logo após a primeira, economizando “checks” diários da API.

Vamos carregar essas novas regras, decoders e ativar a integração reiniciando o Wazuh Manager.

sudo /var/ossec/bin/wazuh-control restart

..::| Primeiros testes

Vou criar um arquivo de log para inserir eventos de autenticação de SSH, porém usando IPs públicos, justamente para chamar a integração.

Então vou criar o arquivo de log /var/log/testes.log primeiro e ajustarei suas permissões:

sudo touch /var/log/testes.log && sudo chmod 662 /var/log/testes.log

Agora vamos configurar o seu monitoramento no /var/ossec/etc/ossec.conf dentro da seção “<ossec_config> … </ossec_config>” dessa forma:

    <localfile>
        <location>/var/log/testes.log</location>
        <log_format>syslog</log_format>
    </localfile>

Para ativar o monitoramento desse log de testes, reiniciamos o Wazuh Manager:

sudo /var/ossec/bin/wazuh-control restart

Agora vamos inserir um evento nesse testes.log, porém com IP público:

sudo echo '2024-02-25T15:28:09.658271-03:00 srv-wazuh sshd[64497]: Failed password for root from 159.223.85.11 port 57414 ssh2' >> /var/log/testes.log

Agora, se tudo deu certo, aparecerá esse evento no Wazuh Dashboard, e outro alerta do AbuseIPDB, fornecendo informações sobre o IP de origem desse evento teste.

Veja como ficaram as informações do alerta gerado pela integração:

Observe o campo “data.abuseipdb.alert.ID“. Nele há o ID do alerta que chamou essa integração, que no caso foi a falha de autenticação SSH. Podemos buscar o alerta original filtrando por essa ID, dessa forma:

..::| Usando a blacklist

É possível usar esse script de integração para baixar e usar uma blacklist do AbuseIPDB. Na conta gratuíta, essa blacklist vem limitada a 10 mil IPs e pode ser baixada até 5 vezes no mesmo dia.

Por padrão, o uso da blacklist vem desabilitado no script de integração. Para habilitar, edite o arquivo “custom-abuseipdb.py” e altere o valor da variável “blacklist_enabled” para “True“.

Ao ativar o uso de blacklist na integração, os IPs a serem consultados serão pesquisados primeiro no banco de dados de cache local e, se não encontrado, a pesquisa será feita na blacklist, que será baixada automaticamente caso não exista localmente.

Se quiser baixá-la manualmente antes, execute esse comando:

sudo /var/ossec/integrations/custom-abuseipdb blacklist

Somente se o IP a ser consultado não estiver no banco de dados de cache local e na blacklist é que ele será consultado diretamente na API do AbuseIPDB.

No entanto, as informações sobre um IP na blacklist são limitadas apenas aos campos “IP“, “Country“, “Score” e “Last Reported“. Como são menos informações do que as que são provenientes da API, por esse motivo o script irá procurar primeiro no banco de dados de cache local.

Recomendo colocar para baixá-la automaticamente 1 vez ao dia. Por exemplo, você pode colocar o script para baixar a blacklist todos os dias a meia-noite, inserindo a seguinte linha no seu crontab (coloque no usuário root por causa das permissões de execução):

0   0   *   *   *   /var/ossec/integrations/custom-abuseipdb blacklist

..::| Customizações

Você poderá alterar várias coisas nesse script. Ao abrir o “custom-abuseipdb.py” você observará a seção “Global variables definitions“, onde é possível alterar várias coisas, entre elas:

  • ip_fields” = É uma lista com os nomes dos campos, do alerta que o Wazuh envia para a integração, onde geralmente estão os IPs. Você pode adicionar mais campos apenas alimentando a lista, colocando entre aspas simples separando por vírgula. Os campos devem estar dentro da chave “data” do JSON do alerta, ou em alguma das suas possíveis subchaves. O script pegará todas as informações de todos os campos dessa lista que ele encontrar no alerta, não apenas o primeiro que encontrar.
  • abuseipdb_local_cache_file” = Local do arquivo com o banco de dados de cache das informações obtidas de consultas a API do AbuseIPDB. Nesse arquivo também há as chaves de API salvas para uso do próprio script.
  • log_file” = Local onde ficarão salvos os eventos provenientes da integração. Aqui ficarão todas as informações obtidas a respeito do IP pesquisado, e também informações sobre o alerta original, ou seja, o que chamou a integração inicialmente. Sobre o alerta original, foram excluídos os campos “full_log” e “previous_output” para evitar loop de decodificação pelo Wazuh. Aqui também ficarão outras mensagens a respeito da execução do script, como por exemplo algum erro ou informação importante.
  • blacklist_enabled” = Se estiver como “True“, verificará na blacklist se a informação a respeito do IP pesquisado é encontrado. Se ainda não foi feito o download da blacklist, o script fará automaticamente no primeiro uso e somente dessa vez. Por isso é importante atualizar diariamente a blacklist, colocando na cron (como mencionei anteriormente). A intenção é evitar fazer pesquisas na API. Se estiver como “False“, a blacklist não será usada pela integração e nem será feito o download automaticamente no primeiro uso.
  • blacklist_file” = Local onde ficará salva a blacklist ao ser baixada pelo script.
  • confidenceMinimum” = É o valor mínimo de scores dos IPs que farão parte da blacklist. Em contas gratuítas, é recomendado não alterar isso.
  • ip_expiration_time” = Tempo de armazenamento das informações de um IP no banco de dados de cache local. Esse valor deverá ser configurado em segundos. O valor padrão é de 86400, ou seja, 24 horas. Nesse caso, se um IP tiver sido inserido no banco de dados de cache local há mais de 24 horas, o script atualizará essa informação nesse banco de dados fazendo uma consulta a API do AbuseIPDB assim que esse IP aparecer em algum alerta que chamará a integração novamente.

..::| Conclusões

Com essa integração, informações muito importantes a respeito de um IP ficarão fáceis de serem consultadas. Não haverá a necessidade de pesquisar cada IP manualmente quando ele for um suspeito.

Além disso, fazer regras com base no score do IP ficou muito fácil. Como mostrei no exemplo da regra 110002, onde um alerta será gerado com level 12 quando um IP consultado no AbuseIPDB tiver um score igual ou maior que 70. Você também poderá criar um active-response para bloquear um IP com base nesse score. Podendo, por exemplo, criar uma regra onde, se o IP consultado tiver 100 de confidence score, ativará o active-response para bloqueá-lo em seus firewalls de borda de rede. Nesse exemplo, seria praticamente impossível um IP com 100 de score não ser malicioso, então as chances de um bloqueio com base num alerta falso-positivo serão praticamente nulas.

Agora você também poderá fazer regras com base em outras informações. Por exemplo, quando o IP for de um determinado país, você poderá criar uma regra com base nessa informação, assim como em outras informações como de qual ISP é esse IP, quantos registros de atos suspeitos ele tem, se ele pertence a rede TOR, se está em uma whitelist (para evitar falsos-positivos), etc.

Uma recomendação: configure a rotação do log /var/ossec/logs/integrations.log com o logrotate do Linux ou similar, pois o Wazuh não rotaciona esse log automaticamente e ele pode ficar muito grande, dependendo da sua infra.

Com certeza as informações passadas por essa integração ajudará bastante na tomada de decisões e na estratégia de segurança da sua empresa.

Fontes:

https://documentation.wazuh.com/current/user-manual/reference/ossec-conf/integration.html
https://wazuh.com/blog/detecting-known-bad-actors-with-wazuh-and-abuseipdb/

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *