Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
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.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-97332-PoC — PoC independente e laboratório reproduzível para CVE-2026-97332 — divulgação não autenticada de arquivos privados no User Private Files < 2.2.0 em WordPress Multisite. | Kitploit
Ferramentas/GitHubGitHub/kolya080808/cve-2026-97332-poc
Ferramentas DefensivasAnálise de VulnerabilidadesExploraçãoExploração de Aplicações WebColeta de InformaçõesSegurança WebTestes de PenetraçãoLabs e Prática

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 →
GitHub
kolya080808/cve-2026-97332-poc

CVE-2026-97332-PoC

PoC independente e laboratório reproduzível para CVE-2026-97332 — divulgação não autenticada de arquivos privados no User Private Files < 2.2.0 em WordPress Multisite.

Ver RepositórioSite
41há 29 diasAinda não revisado
Compartilhar

CVE-2026-97332 — WordPress User Private Files < 2.2.0 Divulgação Não Autenticada de Arquivos Privados (Multisite)

CVE Severity Affected Fixed Requires PoC Ethics

[!NOTE] Este repositório é uma reprodução independente e Prova de Conceito para CVE-2026-97332.

Descoberta original da vulnerabilidade: Akshat Parikh (SN1PER). CNA / advisory: WPScan. PoC e reprodução independentes: Kolya080808.

Divulgação não autenticada de arquivos privados de outros usuários no plugin do WordPress User Private Files (slug do wp.org user-private-files) anterior a 2.2.0 em instalações multisite. O plugin armazena os documentos privados de cada usuário dentro da árvore simples de uploads e os protege com uma única RewriteRule anexada ao .htaccess raiz, que encaminha as requisições de arquivos por meio de uma verificação de acesso em PHP. Em uma rede multisite essa regra nunca é alcançada: um simples GET — sem cookies, sem cabeçalhos, sem login — retorna o arquivo privado de outro usuário como conteúdo estático. Controle de Acesso Quebrado, CWE-284, CVSS 3.1 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N).

Este repositório contém um exploit Python funcional e sem dependências e um laboratório docker de dois contêineres (vulnerável 2.1.9 e corrigido 2.2.0, ambos WordPress multisite) para reproduzir o ataque de ponta a ponta.

[!CAUTION] Este material é fornecido para pesquisa de segurança autorizada e testes defensivos apenas (consulte o aviso legal no final). Atacar sistemas que você não possui ou opera sem permissão por escrito é ilegal.

A vulnerabilidade

O plugin mantém arquivos privados como anexos comuns em um subdiretório plano de uploads por site — wp-content/uploads/upf-docs/ (site principal) e wp-content/uploads/sites/N/upf-docs/ (subsite N) — sem isolamento por usuário. O controle de acesso não está no diretório; ele reside no PHP (dl-file.php: 403 para usuários anônimos, depois uma verificação de autor/lista de permissões) e é alcançado apenas por meio de uma regra de reescrita emitida na ativação do plugin no .htaccess raiz:

# appended by 2.1.9 (upvf_actdeact.php), AFTER the WordPress block
RewriteRule ^wp-content/uploads/upf-docs/(.*)$ https://site/?file=$1 [QSA,L]

Em multisite isto nunca é acionado, por duas razões independentes:

  1. O .htaccess da rede interceta primeiro os ficheiros existentes. O WordPress multisite escreve o seu próprio conjunto de regras no .htaccess da raiz:

    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    

    Uma requisição mapeada para um arquivo fisicamente existente termina a reescrita ali mesmo — o Apache o serve como conteúdo estático, e o mod_rewrite nunca chega ao bloco do plugin no final do arquivo. (O WordPress de site único usa condições negativas !-f/!-d, e é por isso que o mesmo plugin funciona lá.) Isso já expõe arquivos do site principal da rede.

  2. Os caminhos dos subsites não correspondem ao padrão de forma alguma. No multisite, cada site tem seus próprios uploads — wp-content/uploads/sites/N/…, além do prefixo de URL /siteN/ em redes de subdiretório — enquanto a regra está fixada em ^wp-content/uploads/upf-docs/ a partir da URL base do site principal. Mesmo depois que a reescrita interna da rede remove o prefixo /siteN/, o segmento sites/N/ ainda não corresponde.

