| Vetor de Ataque / Gargalo | Sintoma nos Logs / Processos | Causa Técnica | Ação de Mitigação |
|---|---|---|---|
| Flood HTTP L7 | Múltiplos workers lsphp em 50% CPU | Scans em xmlrpc.php e /?author=1 | Bloquear rota no WAF e banir IPs atacantes |
| DoS TLS no Dovecot | Conexões pendentes em pop3-login/imap | Conexões TCP abertas sem envio de handshake TLS | Ajustar login_process_per_connection e timeouts |
| Sobrecarga WAF | Processo python3 do Imunify360 em 56% | Inspeção profunda de milhares de requisições L7 | Dropar tráfego nas camadas de rede (Ipset / CSF) |
| Risco de Falso Positivo | Googlebot identificado em requisições | IPs da rede 66.249. misturados no tráfego | Whitelist 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:
- 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.
- Conexões presas por inatividade: Sockets abertos por até 179 segundos sem tráfego, drenando descritores de arquivo.
- Consultas a usuários curtos: Tentativas com nomes sem domínio forçavam o
auth-workera 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#
- Nunca use sort em fluxos contínuos: Em comandos com
tail -f, utilizeawk '!x[$0]++'para manter a saída reativa sem esperar por EOF. - 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.
- 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:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários