CVE-2026-33653: XSS almacenado en Uploady
Se publicó el CVE-2026-33653, una vulnerabilidad de Cross-Site Scripting almacenado (Stored XSS) descubierta por Xavier Magallanes en Uploady.
| Identificador | CVE-2026-33653 |
|---|---|
| CVSS v3.1 | 4.6 MEDIO |
| Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N |
| CWE | CWE-79 (Cross-site Scripting) |
| Versiones afectadas | Uploady < 3.1.2 |
| Versión corregida | Uploady v3.1.2 |
Resumen
Uploady no sanitiza adecuadamente los nombres de archivo durante el proceso de subida. Un atacante puede subir un archivo con un nombre malicioso que contenga código JavaScript, el cual se renderiza en la aplicación sin el escapado correspondiente. Cuando el nombre del archivo se muestra en la lista de archivos o en la página de detalles, el script se ejecuta en el navegador de cualquier usuario que visualice la página.
Detalles técnicos
La aplicación no realiza el escape de caracteres especiales en los nombres de archivo proporcionados durante la subida. Al almacenar y renderizar estos nombres en la interfaz sin sanitización, un atacante puede inyectar HTML o JavaScript directamente en el nombre del archivo.
El código vulnerable devuelve el nombre de archivo original sin ningún tipo de escape:
// UploadResult.php — Antes del fix (vulnerable)
public function getOriginalFilename(): ?string
{
return $this->originalFilename;
}
Cuando la vista renderiza el nombre del archivo en HTML, los caracteres especiales como <, >, " y ' son interpretados directamente por el navegador, permitiendo la inyección de etiquetas y event handlers.
Prueba de concepto
- Navegar a la sección de subida de archivos.
- Subir un archivo (ej. PNG) renombrado a:
"><img src=x onerror=alert('XSS')>.png - Una vez subido, visitar la lista de archivos o la página de detalles del archivo.
- El payload JavaScript se ejecuta inmediatamente.
Corrección
El parche en la versión 3.1.2 introdujo dos cambios principales:
1. Método seguro para obtener el nombre de archivo — Se agregó getOriginalFilenameSafe() que aplica htmlspecialchars al nombre antes de renderizarlo:
// UploadResult.php — Después del fix
public function getOriginalFilenameSafe(): ?string
{
if ($this->originalFilename === null) {
return null;
}
return htmlspecialchars(
$this->originalFilename,
ENT_QUOTES | ENT_HTML5,
'UTF-8'
);
}
2. Validación de nombres de archivo — FilenameValidator ahora rechaza nombres con caracteres peligrosos y patrones de inyección:
// FilenameValidator.php — Validación añadida
// Detectar caracteres de inyección XSS/HTML
if (preg_match('/[<>"\'\`]/', $filename)) {
$result->addError(
'El nombre contiene caracteres potencialmente peligrosos',
'XSS_CHARACTERS_DETECTED',
['filename' => $filename]
);
}
// Detectar event handlers de JavaScript y protocolos peligrosos
if (preg_match('/(?:javascript|data|vbscript):/i', $filename) ||
preg_match('/on\w+\s*=/i', $filename)) {
$result->addError(
'El nombre contiene un intento de inyección de script',
'SCRIPT_INJECTION_DETECTED',
['filename' => $filename]
);
}
Impacto
Al tratarse de un XSS almacenado, el payload persiste en el servidor y se ejecuta cada vez que se renderiza el nombre del archivo afectado. Durante las pruebas fue posible acceder a document.cookie, lo que demuestra que los datos de sesión pueden quedar expuestos. Un atacante podría:
- Secuestrar sesiones de usuario
- Suplantar la identidad de otros usuarios
- Ejecutar acciones no autorizadas en nombre de la víctima