#104·xmall

xmall 后台订单打印页存在由前台订单字段触发的存储型 XSS

Author: JunRoinxxXCreated Aug 1, 2026Updated Aug 1, 2026

漏洞描述

在审计 xmall 源码时发现,前台订单创建接口会信任并持久化请求体中的收货地址和商品名称,后台订单打印页随后把这些字段写入 HTML Sink,形成跨前台用户到后台订单人员的存储型 XSS。

该数据流已在授权的本地测试环境中完成无害触发验证。为避免扩大风险,本 Issue 不提供可执行 payload。

受影响版本

README 将订单打印功能列为 v1.1 功能;仓库没有发布 tag。当前 master 提交 19e7917d5ed3bd2a2421a3a246ad494c133ba94c 仍保留该链路,首个修复版本未知。

漏洞位置

  • 输入入口:POST /member/addOrder
  • 攻击者可控字段:streetNamegoodsList[*].productName
  • 持久化位置:xmall-sso/xmall-sso-service/src/main/java/cn/exrick/sso/service/impl/OrderServiceImpl.java
  • 渲染位置:xmall-manager-web/src/main/webapp/WEB-INF/jsp/order-print.jsp:132,140

漏洞分析

数据流如下:

streetName / goodsList[*].productNameOrderController.addOrderOrderServiceImpl.createOrdertb_order_shipping.receiver_address / tb_order_item.title → 后台 GET /order/detail/{orderId}order-print.jsp 中的 .html() 和 HTML 字符串拼接。

前台模块注册的 LimitRaterInterceptor 只执行限流并始终放行,不进行登录、token 或 body.userId 归属校验;createOrder 只检查请求中的 userId 是否对应现有会员。后台打印页由 Shiro 的最终 /* = authc 规则保护,因此触发者是独立的已登录后台人员,而不是输入者本人。

关键 Sink:

javascript
$("#expressAdress").html(data.result.tbOrderShipping.receiverAddress);
$("#products").append("<tr class='text-c'><td>" + products[i].title + "</td>...");

影响

能够访问前台接口的攻击者可创建包含恶意文本字段的订单;后台订单人员查看或打印该订单时,攻击者控制的内容会在后台浏览器上下文中执行。可能影响后台会话中的数据读取和操作完整性,但需要受害者打开对应订单,故按中等严重度报告。

与已有 CVE 的边界

CVE-2025-65540 / Issue #101 涉及 member-list.jspmember-show.jsp 中会员 username/description 的渲染。本 Issue 涉及不同的输入接口、数据库字段、后台页面和修复位置:/member/addOrder → 订单地址/商品标题 → order-print.jsp。Issue #103 讨论前台会员接口缺失鉴权;该问题会放大本链路,但没有覆盖订单打印页的 XSS Sink。

修复建议

  1. 在订单打印页使用 .text()textContent 或文本节点渲染普通文本字段,避免字符串拼接 HTML。
  2. 创建订单时从服务端会话确定会员身份,并根据商品 ID 从服务端重新读取商品名称,不信任请求体快照字段。
  3. 对地址等文本字段增加合理的长度和值域约束。
  4. 增加从前台订单输入到后台打印页的回归测试。

参考: