Anatomia de um incidente: mitigando ataques L7 simultâneos, dos no Dovecot e gargalos de CPU
Voltar para blog

Anatomia de um incidente: mitigando ataques L7 simultâneos, dos no Dovecot e gargalos de CPU

25/10/2026 · 5 min · Cibersegurança

Vetor de Ataque / GargaloSintoma nos Logs / ProcessosCausa TécnicaAção de Mitigação
Flood HTTP L7Múltiplos workers lsphp em 50% CPUScans em xmlrpc.php e /?author=1Bloquear rota no WAF e banir IPs atacantes
DoS TLS no DovecotConexões pendentes em pop3-login/imapConexões TCP abertas sem envio de handshake TLSAjustar login_process_per_connection e timeouts
Sobrecarga WAFProcesso python3 do Imunify360 em 56%Inspeção profunda de milhares de requisições L7Dropar tráfego nas camadas de rede (Ipset / CSF)
Risco de Falso PositivoGooglebot identificado em requisiçõesIPs da rede 66.249. misturados no tráfegoWhitelist de subnets Google antes do ban em massa

O dia em que três frentes caíram ao mesmo tempo: L7, Dovecot e CPU#

Em ambientes de produção com dezenas de aplicações ativas, problemas de infraestrutura raramente ocorrem de forma isolada. Recentemente, enfrentei um incidente em cascata: múltiplos vetores de ataque simultâneos saturando a CPU do host, conexões pendentes no serviço de e-mail e o próprio agente de segurança consumindo mais de 56% de um núcleo de CPU para processar o volume de eventos.

Abaixo, compartilho o diagnóstico passo a passo, armadilhas comuns no monitoramento de logs em tempo real e os comandos aplicados para conter o tráfego malicioso.

1. O ponto de partida: alta rotatividade de processos no /proc#

O alerta inicial indicou saturação de CPU. Ao listar os processos mais pesados com ps:

PID     PPID USER     %CPU COMMAND
2360882    1 root     56.9 python3
2455792  ... client1  50.0 lsphp
2451123  ... client2  46.9 lsphp
2455756  ... client3  40.5 lsphp
   5395    1 mysql    20.6 mariadbd

Havia múltiplos processos lsphp consumindo picos de processamento e um processo python3 persistente preso em 56.9% rodando diretamente sob o PPID 1 (systemd).

Ao tentar inspecionar o arquivo /proc/PID/environ de um worker PHP específico:

cat /proc/2417685/environ
# No such file or directory

Em cenários de alta rotatividade (high churn), o processo conclui a requisição ou é reciclado pelo timeout em milissegundos. Para contornar essa volatilidade e capturar o script em execução, utilizei um loop contínuo de amostragem rápida:

while true; do 
  pids=$(pgrep -u client1 lsphp)
  for pid in $pids; do 
    if [ -d /proc/$pid ]; then 
      strings /proc/$pid/environ | grep SCRIPT_FILENAME
    fi
  done
  sleep 0.5
done

2. Primeiro vetor: ataque distribuído em camada 7 (xmlrpc.php)#

Inspecionando os processos associados a padrões comuns do WordPress (ps faux | egrep 'xml|wp-cron' | sort), a origem dos picos ficou evidente:

client1  2622104 10.2  ...  \_ lsphp:2/domains/domain1.com.br/public_html/xmlrpc.php
client2  2672865 30.3  ...  \_ lsphp:/domain2.com.br/private_html/xmlrpc.php
client3  2664014 12.9  ...  \_ lsphp:ns/domain3.com.br/private_html/xmlrpc.php
client4  2673451 28.0  ...  \_ lsphp:domain4.com/public_html/xmlrpc.php

Os processos operavam em estado R<s (Running, High Priority, Session Leader). Uma botnet distribuída executava chamadas em massa para o xmlrpc.php utilizando system.multicall em dezenas de domínios ao mesmo tempo. Cada requisição carregava o framework completo e disparava consultas no MariaDB.

Mapeamento em tempo real e alívio imediato#

Para registrar os domínios impactados sem travar o terminal:

while true; do 
    ps faux | grep '[x]mlrpc.php' | awk '{print $NF}' | while read line; do 
        if ! grep -q "$line" xmlrpc.txt 2>/dev/null; then 
            echo "$(date '+%Y-%m-%d %H:%M:%S') - $line" >> xmlrpc.txt
        fi
    done
    sleep 1
done

Para alívio imediato dos workers presos:

ps aux | grep xmlrpc.php | awk '{print $2}' | xargs kill -9

3. Segundo vetor: dos no Dovecot e a armadilha do sort em streams#

No serviço de e-mail, o maillog registrava anomalias simultâneas de conexão:

dovecot: imap-login: Disconnected: Inactivity (auth failed, 1 attempts in 179 secs): user=<user1@domain.com.br>, rip=179.x.x.57
dovecot: auth-worker: shadow(user2,186.x.x.225): unknown user
dovecot: imap-login: Disconnected: Connection closed (no auth attempts in 0 secs): rip=64.15.129.109 ... TLS handshaking: Connection closed

Foram identificados três padrões abusivos:

  1. Exaustão de TLS: Blocos inteiros de IP abriam handshakes TLS e abortavam a conexão em seguida, forçando a CPU a realizar cálculos criptográficos sem autenticar.
  2. Conexões presas por inatividade: Sockets abertos por até 179 segundos sem tráfego, drenando descritores de arquivo.
  3. Consultas a usuários curtos: Tentativas com nomes sem domínio forçavam o auth-worker a buscar entradas no /etc/shadow.

A armadilha do pipe em streams contínuos#

Ao tentar ordenar logs com tail -f /var/log/maillog | sort | uniq, o terminal congela porque o sort aguarda o fim do arquivo (EOF). Para deduplicação em tempo real em streams contínuos, use array associativo no awk:

tail -f /var/log/maillog | grep -E "auth-worker|login" | awk '!x[$0]++'

4. O consumo de 56.9% no Imunify360 (Python)#

O alto uso de CPU do python3 era um reflexo direto do volume de tráfego. O agente do Imunify360 inspecionava cada execução PHP via Proactive Defense e processava a fila de eventos do WAF/WebShield. Sob bombardeio em massa, o agente de segurança passou a disputar ciclos de CPU diretamente com as aplicações legítimas.

5. Enumeração de usuários e validação do googlebot#

Nos logs de acesso web, a botnet realizava enumeração de autores com referers falsificados de motores de busca (https://www.google.com):

GET /wp-json/wp/v2/users HTTP/1.1" 200 ... "https://www.google.com"
GET /?author=1 HTTP/1.1" 200 ... "https://www.google.com"
GET /wp-login.php HTTP/1.1" 200 ... "https://www.google.com"

Preservando os ranges legítimos do googlebot#

Ao extrair a lista de IPs atacantes, foram encontrados também endereços oficiais do rastreador do Google (66.249.64.0/19 e 2001:4860::/32). Banir esses IPs causaria desindexação em massa dos sites.

Para garantir a passagem do rastreador sem retenção por desafios de captcha:

imunify360-agent ip-list local add --ip 66.249.64.0/19 --purpose white --comment "Googlebot Official Range - Priority"
imunify360-agent ip-list local add --ip 2001:4860::/32 --purpose white --comment "Google IPv6 Range - Priority"
imunify360-agent whitelisted-crawlers add --name google

6. Pipeline de bloqueio em lote#

Com a lista sanitizada excluindo o Googlebot (grep -v), os IPs agressores foram adicionados diretamente à Blacklist local do firewall:

grep -v "66.249." resultados.txt | awk '{print $1}' | sort -u | while read ip; do
    imunify360-agent ip-list local add --ip $ip --purpose black --comment "Botnet WP Scan / Bruteforce"
done

Lições técnicas do incidente#

  1. Nunca use sort em fluxos contínuos: Em comandos com tail -f, utilize awk '!x[$0]++' para manter a saída reativa sem esperar por EOF.
  2. Monitore o impacto do WAF na CPU: Quando agentes de segurança como o Imunify360 atingem mais de 50% de CPU analisando requisições na camada 7, transfira o bloqueio para subnets e camadas mais baixas de rede.
  3. Valide antes de banir em lote: Rastreie e adicione os ranges oficiais do Googlebot na lista branca antes de disparar scripts de bloqueio em massa.

Perguntas Frequentes (FAQ)#

Por que usar sort com tail -f congela a saída do terminal?#

O comando sort precisa ler todo o fluxo de entrada até encontrar o final do arquivo (EOF) para poder ordenar as linhas alfabeticamente. Como o tail -f mantém o stream de log aberto indefinidamente, o sort retém tudo em seu buffer interno e nunca imprime nada no terminal. O substituto correto é awk '!x[$0]++'.

Como o DoS de TLS no Dovecot afeta o servidor de e-mails?#

Atacantes abrem conexões TCP nas portas 993/995 e não concluem o handshake TLS ou enviam payloads incompletos lentamente. Isso mantém processos de login (imap-login ou pop3-login) alocados na memória, exaurindo o limite de conexões simultâneas do Dovecot e impedindo que clientes legítimos baixem e-mails.

Como evitar que o Googlebot seja bloqueado por engano em ataques L7 ao WordPress?#

Muitas botnets falsificam o User-Agent do Googlebot ou usam https://www.google.com como Referer. Para evitar desindexação catastrófica, verifique o IP reverso contra os blocos oficiais do Google (66.249.64.0/19 e 2001:4860::/32) e adicione esses ranges na lista branca do WAF antes de rodar qualquer script de banimento em massa.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários