Pontue ameaças DNS com VirusTotal

O que é e por que usar

Pontuar ameaças DNS com VirusTotal significa consultar, para cada domínio ou registro DNS que aparece no seu ambiente, o veredito de dezenas de motores de reputação agregados pela plataforma — e transformar esse veredito em um número que você possa ordenar, filtrar e priorizar. Em vez de abrir o site do VirusTotal manualmente para cada hostname suspeito, você envia o domínio (ou o hash, ou a URL) para a API v3 e recebe de volta a contagem de detecções, as categorias atribuídas por fornecedores como Fortinet, Sophos e BitDefender, o histórico de resolução e os registros DNS passivos associados.

O uso prático aparece quando você precisa responder perguntas como: “dos 4.300 domínios que apareceram nos logs de DNS do último mês, quais têm reputação suja?” ou “o subdomínio que acabou de ser criado dentro do meu domínio corporativo já foi sinalizado por algum fornecedor?”. Fazer isso na mão é inviável — a API gratuita do VirusTotal permite 4 requisições por minuto e 500 por dia, o que já é suficiente para uma varredura incremental bem desenhada, mas não para cliques manuais em planilha.

A pontuação em si não é um número oficial do VirusTotal: ela é uma métrica que você define (por exemplo, detecções ÷ total de motores × 100) para conseguir comparar domínios entre si e definir limiares de ação. É essa pontuação que permite automatizar decisões — bloquear, alertar ou apenas registrar para revisão.

Black and white close-up of a dictionary page focusing on the definition of 'virus'.

Pré-requisitos

  • Conta no VirusTotal (gratuita ou paga) e uma API key obtida em virustotal.com → perfil → API Key.
  • n8n instalado (self-hosted ou cloud) em versão 1.x ou superior.
  • Noções básicas de HTTP Request, JSON e expressões no n8n (uso de {{ $json.campo }}).
  • Uma lista de domínios para analisar — pode vir de um CSV, de um banco, de um export do seu DNS sinkhole (Pi-hole, AdGuard, NextDNS) ou de logs do Cloudflare Gateway.
  • Um destino para o resultado: Google Sheets, Notion, Slack, e-mail ou uma tabela no Postgres.
  • Entendimento dos limites de rate: API pública = 4 req/min; API premium = 500 req/min. Isso define o desenho do fluxo.

Exemplo Prático: auditoria semanal de subdomínios sombra em uma agência de e-commerce

Cenário: a agência LojaDireta gerencia 62 lojas virtuais hospedadas em Shopify, WooCommerce e Vercel. Toda semana aparecem subdomínios novos — alguns legítimos (campanhas, testes A/B), outros criados por estagiários via painel de DNS sem aviso, e alguns que são typosquatting de marcas dos clientes (ex.: lojadireta-promo.shop, cliente-oficial-br.com). A equipe de segurança não tem SIEM e precisa de um relatório semanal que mostre, para cada domínio descoberto, quantos motores o sinalizaram e qual a categoria atribuída.

O que será automatizado:

  1. Um Cron semanal (segunda-feira, 06h) lê uma planilha Google Sheets chamada dominios_monitorados com as colunas dominio, cliente, origem.
  2. Para cada linha, o n8n chama a API do VirusTotal (/domains/{dominio}) com Wait de 16 segundos entre chamadas (respeitando 4 req/min).
  3. O retorno é transformado em uma linha com: dominio, deteccoes, total_motores, pontuacao, categorias, reputacao, ultima_analise.
  4. Domínios com pontuação ≥ 5% ou reputacao < 0 disparam um alerta no Slack #sec-ops com menção ao responsável pelo cliente.
  5. O relatório completo é gravado em uma segunda aba da planilha (resultados_2025_Wxx) e enviado por e-mail em CSV para o gestor de contas.

Resultado esperado: na primeira execução, a agência descobriu que 3 dos 62 domínios tinham pontuação acima de 20% — dois eram clones de lojas (phishing ativo) e um era um domínio legítimo de cliente que havia sido comprometido e estava servindo redirecionamento malicioso. O relatório semanal passou a rodar em 14 minutos (62 domínios × 16s de espera + processamento), sem intervenção humana.

Configuração Passo a Passo

1. Obter e testar a API key do VirusTotal

Acesse sua conta no VirusTotal, copie a chave e teste com um domínio conhecido antes de montar o fluxo:

curl -X GET "https://www.virustotal.com/api/v3/domains/google.com" \
  -H "x-apikey: SUA_API_KEY_AQUI"

Resposta resumida (campos relevantes):

{
  "data": {
    "id": "google.com",
    "type": "domain",
    "attributes": {
      "last_analysis_stats": {
        "harmless": 71,
        "malicious": 0,
        "suspicious": 0,
        "undetected": 22,
        "timeout": 0
      },
      "reputation": 542,
      "categories": {
        "Forcepoint ThreatSeeker": "search engines",
        "Sophos": "search engines"
      },
      "last_analysis_date": 1730995200,
      "registrar": "MarkMonitor Inc."
    }
  }
}

2. Criar o fluxo no n8n — nós e configurações

Nó 1 — Schedule Trigger
Tipo: Schedule Trigger. Parâmetro: Cron Expression = 0 6 * * 1 (toda segunda às 06h).

Nó 2 — Google Sheets (Read)
Tipo: Google Sheets. Operation: Read Rows. Document: dominios_monitorados. Sheet: lista. Range: A2:C1000. Marque Header Row.

Nó 3 — Split In Batches
Tipo: Split In Batches. Batch Size: 1. Isso força o processamento sequencial, essencial para não estourar o rate limit.

Nó 4 — HTTP Request (VirusTotal)
Tipo: HTTP Request. Method: GET. URL:

https://www.virustotal.com/api/v3/domains/{{ $json.dominio }}

Authentication: Header Auth. Name: x-apikey. Value: sua chave (use Credential, não hardcode). Options → Response → Include Response Headers and Status: ligado (para capturar 429). Continue On Fail: ligado.

Nó 5 — Code (calcular pontuação)
Tipo: Code. Language: JavaScript. Cole o snippet abaixo:

const dominio = $('Split In Batches').item.json.dominio;
const resp = $json;

if (!resp || !resp.data) {
  return [{ json: { dominio, erro: 'sem_resposta', pontuacao: null } }];
}

const stats = resp.data.attributes.last_analysis_stats || {};
const malicious = stats.malicious || 0;
const suspicious = stats.suspicious || 0;
const total = malicious + suspicious + (stats.harmless || 0)
            + (stats.undetected || 0) + (stats.timeout || 0);

const pontuacao = total > 0
  ? Number((((malicious + suspicious) / total) * 100).toFixed(2))
  : null;

const categorias = Object.values(resp.data.attributes.categories || {});

return [{
  json: {
    dominio,
    deteccoes: malicious + suspicious,
    total_motores: total,
    pontuacao,
    reputacao: resp.data.attributes.reputation,
    categorias: [...new Set(categorias)].join(' | '),
    ultima_analise: new Date(resp.data.attributes.last_analysis_date * 1000)
      .toISOString().slice(0, 10)
  }
}];

Nó 6 — IF (separar ameaças)
Tipo: IF. Condição: {{ $json.pontuacao }} >= 5 OR {{ $json.reputacao }} < 0. Saída true → alerta; false → apenas registro.

Nó 7 — Slack (alerta)
Tipo: Slack. Operation: Send Message. Channel: #sec-ops. Text:

:warning: Domínio suspeito detectado
*Domínio:* {{ $json.dominio }}
*Pontuação:* {{ $json.pontuacao }}% ({{ $json.deteccoes }}/{{ $json.total_motores }} motores)
*Reputação:* {{ $json.reputacao }}
*Categorias:* {{ $json.categorias }}

