Skip to content
KitploitKITPLOIT
StrumentiBlog
Log in
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-19264 — CVE-2026-19264 - Path traversal critico non autenticato fino alla compromissione completa dell'istanza in Postiz (< 2.22.1). Writeup tecnico: bypass dell'ordine di decodifica, escalation di JWT_SECRET e analisi della correzione a monte. | Kitploit
Strumenti/GitHubGitHub/darklycn1976/cve-2026-19264
Analisi delle VulnerabilitàAnalisi del CodiceExploitSfruttamento di Applicazioni WebSicurezza WebApprendimento e Formazione
GitHubdarklycn1976/cve-2026-19264

CVE-2026-19264

CVE-2026-19264 - Path traversal critico non autenticato fino alla compromissione completa dell'istanza in Postiz (< 2.22.1). Writeup tecnico: bypass dell'ordine di decodifica, escalation di JWT_SECRET e analisi della correzione a monte.

Vedi RepositorySito web
81 mese faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2026-19264 - Unauthenticated Path Traversal to Full Instance Takeover in Postiz

CVE-2026-19264 - Path Traversal non autenticato che porta alla compromissione totale dell'istanza in Postiz

Autore: Krithik Babu P (@DarkLycn1976) Pubblicato: 2026-08-10 CVE: CVE-2026-19264 Gravità: Critica - CVSS 4.0 9.3 / CVSS 3.1 9.8 CWE: CWE-22 - Limitazione impropria di un pathname a una directory ristretta Versioni affette: gitroomhq/postiz-app < 2.22.1 Corretta in: v2.22.1


TL;DR

Postiz serviva i media archiviati localmente tramite una route che univa i segmenti di percorso forniti dall'URL alla directory di upload e trasmetteva il risultato al chiamante - senza normalizzazione del percorso, senza controllo di contenimento e senza autenticazione.

Il payload di traversal più ovvio restituisce 404, perché Next.js compatta i segmenti ../ prima del routing. Ma i separatori codificati in URL sopravvivono al matching delle route e vengono decodificati esattamente un'ultima volta lungo il percorso verso la chiamata al filesystem, ripristinando la traversal al di là di ogni controllo.

Un attaccante non autenticato poteva leggere qualsiasi file leggibile dal processo dell'applicazione - incluso il suo stesso ambiente, che contiene il segreto di firma JWT. Poiché Postiz firma i token di sessione con quel segreto e li emette senza un claim di scadenza, il suo recupero trasforma una primitiva di lettura file in una sessione permanente e falsificabile come qualsiasi utente, incluso un amministratore.

Una richiesta GET non autenticata fino alla compromissione totale dell'istanza.


1. Contesto

Postiz è una piattaforma open-source di pianificazione per i social media - circa 34.000 stelle su GitHub al momento della stesura - realizzata come frontend Next.js con backend NestJS. È ampiamente auto-ospitata da agenzie e piccoli team per gestire account social connessi, contenuti pianificati e fatturazione.

Le distribuzioni self-hosted possono archiviare i media caricati localmente anziché su un object storage. Questo comportamento è controllato da una singola variabile d'ambiente:

STORAGE_PROVIDER=local

Questo è il valore incluso in .env.example, quindi è ciò che eseguono la maggior parte degli auto-ospitanti a meno che non configurino deliberatamente S3 o Cloudflare R2.

2. La superficie d'attacco

Quando lo storage locale è attivo, next.config.js riscrive il percorso pubblico /uploads/:path* su una route API interna:

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

Due proprietà rendono questa route interessante prima ancora che entri in gioco un bug:

  1. Non è autenticata. Nessun middleware del frontend la protegge. Servire media pubblici è lo scopo, quindi non è richiesta alcuna sessione.
  2. È un catch-all. Il segmento catch-all opzionale [[...path]] fa sì che ogni componente rimanente del percorso arrivi come un array che l'handler è libero di interpretare.

Quando STORAGE_PROVIDER è un valore diverso da local, la riscrittura punta a /404 e l'handler è irraggiungibile. Quel gate di configurazione è l'unica cosa che separa una distribuzione da questo bug.

3. Il codice vulnerabile

L'handler, prima della 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
};

Tre difetti in quattro righe:

  • Nessuna normalizzazione. path.normalize(), path.resolve() - nessuno dei due viene chiamato. Qualsiasi segmento arrivi viene concatenato così com'è.
  • Nessun controllo di contenimento. Nulla verifica che il filePath risultante si trovi ancora all'interno di UPLOAD_DIRECTORY.
  • Concatenazione di stringhe, non unione di percorsi. + '/' + tratta i componenti come testo, non come un percorso con una semantica.

Il risultato finisce direttamente in createReadStream() e i byte vengono trasmessi al chiamante con un tipo MIME dedotto dal nome del file. Non esiste un'allow-list di estensioni né un filtro sui contenuti.

4. Perché il payload più ovvio fallisce

L'attacco da manuale è:

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

Su Postiz questo restituisce 404, e quel 404 è l'intera ragione per cui questo bug è sopravvissuto fino a quando non è stato trovato.

Next.js normalizza il percorso della richiesta durante il routing. I segmenti ../ grezzi vengono compattati prima che il router decida quale handler invocare. Quando la richiesta raggiunge il catch-all, la traversal è già stata eliminata - o il percorso risolve da qualche parte senza una route corrispondente, oppure risolve nuovamente all'interno di /uploads senza più i segmenti ...

Per chi sta testando rapidamente, quel 404 si legge come "il framework ci pensa lui". È una difesa genuina e funzionante. Il problema non è che sia assente - è dove nella pipeline viene eseguita.

5. Il bypass - una discrepanza nell'ordine di decodifica

Il matching delle route e l'handler della richiesta non eseguono lo stesso numero di passaggi di percent-decoding.

Se i separatori sono percent-encoded, la sequenza non è un separatore di percorso durante il matching delle route. %2e%2e%2f è solo una stringa opaca - testo inerte che il normalizzatore non ha alcun motivo di toccare. Attraversa il routing intatta, viene fatta corrispondere dal catch-all e viene decodificata mentre entra nei params dell'handler, dove torna a essere ../.

A quel punto viene concatenata a UPLOAD_DIRECTORY e passata a createReadStream() - oltre il routing, oltre la normalizzazione, oltre ogni controllo che l'avrebbe fermata.

Forme funzionanti:

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

Il doppio encoding non funziona - %252e rimane letterale attraverso il singolo passaggio di decodifica e non diventa mai un punto. Esattamente uno strato di encoding è il punto ottimale, un utile promemoria che "codificare di più" non è una strategia.

L'invariante da tenere a mente:

Un controllo eseguito prima che la decodifica sia completa non sta proteggendo il sink.

6. Escalation - dalla lettura arbitraria alla compromissione totale dell'istanza

Una primitiva di lettura file è di per sé di gravità Alta. Ciò che la rende Critica è ciò che riesce a raggiungere.

Passo 1 - leggere l'ambiente. La configurazione stessa del processo Node è su disco nella root della distribuzione. .env fornisce, tra le altre cose:

  • JWT_SECRET - la chiave di firma dei token di sessione
  • DATABASE_URL - credenziali Postgres complete
  • Segreti OAuth dei provider connessi e chiavi di fatturazione

Passo 2 - falsificare una sessione. Postiz firma i token di sessione con JWT_SECRET usando HS256 tramite jsonwebtoken. In modo critico, i token vengono emessi senza expiresIn, quindi un token falsificato è valido indefinitamente.

Scarica lo strumento