secaudit.eu
Incidentes

WordPress continua a ser hackeado? Encontre a backdoor

WordPress continua a ser infetado após a limpeza? Encontre as backdoors que sobrevivem: auto_prepend_file, mu-plugins, plugins falsos, cron e sites vizinhos.

Por , AngelForce OÜ 8 min de leitura

Também em: English Deutsch Français Español Italiano Polski Türkçe

Se o WordPress continua a ser hackeado depois de o ter limpado, o atacante quase de certeza ainda tem uma via de acesso. Ou uma backdoor sobreviveu à limpeza, ou a falha que foi usada da primeira vez continua aberta. Uma cópia nova do core e meia dúzia de ficheiros apagados não resolvem nenhuma das duas situações.

Este guia parte do princípio de que a limpeza já foi feita. Percorre os locais que costumam sobreviver-lhe, pela ordem em que o código é executado, com uma verificação concreta para cada um. Termina com o passo que realmente quebra o ciclo.

Porque é que o WordPress volta a ser hackeado depois da limpeza

Há duas causas, e muitas vezes coexistem. A primeira é a persistência. Os atacantes raramente deixam uma única backdoor: plantam várias, em sítios diferentes, para que as restantes continuem a funcionar quando uma é descoberta. A segunda é um ponto de entrada que continua aberto: um plugin vulnerável, uma palavra-passe roubada ou um site vizinho na mesma conta de alojamento.

O intervalo de tempo dá uma pista. Uma reinfeção poucas horas depois da limpeza aponta normalmente para uma backdoor que sobreviveu ou para uma tarefa agendada que reescreve ficheiros. Uma reinfeção passados dias ou semanas aponta mais para uma falha aberta que alguém, ou algum bot, voltou a encontrar.

As falhas abertas são encontradas depressa. O relatório State of WordPress Security in 2026, da Patchstack, é de um fornecedor, e deve ser lido como tal. Concluiu que, nas vulnerabilidades muito exploradas, a mediana ponderada do tempo até à exploração em massa foi de 5 horas. Cerca de metade das vulnerabilidades de grande impacto foi explorada em 24 horas. O mesmo relatório indica que 46 % das vulnerabilidades não tinham correção disponível no momento da divulgação pública. Um plugin por atualizar não é um risco pequeno que se resolve na semana seguinte.

Diagrama: o ciclo de reinfeção e as quatro camadas onde as backdoors se escondem
Cada limpeza que falha uma camada alimenta o ciclo. Só fechar o ponto de entrada e renovar todas as credenciais o quebra.

Antes de procurar: guarde primeiro as provas

Faça uma cópia completa dos ficheiros e da base de dados e guarde os registos de acesso do servidor web antes de apagar mais nada. Depois registe as datas de modificação dos ficheiros, para mais tarde conseguir cruzar as alterações com as entradas dos registos:

cd /path/to/site
find . -type f -printf '%T@ %TY-%Tm-%Td %TH:%TM %p\n' | sort -n > ~/file-times-$(date +%F).txt
wp db export ~/evidence-db-$(date +%F).sql

A exportação da base de dados contém dados pessoais, por isso transfira estas cópias para fora do servidor, para um armazenamento seguro. Caso contrário, a pesquisa de cópias de segurança mais abaixo vai listá-las e podem ser lidas ou apagadas por engano. Mantenha uma lista simples, com data, de cada ficheiro, utilizador e tarefa cron que remover. Vai precisar dela para encontrar o ponto de entrada. E, se estiverem envolvidos dados pessoais, também porque o artigo 33.º, n.º 5, do RGPD obriga a documentar todas as violações de dados pessoais, mesmo as que não são notificadas. Os passos completos de contenção estão no nosso plano de remoção de malware do WordPress.

Backdoors executadas antes de o WordPress carregar

A diretiva auto_prepend_file do PHP executa um ficheiro antes de cada pedido PHP, antes de o WordPress carregar e antes de qualquer plugin de segurança o poder ver. É precisamente por isso que os atacantes gostam dela. Pode ser definida no php.ini, num ficheiro .user.ini em qualquer diretoria ou com php_value no .htaccess, em Apache com mod_php. Pesquise a conta inteira:

grep -Rn --include='*.ini' --include='.htaccess' -E 'auto_(prepend|append)_file' ~ 2>/dev/null
php --ini

O php --ini mostra os ficheiros de configuração carregados pelo PHP na linha de comandos. O PHP do servidor web pode usar outros, pelo que deve verificar também as definições de PHP no painel do alojamento. Qualquer valor que aponte para um ficheiro que não reconhece é um achado. Anote o caminho, guarde uma cópia desse ficheiro e só depois remova a definição. As alterações ao .user.ini podem demorar alguns minutos a aplicar-se, porque o PHP mantém esses ficheiros em cache.

A seguir, verifique o wp-config.php. Um ficheiro limpo tem um único require_once ABSPATH . 'wp-settings.php'; perto do fim e nada executável acima do bloco de comentários inicial. Procure qualquer outra coisa:

head -n 5 wp-config.php
grep -nE 'eval\(|base64_decode|gzinflate|str_rot13|include|require' wp-config.php
awk 'length > 300 {print FILENAME": line "NR" is "length" chars"}' wp-config.php

Depois compare-o com diff com uma cópia boa conhecida, de uma cópia de segurança antiga. Se não tiver nenhuma cópia limpa, compare a estrutura com a do wp-config-sample.php. Includes a mais, linhas muito compridas e código antes do bloco de comentários não pertencem ali.

Esconderijos dentro de wp-content: mu-plugins e plugins falsos

Plugins must-use e drop-ins

Tudo o que está em wp-content/mu-plugins é executado automaticamente em cada pedido. O WordPress só os mostra num separador próprio, «Must-Use», no ecrã de Plugins; os ficheiros PHP em subpastas nem sequer aparecem listados, e nenhum pode ser desativado a partir do painel. Muitos sites não têm qualquer plugin must-use. Alguns alojamentos instalam um para cache ou gestão.

ls -la wp-content/mu-plugins
wp plugin list --status=must-use
wp plugin list --status=dropin

Os drop-ins são outro local a verificar: object-cache.php, advanced-cache.php e db.php, diretamente em wp-content. São legítimos quando foram colocados ali pela sua cache ou pelo alojamento. Se nada do que instalou os explica, trate-os como suspeitos.

Pastas de plugins falsos ou parecidos com os reais

Uma pasta de plugin sem cabeçalho de plugin válido não aparece no WordPress, mas os seus ficheiros PHP continuam a poder ser chamados diretamente por URL. Compare o que está no disco com o que o WordPress conhece:

ls -1 wp-content/plugins | sort > on-disk.txt
wp plugin list --field=name | sort > registered.txt
comm -23 on-disk.txt registered.txt

O index.php e plugins de ficheiro único, como o hello.php, vão aparecer no resultado. Tudo o resto merece uma análise. Desconfie de nomes que imitam plugins reais, por exemplo com um hífen a mais, um número de versão no nome da pasta ou algo como «wp-security-core». Depois execute wp plugin verify-checksums --all. Só cobre plugins alojados no WordPress.org. Nos plugins premium, descarregue uma cópia nova junto do fornecedor e compare-a com diff -r. Para o core do WordPress, siga o nosso guia sobre como verificar os checksums do WordPress.

Ficheiros PHP alterados recentemente

find wp-content -type f -name '*.php' -mtime -30 -printf '%TY-%Tm-%Td %p\n' | sort
find wp-content -type f -name '*.php' -ctime -30

Se ainda não reinstalou o core, -newer wp-includes/version.php é outro ponto de referência útil. Os atacantes conseguem falsificar as datas de modificação com touch, mas não conseguem falsificar facilmente a data de alteração do inode (-ctime), por isso execute ambos os comandos. Os ficheiros PHP na pasta uploads são tratados à parte no nosso guia sobre web shells em wp-content/uploads.

Persistência na base de dados e no agendador: cron, administradores, palavras-passe de aplicação

Os eventos do WP-Cron vivem na base de dados e sobrevivem a uma limpeza de ficheiros. Liste-os:

wp cron event list --fields=hook,next_run_relative,recurrence

Cada hook deve pertencer ao core ou a um plugin que conheça. Perante um hook desconhecido, pesquise o código com grep -Rn "hook_name" wp-content. Se o código que o regista estiver num mu-plugin ou num ficheiro que não consegue explicar, encontrou uma backdoor. Um hook sem código correspondente pode significar que a backdoor foi removida mas o evento ficou. Apague o evento com wp cron event delete hook_name.

Depois verifique o lado do servidor: crontab -l para o utilizador do alojamento, mais a secção de cron do painel de alojamento. Uma tarefa que execute curl ou wget, ou que escreva na raiz web, pode restaurar discretamente uma backdoor todas as noites.

Seguem-se as contas. Este comando lista todos os administradores e, a seguir, as palavras-passe de aplicação de cada um:

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
for id in $(wp user list --role=administrator --field=ID); do echo "User $id"; wp user application-password list "$id"; done

As palavras-passe de aplicação continuam a funcionar depois de uma alteração de palavra-passe, pelo que uma entrada desconhecida aqui é tão grave como um administrador desconhecido. A revisão completa está no nosso guia sobre funções de utilizador no WordPress.

Por fim, procure scripts injetados no conteúdo e nas definições. Substitua wp_ se o prefixo das suas tabelas for diferente:

