Vulnerabilidades do WordPress

Como os robôs e as ferramentas de detecção reconhecem o WordPress, e quais indícios remover

Como os robôs e o Wappalyzer reconhecem um site WordPress

Antes de testar uma falha, um robô precisa saber com o que está falando. Muitas vezes basta uma única página do seu site para ele concluir “WordPress” e, depois, algumas requisições para montar a lista dos seus plugins e das versões deles. Este artigo detalha os sinais exatos que o Wappalyzer e o WPScan usam, o que você pode remover de forma limpa e o que continuará visível, faça o que fizer.

Por que a identificação vem antes do ataque

Os ataques automatizados ao WordPress raramente miram o “seu” site. Eles miram uma versão específica de plugin, conhecida por ser vulnerável, em milhares de sites ao mesmo tempo. Para separar esses sites, um robô responde a três perguntas, nesta ordem: é WordPress? Qual a versão do núcleo? Quais plugins, e em que versão?

O WPScan ilustra bem essa lógica. Por padrão, quando procura plugins em modo agressivo, ele não testa todos os plugins existentes: tenta a lista dos plugins conhecidos por ter uma vulnerabilidade na sua base. Um site que não responde nada de útil a essas perguntas sai mais rápido da fila; um site que anuncia tudo vai parar no grupo certo.

Os sinais passivos: o que a página inicial revela

Uma ferramenta “passiva” não faz nenhuma requisição especial: ela lê o HTML, os cabeçalhos e o JavaScript da página que qualquer visitante recebe. É o caso da extensão de navegador Wappalyzer.

A tag generator

O núcleo adiciona por padrão a função wp_generator ao gancho wp_head. Ela exibe <meta name="generator" content="WordPress 6.9" /> com o número exato da versão. A mesma informação aparece nos feeds: <generator> no RSS 2, atributo version no Atom. Remover a tag do <head> sem tratar os feeds deixa, portanto, a versão acessível em /feed/.

Os caminhos /wp-content/ e /wp-includes/

Folhas de estilo, scripts, imagens: quase todos os recursos de um site WordPress passam por /wp-content/ (temas, plugins, mídia) ou /wp-includes/ (bibliotecas do núcleo). É o sinal mais robusto e, de quebra, entrega o nome dos plugins: /wp-content/plugins/nome-do-plugin/ aparece às claras no código-fonte.

O parâmetro ver=

Os scripts e estilos do núcleo são registrados com uma versão padrão igual à versão do WordPress (default_version = get_bloginfo( 'version' ) em script-loader.php). Nem todos a usam (o jQuery tem o seu próprio número), mas um script registrado sem versão, como wp-includes/js/comment-reply.min.js, é servido com ?ver= seguido da versão do núcleo, mesmo quando a tag generator já sumiu. Os plugins, por sua vez, costumam passar o próprio número de versão nesse mesmo parâmetro.

Os cabeçalhos HTTP

  • Link: <https://exemplo.com.br/wp-json/>; rel="https://api.w.org/": enviado por rest_output_link_header() nas páginas públicas para anunciar a API REST. O Wappalyzer o reconhece diretamente.
  • X-Pingback: desde o WordPress 4.4, só é enviado em um conteúdo individual (post, página) que aceita pings, e aponta para xmlrpc.php.
  • Links curtos: wp_shortlink_header() adiciona um Link do tipo shortlink nos conteúdos.

JavaScript e classes CSS

O editor de blocos deixa classes wp-block-... no conteúdo publicado. No JavaScript, objetos como wp.ajax, uma variável wp_username ou o script wp-embed.min.js bastam para o Wappalyzer. O WooCommerce acrescenta seus próprios marcadores (woocommerce_params, wc_add_to_cart_params).

Mapa dos sinais de detecção do WordPress em quatro colunas: código HTML, cabeçalhos HTTP, JavaScript e requisições ativas
As três primeiras colunas são lidas em uma página comum; a última exige requisições direcionadas.
SinalOnde verRevela a versão?Lido por
Tag generator<head> de cada páginaSim, exataWappalyzer, WPScan
Generator dos feeds/feed/, /feed/atom/Sim, exataWPScan
Caminhos wp-content, wp-includesCódigo-fonteNão (mas revela nomes de plugins)Wappalyzer, WPScan
Parâmetro ver=Endereços dos scripts e estilosMuitas vezesWPScan
Cabeçalho Link api.w.orgResposta HTTPNãoWappalyzer
X-PingbackResposta HTTP (posts)NãoWappalyzer
Variáveis JavaScriptScripts da páginaNãoWappalyzer
readme.txt de pluginRequisição dedicadaSim (Stable tag)WPScan
Impressões MD5Requisições a arquivos estáticosSimWPScan
Principais sinais, segundo a definição de WordPress do webappanalyzer e o código do WPScan.