Assim, para qualquer rede multisite (tanto de subdiretório quanto de subdomínio), um atacante não autenticado obtém arquivos privados diretamente:

GET /wp-content/uploads/upf-docs/victim.pdf HTTP/1.1            (main site)
GET /site2/wp-content/uploads/sites/2/upf-docs/victim.pdf HTTP/1.1  (subsite)

A resposta é 200 com o corpo do arquivo — servido pelo Apache diretamente de uploads/, a verificação de acesso do plugin nunca é executada.

Os nomes dos arquivos são previsíveis

Nada na URL é secreto. O plugin constrói os nomes a partir do id numérico do utilizador proprietário e do segundo unix do upload:

Gerador (2.1.9)Nome no disco
UI moderna, inc/functions-file.php:73-77usr{ID}_{unixtime}_{original}
template clássico, inc/classic-user-functions.phpuser_{ID}_doc_{unixtime}.{ext}

Os ids de utilizador numa rede multisite são pequenos inteiros sequenciais de uma tabela wp_users partilhada, pelo que um atacante precisa apenas de uma estimativa aproximada da hora do upload (uma janela de ±2 minutos é ≈240 pedidos) e do nome original do arquivo ou apenas da sua extensão. Sufixos de colisão (-1, -2) existem, mas são raros.

A correção (2.2.0)

A versão 2.2.0 substituiu a reescrita do arquivo raiz por um upf-docs/.htaccess ao nível da pasta — RewriteRule ^(.*)$ {home}/?file=$1 [R=302,QSA,L], mais Require all denied quando o mod_rewrite está ausente — recriado em cada admin_init (auto-recuperação, por subsite). Uma regra por diretório é acionada para qualquer caminho físico sob upf-docs/, independentemente do prefixo da URL, pelo que o GET direto passa agora a ser um 302 para a verificação de acesso e, para utilizadores anónimos, um 403. Os arquivos não são movidos; apenas o mecanismo de interceção muda. Essa distinção entre 302 e estático é também o que o exploit usa como detetor comportamental.

Versões afetadas

  • User Private Files < 2.2.0 (verificado contra os fontes 2.1.9; as tags SVN 1.0.1–2.1.9 têm o mesmo design de reescrita na raiz)
  • Apenas implementações multisite (subdiretório e subdomínio). As instalações single-site estão protegidas pela regra raiz desde que o Apache leia o .htaccess e o arquivo fosse gravável na ativação (o nginx precisa da reescrita manual do fornecedor — ver o readme do plugin).
  • Referência: aviso do WPScan d6a144b3-84e3-43eb-92d3-fd4505dd0e1c (publicado em 2026-10-02, investigador Akshat Parikh / SN1PER).

Como funciona o exploit

O exploit.py usa apenas a biblioteca padrão do Python 3 (http.client), não segue redirecionamentos e não envia cookies:

  1. Verificação de versão (passo 1, antes de qualquer ataque): GET /wp-content/plugins/user-private-files/readme.txt, analisa Stable tag:. < 2.2.0 → prossegue; >= 2.2.0 → caixa vermelha NOT VULNERABLE - plugin 2.2.0+ (patched) e código de saída 6. Se o readme.txt não for legível, é mostrada uma sonda comportamental de um nome inexistente em upf-docs/ (302 = reescrita em vigor, 404 = entrega estática) e a execução continua (--skip-check desativa todo o passo).
  2. Construção da URL (passo 2): site principal {base}/wp-content/uploads/upf-docs/{file} ou, com --site site2 --site-id 2, {base}/site2/wp-content/uploads/sites/2/upf-docs/{file}.
  3. Obtenção (passo 3): um simples GET, sem Cookie, sem Authorization.
  4. Veredicto (passo 4):
    • 200 → * LEAKED com as primeiras linhas do arquivo como prova, saída 0;
    • 302 → "redirected to the access check - not vulnerable / patched", saída 1;
    • 404 → "file not found (wrong name?)" — tente --guess, saída 1.
Baixar ferramenta