wp db search 'script src=' wp_options wp_posts --stats
wp db search 'atob(' wp_options wp_posts --stats
wp db query "SELECT option_name, LENGTH(option_value) AS size FROM wp_options WHERE autoload IN ('yes','on','auto','auto-on') ORDER BY size DESC LIMIT 20;"

As duas primeiras pesquisas encontram inclusões de scripts externos e a descodificação base64 que o JavaScript injetado costuma usar. A consulta lista as maiores opções com carregamento automático; uma opção de nome desconhecido com um valor grande merece ser analisada com atenção.

O plugin de auditoria da secaudit.eu reúne num só local estes indicadores de comprometimento: administradores ocultos, entradas de cron suspeitas, spam na base de dados e discrepâncias nos checksums do core. Deteta e comunica. As decisões de remoção continuam a ser suas → secaudit.eu

O resto da conta de alojamento: sites vizinhos e cópias antigas

O alojamento partilhado costuma executar vários sites sob o mesmo utilizador de sistema. Uma cópia de teste (staging) infetada, uma instalação esquecida em /old/ ou /test/, ou um ZIP de cópia de segurança na raiz web voltam a infetar o site que acabou de limpar. Liste tudo o que existe na conta:

find ~ -name wp-config.php 2>/dev/null
find ~ -type d -name wp-includes 2>/dev/null
find ~ -type f \( -name '*.zip' -o -name '*.tar.gz' -o -name '*.sql' \) 2>/dev/null

Limpe todos os sites da conta ao mesmo tempo ou apague os que ninguém usa. Se todos os sites partilharem o mesmo utilizador SFTP e o mesmo utilizador de base de dados, qualquer site comprometido pode escrever em todos os outros. Sempre que possível, separe-os em utilizadores distintos ou em contas de alojamento diferentes.

Feche o ponto de entrada e renove todas as credenciais de uma só vez

Encontre o ponto de entrada nos registos de acesso, em torno da data e hora da primeira infeção. Procure pedidos POST a ficheiros estranhos e aos endpoints do plugin que suspeita:

grep '"POST ' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -30

Atualize o plugin vulnerável. Se não existir correção ou o plugin estiver abandonado, substitua-o. Se não precisa dele, apague-o. Desativar não chega, porque os ficheiros continuam no disco e podem ser acedidos por URL.

Depois renove todas as credenciais numa única sessão, depois da limpeza e não antes. Se renovar cedo demais, uma backdoor que tenha sobrevivido limita-se a ler os valores novos.

  • Todas as palavras-passe de administrador do WordPress e todas as palavras-passe de aplicação
  • Palavra-passe da base de dados: altere-a no painel do alojamento e depois atualize DB_PASSWORD no wp-config.php
  • Início de sessão no painel do alojamento, palavras-passe SFTP e chaves SSH (remova as chaves que não reconhece)
  • Chaves de API guardadas em plugins (pagamentos, SMTP, CDN, armazenamento de cópias de segurança)
  • Chaves de segurança e salts: wp config shuffle-salts

Repita as mesmas verificações 24 horas depois e novamente ao fim de 7 dias, e compare os resultados com os seus ficheiros de provas. Se um mu-plugin, um evento de cron ou um ficheiro modificado reaparecer, ainda há algo que não encontrou.

Perguntas frequentes

Quando devo deixar de fazer isto sozinho?

Pare à segunda reinfeção, ou de imediato se houver risco de dados de clientes terem sido expostos ou se o site for uma loja com encomendas. Nessa altura, recorra a uma equipa profissional de resposta a incidentes. Se estiverem em causa dados pessoais, verifique também se se aplica a notificação à autoridade de controlo no prazo de 72 horas prevista no RGPD. O nosso guia sobre a notificação de violação de dados em 72 horas explica quando começa a contar o prazo.

Basta restaurar uma cópia de segurança?

Só se a cópia for anterior à primeira backdoor e se o ponto de entrada já estiver fechado. Caso contrário, restaura o problema juntamente com o site.

Mudar todas as palavras-passe pára os ataques?

Não. Uma backdoor que sobreviveu não precisa de palavra-passe. Renove as credenciais depois da pesquisa, não em vez dela.

Este artigo é uma informação geral e não constitui aconselhamento jurídico.

Verifique o seu site

O Security Audit & Compliance é o plugin WordPress da secaudit.eu: procura malware, spam oculto, ficheiros do núcleo alterados e código vulnerável, e associa as conclusões ao art. 32.º do RGPD, ao art. 21.º da NIS2 e ao OWASP Top 10.

Ver o que o plugin verifica
Melike Görel

Melike Görel escreve os guias da secaudit.eu sobre segurança do WordPress e conformidade com as regras da UE para a AngelForce OÜ, a empresa de software de Taline responsável pela secaudit.eu.

Elaborado com apoio de IA e verificado antes da publicação. Este artigo é informação geral e não constitui aconselhamento jurídico.