Como o Wappalyzer decide

O Wappalyzer compara a página com uma base de definições: para cada tecnologia, uma lista de expressões regulares a procurar no HTML, nas meta tags, nos cabeçalhos, nos cookies, nos endereços de scripts e nas variáveis JavaScript. O repositório original se tornou privado em agosto de 2023; a base continua evoluindo em projetos abertos como o webappanalyzer (Enthec), que mantém o mesmo formato.

A definição “WordPress” dessa base é curta e instrutiva:

  • meta generator: ^WordPress(?: ([\d.]+))?, capturando o número da versão;
  • headers: X-Pingback terminando em /xmlrpc.php, e link contendo rel="https://api.w.org/";
  • scriptSrc e html: caminhos /wp-content/ ou /wp-includes/, wp-embed.min.js;
  • js: wp.ajax, wp.receiveEmbedMessage, wp_username;
  • implies: PHP e MySQL. Já a definição do WooCommerce exige WordPress (requires).

Consequência prática: um único sinal basta. Remover a tag generator não muda o veredito enquanto os caminhos /wp-content/ continuarem na página. Ao contrário, um site que expõe os marcadores de outro software será classificado nessa outra categoria pelo mesmo mecanismo.

Como o WPScan vai além

O WPScan é um scanner de segurança dedicado ao WordPress, usado tanto por auditores quanto por atacantes. O código-fonte dele é público, o que permite saber exatamente o que ele procura:

  • Detecção do WordPress: a página inicial e uma página 404 são examinadas (caminhos, generator, comentários HTML que mencionam WordPress, admin-ajax.php em um script embutido). Em modo misto ou agressivo, ele também carrega wp-login.php e wp-admin/install.php.
  • Versão do núcleo: generators dos feeds RSS, Atom e RDF, readme.html (baixa confiança para versões recentes) e, depois, impressões digitais: ele compara o MD5 de arquivos estáticos com uma base de somas conhecidas, com confiança de 100%.
  • Plugins: endereços vistos na página inicial e na página 404, padrões nos cabeçalhos, comentários e variáveis JavaScript e, depois, requisições para /wp-content/plugins/slug/. Uma resposta 200, 401, 403 ou 500 vale como presença.
  • Versão dos plugins: leitura do readme.txt, campo Stable tag (confiança 80) ou seção do registro de alterações (confiança 50).
Diagrama em seis etapas do andamento de uma varredura do WPScan, da página inicial ao cruzamento com uma base de vulnerabilidades
A primeira etapa é só uma visita comum; as etapas em laranja geram requisições visíveis nos seus logs de acesso.

As etapas ativas deixam rastros: dezenas de requisições para /wp-content/plugins/.../readme.txt com 404 em poucos segundos denunciam uma enumeração. É também aí que uma defesa pode agir, não respondendo 200 ou 403 para arquivos que não precisam ser públicos.

Demonstração do WPScan pela Secureaks: enumeração de plugins e versões em um site de teste.

O que você pode reduzir, e o que continuará visível

O WordPress permite remover vários anúncios com ganchos simples, por exemplo em um plugin próprio:

remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );   // feeds incluídos
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 );
remove_action( 'template_redirect', 'wp_shortlink_header', 11 );
remove_action( 'wp_head', 'rest_output_link_wp_head', 10 );
remove_action( 'template_redirect', 'rest_output_link_header', 11 );

Atenção à prioridade: remove_action() só funciona se usar a mesma prioridade do momento em que a ação foi adicionada (11 para os dois cabeçalhos). Por outro lado, não adianta procurar wlwmanifest_link: esse link do Windows Live Writer não é mais emitido pelo núcleo desde o WordPress 6.3.

