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.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-19264 — CVE-2026-19264 - Travessia de caminho crítica não autenticada para assumir completamente a instância no Postiz (< 2.22.1). Análise técnica: bypass da ordem de decodificação, escalonamento de JWT_SECRET e análise da correção upstream. | Kitploit
Ferramentas/GitHubGitHub/darklycn1976/cve-2026-19264
Análise de VulnerabilidadesAnálise de CódigoExploraçãoExploração de Aplicações WebSegurança WebAprendizado e Educação
GitHubdarklycn1976/cve-2026-19264

CVE-2026-19264

CVE-2026-19264 - Travessia de caminho crítica não autenticada para assumir completamente a instância no Postiz (< 2.22.1). Análise técnica: bypass da ordem de decodificação, escalonamento de JWT_SECRET e análise da correção upstream.

Ver RepositórioSite
8há 1 mêsAinda não revisado

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-19264 - Path Traversal não autenticado para assunção total da instância no Postiz

CVE-2026-19264 - Path Traversal não autenticado para assunção total da instância no Postiz

Autor: Krithik Babu P (@DarkLycn1976) Publicado: 2026-08-10 CVE: CVE-2026-19264 Gravidade: Crítica - CVSS 4.0 9.3 / CVSS 3.1 9.8 CWE: CWE-22 - Limitação inadequada de nome de caminho a um diretório restrito Afetados: gitroomhq/postiz-app < 2.22.1 Corrigido em: v2.22.1


TL;DR

O Postiz servia mídias armazenadas localmente por meio de uma rota que concatenava segmentos de caminho fornecidos pela URL ao diretório de upload e transmitia o resultado de volta - sem normalização de caminho, sem verificação de contenção e sem autenticação.

O payload de traversal óbvio retorna 404, porque o Next.js colapsa os segmentos ../ antes do roteamento. Mas separadores codificados em URL sobrevivem à correspondência de rota e são decodificados exatamente mais uma vez no caminho até a chamada ao sistema de arquivos, restaurando a traversal do outro lado de todas as verificações.

Um atacante não autenticado podia ler qualquer arquivo legível pelo processo da aplicação - incluindo seu próprio ambiente, que contém o segredo de assinatura JWT. Como o Postiz assina tokens de sessão com esse segredo e os emite sem uma declaração de expiração, recuperá-lo converte uma primitiva de leitura de arquivo em uma sessão permanente e forjável como qualquer usuário, incluindo um administrador.

Uma única requisição GET não autenticada para a assunção total da instância.


1. Contexto

Postiz é uma plataforma de agendamento de mídias sociais de código aberto - cerca de 34.000 estrelas no GitHub no momento em que este texto foi escrito - construída como um frontend Next.js com um backend NestJS. É amplamente auto-hospedado por agências e pequenas equipes para gerenciar contas sociais conectadas, conteúdo agendado e cobrança.

Implantações auto-hospedadas podem armazenar mídias enviadas localmente em vez de em armazenamento de objetos. Esse comportamento é controlado por uma única variável de ambiente:

STORAGE_PROVIDER=local

Este é o valor incluído no .env.example, portanto é o que a maioria dos auto-hospedeiros executa, a menos que configurem deliberadamente S3 ou Cloudflare R2.

2. A superfície de ataque

Quando o armazenamento local está ativo, o next.config.js reescreve o caminho público /uploads/:path* para uma rota de API interna:

apps/frontend/src/app/(app)/api/uploads/[[...path]]/route.ts

Duas propriedades tornam esta rota interessante antes mesmo de qualquer bug estar envolvido:

  1. Ela não é autenticada. Nenhum middleware de frontend a protege. Servir mídia pública é a intenção, então nenhuma sessão é necessária.
  2. Ela é um catch-all. O segmento catch-all opcional [[...path]] significa que cada componente de caminho restante chega como um array que o handler é livre para interpretar.

Quando STORAGE_PROVIDER é qualquer coisa diferente de local, a reescrita aponta para /404 e o handler fica inacessível. Essa barreira de configuração é a única coisa entre uma implantação e este bug.

3. O código vulnerável

O handler, antes da v2.22.1:

export const GET = async (request: NextRequest, context) => {
  const { path } = await context.params;
  const filePath =
    process.env.UPLOAD_DIRECTORY + '/' + (path ?? []).join('/');

  const response  = createReadStream(filePath);
  const fileStats = statSync(filePath);
  // ... stream the file back to the caller
};

Três defeitos em quatro linhas:

  • Sem normalização. path.normalize(), path.resolve() - nenhum é chamado. Quaisquer segmentos que chegam são concatenados literalmente.
  • Sem verificação de contenção. Nada verifica se o filePath resultante ainda está dentro de UPLOAD_DIRECTORY.
  • Concatenação de strings, não junção de caminhos. + '/' + trata os componentes como texto, não como um caminho com semântica.

O resultado vai direto para createReadStream() e os bytes são transmitidos ao chamador com um tipo MIME inferido do nome do arquivo. Não há lista de permissões de extensões nem filtro de conteúdo.

4. Por que o payload óbvio falha

O ataque clássico é:

GET /uploads/../../../etc/passwd

No Postiz isso retorna 404, e esse 404 é exatamente o motivo pelo qual este bug sobreviveu até ser encontrado.

O Next.js normaliza o caminho da requisição durante o roteamento. Segmentos crus de ../ são colapsados antes que o roteador decida qual handler invocar. Quando a requisição chega ao catch-all, a traversal já foi eliminada - ou o caminho resolve para algum lugar sem rota correspondente, ou resolve de volta dentro de /uploads com os segmentos de ponto removidos.

Para alguém testando rapidamente, esse 404 parece "o framework lida com isso". É uma defesa genuína e funcional. O problema não é que ela esteja ausente - é onde no pipeline ela é executada.

5. O bypass - uma incompatibilidade na ordem de decodificação

A correspondência de rota e o handler de requisição não executam o mesmo número de passagens de decodificação percentual.

Se os separadores forem codificados em percentual, a sequência não é um separador de caminho durante a correspondência de rota. %2e%2e%2f é apenas uma string opaca - texto inerte que o normalizador não tem motivo para tocar. Ela atravessa o roteamento intacta, é correspondida pelo catch-all e é decodificada no caminho para os params do handler, onde se torna ../ novamente.

Nesse ponto, ela é concatenada a UPLOAD_DIRECTORY e entregue a createReadStream() - depois do roteamento, depois da normalização, depois de todo controle que a teria impedido.

Formas funcionais:

GET /uploads/%2e%2e%2fsecretdir%2fsecret.txt        → 200, file outside the upload directory
GET /uploads/..%2f..%2f..%2fetc%2fpasswd            → 200
GET /uploads/%2e%2e%2f%2e%2e%2f...%2fetc%2fpasswd   → 200, returned the real /etc/passwd

A dupla codificação não funciona - %252e permanece literal através da única passagem de decodificação e nunca se torna um ponto. Exatamente uma camada de codificação é o ponto ideal, o que é um lembrete útil de que "codificar mais forte" não é uma estratégia.

O invariante a reter:

Um controle que é executado antes que a decodificação esteja completa não está protegendo o sink.

6. Escalação - de leitura arbitrária a assunção da instância

Uma primitiva de leitura de arquivo é Alta por si só. O que torna isso Crítico é o que ela alcança.

Passo 1 - ler o ambiente. A configuração do próprio processo Node está em disco na raiz da implantação. O .env fornece, entre outras coisas:

  • JWT_SECRET - a chave de assinatura dos tokens de sessão
  • DATABASE_URL - credenciais completas do Postgres
  • Segredos OAuth dos provedores conectados e chaves de cobrança

Passo 2 - forjar uma sessão. O Postiz assina tokens de sessão com JWT_SECRET usando HS256 via jsonwebtoken. Criticamente, os tokens são emitidos sem expiresIn, então um token forjado é válido indefinidamente.

Passo 3 - tornar-se qualquer um. O middleware de autenticação re-resolve o usuário do banco de dados usando a declaração id. Ele deliberadamente não confia em uma declaração como isSuperAdmin do token - bom design - mas esse endurecimento é irrelevante quando você pode assinar um id arbitrário. Assinar { id: <victim user id> } produz uma sessão indistinguível de um login legítimo:

Baixar ferramenta