Nó 8 — Wait
Tipo: Wait. Amount: 16 segundos. Conecte de volta ao nó 3 (loop). Esse é o detalhe que separa um fluxo que funciona de um que toma 429 Too Many Requests na terceira execução.

Nó 9 — Google Sheets (Append)
Tipo: Google Sheets. Operation: Append Row. Document: dominios_monitorados. Sheet: resultados. Mapeie as colunas com os campos do nó 5.

3. Testar com um domínio malicioso conhecido

Use testsafebrowsing.appspot.com ou qualquer domínio do feed público do PhishTank para validar que a pontuação sobe acima de zero. Se todos os domínios retornarem pontuacao = 0, o mapeamento de campos está errado — verifique last_analysis_stats no retorno bruto.

4. Agendar o envio do relatório consolidado

Adicione um segundo Schedule Trigger para terça-feira 08h que lê a aba resultados, gera um CSV com o nó Convert to File e envia por e-mail via Gmail ou SMTP para o gestor.

Dicas e Variações

  • Pontue também URLs, não só domínios. O endpoint /urls exige base64 URL-safe do endereço completo. Útil para detectar links em campanhas de e-mail antes do disparo.
  • Use o endpoint /domains/{dominio}/resolutions para pegar o histórico de IPs. Domínios que trocaram de IP mais de 5 vezes em 30 dias merecem atenção mesmo com pontuação baixa.
  • Cache local de 30 dias. Grave o resultado em um Postgres e consulte antes de chamar a API. Domínios estáveis não precisam ser reconsultados toda semana — economiza cota.
  • Combine com AbuseIPDB para os IPs resolvidos: um domínio com pontuação 0 mas cujo IP tem 80% de abuso é um forte sinal de comprometimento.
  • Defina limiares por cliente. Um e-commerce de alto tráfego tolera pontuação 2%; um site institucional não deveria passar de 0%.
Conceptual image featuring gold pills and syringes forming the word 'virus' on a dark backdrop.

Erros Comuns e Como Evitá-los

1. Ignorar o rate limit de 4 req/min. O erro mais frequente: montar o fluxo sem Wait e receber 429 na terceira chamada. Sempre coloque Split In Batches com batch 1 + Wait de 16s.

2. Confundir malicious com suspicious. A pontuação precisa somar os dois. Um domínio com 3 detecções suspicious e 0 malicious ainda é um problema — muitos fluxos só olham malicious e perdem phishing em estágio inicial.

3. Não tratar domínios inexistentes. Se o domínio nunca foi analisado, a API retorna 404 Not Found. Configure Continue On Fail no HTTP Request e trate no Code: if ($json.error) return [{ json: { dominio, erro: 'nao_analisado' } }].

4. Hardcodar a API key no nó. Use Credentials do n8n. Chaves em texto plano vazam em exports de workflow.

5. Achar que pontuação alta = bloqueio imediato. Falsos positivos existem (domínios recém-registrados, CDNs compartilhados). Use a pontuação para priorizar revisão humana, não para bloquear automaticamente, a menos que você tenha um segundo sinal (IP ruim, WHOIS recente, certificado emitido há menos de 48h).

6. Esquecer o fuso horário no last_analysis_date. O campo vem em Unix epoch UTC. Converta com new Date(x * 1000) antes de exibir.

Próximos Passos

Comece pequeno: pegue os 20 domínios mais críticos da sua operação, monte o fluxo com os nós 1 a 6 descritos acima e rode uma vez manualmente. Valide a pontuação contra o site do VirusTotal para 2 ou 3 domínios — se bater, você está pronto para escalar. Na semana seguinte, adicione o nó de alerta no Slack e o registro em planilha. Na terceira semana, integre o endpoint /resolutions e comece a monitorar também os IPs. A partir daí, expanda para URLs de campanhas de e-mail e para os subdomínios descobertos via Certificate Transparency (crt.sh) — esse é o próximo salto natural: descobrir o que existe antes que alguém te avise.

Gostou do conteúdo? Inscreva-se para receber as novidades:

CATEGORIES:

rotinas

Tags:

Comments are closed