Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-19264 — CVE-2026-19264 - Traversée de chemin non authentifiée critique menant à une prise de contrôle complète de l'instance dans Postiz (< 2.22.1). Analyse technique : contournement de l'ordre de décodage, escalade JWT_SECRET, et analyse du correctif en amont. | Kitploit
Outils/GitHubGitHub/darklycn1976/cve-2026-19264
Analyse des VulnérabilitésAnalyse de CodeExploitationExploitation d'Applications WebSécurité WebApprentissage et Éducation
GitHubdarklycn1976/cve-2026-19264

CVE-2026-19264

CVE-2026-19264 - Traversée de chemin non authentifiée critique menant à une prise de contrôle complète de l'instance dans Postiz (< 2.22.1). Analyse technique : contournement de l'ordre de décodage, escalade JWT_SECRET, et analyse du correctif en amont.

Voir le dépôtSite web
8il y a 1 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-19264 — Traversée de chemin non authentifiée menant à une prise de contrôle totale de l'instance Postiz

CVE-2026-19264 — Traversée de chemin non authentifiée menant à une prise de contrôle totale de l'instance Postiz

Auteur : Krithik Babu P (@DarkLycn1976) Publié : 2026-08-10 CVE : CVE-2026-19264 Sévérité : Critical — CVSS 4.0 9.3 / CVSS 3.1 9.8 CWE : CWE-22 — Limitation inappropriée d'un nom de chemin à un répertoire restreint Versions affectées : gitroomhq/postiz-app < 2.22.1 Corrigé dans : v2.22.1


TL;DR

Postiz servait les médias stockés localement via une route qui concaténait les segments de chemin fournis dans l'URL au répertoire de téléversement, puis renvoyait le résultat en streaming — sans normalisation de chemin, sans vérification de confinement et sans authentification.

La charge utile de traversée évidente renvoie une 404, car Next.js réduit les segments ../ avant le routage. Mais les séparateurs encodés en URL survivent à la correspondance de routes et sont décodés exactement une fois de plus sur le chemin vers l'appel au système de fichiers, rétablissant la traversée de l'autre côté de chaque contrôle.

Un attaquant non authentifié pouvait lire tout fichier lisible par le processus de l'application — y compris son propre environnement, qui contient le secret de signature JWT. Comme Postiz signe les jetons de session avec ce secret et les émet sans date d'expiration, récupérer ce secret transforme une primitive de lecture de fichier en une session permanente et forgeable sous l'identité de n'importe quel utilisateur, y compris un administrateur.

Une seule requête GET non authentifiée pour une prise de contrôle totale de l'instance.


1. Contexte

Postiz est une plateforme open-source de planification de publications sur les réseaux sociaux — environ 34 000 étoiles GitHub au moment de la rédaction — construite avec un frontend Next.js et un backend NestJS. Elle est largement auto-hébergée par des agences et de petites équipes pour gérer des comptes de réseaux sociaux connectés, le contenu planifié et la facturation.

Les déploiements auto-hébergés peuvent stocker les médias téléversés localement plutôt que sur un stockage d'objets. Ce comportement est contrôlé par une seule variable d'environnement :

STORAGE_PROVIDER=local

C'est la valeur fournie dans .env.example, c'est donc ce que la plupart des auto-hébergeurs utilisent, sauf s'ils configurent délibérément S3 ou Cloudflare R2.

2. La surface d'attaque

Lorsque le stockage local est actif, next.config.js réécrit le chemin public /uploads/:path* vers une route API interne :

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

Deux propriétés rendent cette route intéressante avant même qu'un bug n'intervienne :

  1. Elle n'est pas authentifiée. Aucun middleware frontend ne la protège. Servir des médias publics est l'intention, donc aucune session n'est requise.
  2. C'est un attrape-tout. Le segment attrape-tout optionnel [[...path]] signifie que chaque composant de chemin restant arrive sous forme de tableau que le handler est libre d'interpréter.

Lorsque STORAGE_PROVIDER a une valeur autre que local, la réécriture pointe vers /404 et le handler est inaccessible. Cette barrière de configuration est la seule chose qui se dresse entre un déploiement et ce bug.

3. Le code vulnérable

Le handler, avant la 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
};

Trois défauts en quatre lignes :

  • Aucune normalisation. Ni path.normalize() ni path.resolve() n'est appelé. Quels que soient les segments reçus, ils sont concaténés tels quels.
  • Aucune vérification de confinement. Rien ne vérifie que le filePath résultant se trouve toujours à l'intérieur de UPLOAD_DIRECTORY.
  • Concaténation de chaînes, pas jonction de chemins. + '/' + traite les composants comme du texte, pas comme un chemin ayant une sémantique.

Le résultat va directement dans createReadStream() et les octets sont diffusés à l'appelant avec un type MIME déduit du nom de fichier. Il n'y a ni liste blanche d'extensions ni filtre de contenu.

4. Pourquoi la charge utile évidente échoue

L'attaque classique est :

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

Sur Postiz, elle renvoie une 404, et cette 404 est la raison même pour laquelle ce bug a survécu jusqu'à sa découverte.

Next.js normalise le chemin de la requête pendant le routage. Les segments ../ bruts sont réduits avant que le routeur décide quel handler invoquer. Au moment où la requête atteint l'attrape-tout, la traversée a déjà été éliminée — soit le chemin se résout vers un endroit sans route correspondante, soit il se résout à l'intérieur de /uploads, les segments point ayant disparu.

Pour quelqu'un qui teste rapidement, cette 404 se lit comme « le framework gère cela ». C'est une défense réelle et fonctionnelle. Le problème n'est pas qu'elle soit absente — c'est où dans le pipeline elle s'exécute.

5. Le contournement — un décalage dans l'ordre de décodage

La correspondance de routes et le handler de requête n'effectuent pas le même nombre de passes de décodage en pourcentage.

Si les séparateurs sont encodés en pourcentage, la séquence n'est pas un séparateur de chemin pendant la correspondance de routes. %2e%2e%2f n'est qu'une chaîne opaque — un texte inerte que le normaliseur n'a aucune raison de toucher. Elle traverse le routage intacte, correspond à l'attrape-tout, puis est décodée en chemin vers les params du handler, où elle redevient ../.

À ce stade, elle est concaténée à UPLOAD_DIRECTORY et transmise à createReadStream() — après avoir franchi le routage, la normalisation et chaque contrôle qui aurait pu l'arrêter.

Variantes fonctionnelles :

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

Le double encodage ne fonctionne pas — %252e reste littéral à travers l'unique passe de décodage et ne devient jamais un point. Exactement une couche d'encodage est le point idéal, ce qui rappelle utilement que « encoder plus fort » n'est pas une stratégie.

L'invariant à retenir :

Un contrôle qui s'exécute avant la fin du décodage ne protège pas le sink.

6. Escalade — de la lecture arbitraire à la prise de contrôle de l'instance

Une primitive de lecture de fichier est de gravité High à elle seule. Ce qui la rend Critical, c'est ce qu'elle permet d'atteindre.

Étape 1 — lire l'environnement. La configuration du processus Node lui-même est sur disque, à la racine du déploiement. .env permet d'obtenir, entre autres :

  • JWT_SECRET — la clé de signature des jetons de session
  • DATABASE_URL — les identifiants Postgres complets
  • Les secrets OAuth des fournisseurs connectés et les clés de facturation

Étape 2 — forger une session. Postiz signe les jetons de session avec JWT_SECRET en utilisant HS256 via jsonwebtoken. Point critique : les jetons sont émis sans expiresIn, si bien qu'un jeton forgé est valide indéfiniment.

Télécharger l’outil