SinalDefesaEfeitoLimite
GeneratorFiltro the_generatorVersão removida das páginas e feedsTemas e plugins que escrevem o próprio generator
Link api.w.orgRemoção dos dois ganchos RESTAnúncio da API eliminadoA API continua acessível em /wp-json/
ver=Reescrita ou remoção do parâmetroVersão do núcleo menos visívelPode atrapalhar a invalidação do cache do navegador
Caminhos wp-contentAliases de endereços + regras de servidorSem /wp-content/ no HTMLDelicado: CSS, JS e JSON também contêm caminhos
readme.txt de pluginsBloqueio no servidor (404)Versão do plugin mais difícil de lerImpressões de arquivos estáticos continuam possíveis
Impressões MD5Nenhuma defesa simplesSó a atualização elimina a vulnerabilidade
Reduzir a superfície de identificação: efeitos e limites.

Tenha a hierarquia em mente: esconder a versão não corrige nenhuma falha. Um robô que não consegue identificar o seu site ainda pode tentar o exploit às cegas, e alguns fazem isso. O mascaramento reduz o ruído e tira o seu site das listas de alvos selecionados; atualizações, backups e uma autenticação sólida continuam sendo a base.

Esconder o WordPress ou imitar outro software?

Remover os sinais deixa um site “sem identidade”, o que já é um indício. A outra abordagem consiste em apresentar os marcadores de outro software. É o princípio da imitação de CMS do WP Masquerade, plugin de segurança para WordPress:

FunçãoGratuitoPro
Generator do WordPress removido das páginas e dos feedsSimSim
Tag generator autêntica de outro software5 perfis (Joomla!, Drupal, PrestaShop, TYPO3, MediaWiki)41 perfis
Marcadores reconhecidos pelo Wappalyzer (cabeçalhos, variáveis JavaScript)NãoSim
Links RSD, Windows Live Writer e links curtos removidosSimSim
Anúncios da API REST e do oEmbed removidosNãoSim
Aliases para wp-content, mídia, wp-includes, plugins, temasNãoSim
Arquivos readme e traduções do plugin em 404 (Apache, LiteSpeed)SimSim
O que o WP Masquerade faz contra a identificação (segundo a documentação do produto).

Os limites estão documentados: os nomes das pastas de plugins e temas continuam presentes sob a pasta-raiz deles, os arquivos CSS e JavaScript não são reescritos globalmente, e nenhum resultado é garantido em um detector. Veja também os links técnicos e os endereços dos arquivos.

Teste o seu próprio site em dez minutos

  1. Abra uma janela anônima (desconectado) e exiba o código-fonte: procure generator, wp-content, wp-includes, ver=.
  2. Abra /feed/ e procure a tag <generator>.
  3. Leia os cabeçalhos com curl -sI https://seu-site.com.br/: há um Link para api.w.org?
  4. Rode a extensão Wappalyzer na página inicial e depois em um post.
  5. Teste /wp-content/plugins/algum-plugin/readme.txt: um 200 entrega a versão a qualquer pessoa.
  6. Depois de cada ajuste, limpe o cache de páginas: uma cópia antiga pode manter os sinais antigos.
  7. Se for rodar o WPScan, faça isso somente em um site pelo qual você é responsável.
O ponto de vista do visitante: os métodos simples para reconhecer um site WordPress.

Para lembrar

  • Um único sinal basta para o Wappalyzer: remover a tag generator não adianta enquanto /wp-content/ continuar visível.
  • A versão do núcleo também vaza pelos feeds, pelo parâmetro ver= e pelas impressões digitais de arquivos.
  • O WPScan lê o Stable tag dos readme.txt de plugins: bloqueie esses arquivos se eles responderem 200.
  • remove_action() exige a mesma prioridade da adição (11 para os cabeçalhos Link).
  • Esconder reduz o direcionamento automático, mas só as atualizações eliminam uma falha.

Fontes

Outras fontes consultadas: documentação oficial e páginas oficiais dos fornecedores.

Segurança WordPress, sem ruído

Um e-mail quando importa: novos ataques observados, atualizações do plugin e guias práticos. Sem spam, cancelamento com um clique.

Comece de graça e mude para o Pro quando estiver pronto

A versão gratuita já oculta o WordPress e muda o endereço da página de login. O Pro acrescenta aliases, verificação em duas etapas e monitoramento.