开源商业系统MCMS-java代码分析

前言

MCMS为java免费开源商用的CMS内容管理系统/基于SpringBoot 2/前端Vue3/element plus/提供上百套模板,同时提供实用的插件,接近企业级代码,适合初学者练手,很值得搭建起来自己研究一下,注入点很多,可以再详细查看。

搭建

代码下载地址https://gitee.com/mingSoft/MCMS/tree/5.2.6/

下载完修改一下数据库配置文件,添加mcms数据库即可启动

审计

1、SQL注入

1、第一处

方法名是queryChildren,追踪哪个用了这个方法,在最上面

继续追谁调用了这个方法

可以看到有四个地方调用了这个方法

查看第一个方法

可以看到_category的id来自于category.getid()方法,追踪这个category来源于哪里。

可以看到@ModelAttribute注解,这个注解告诉 Spring MVC 应用程序,方法参数 category 应该从 HTTP 请求的查询参数或者表单数据中自动绑定。Spring MVC 会尝试将请求中的参数名与 CategoryEntity 类的属性名匹配,并填充 CategoryEntity 对象。

根据代码看到只校验了title是否为空,那么我们可以构造测试数据包

尝试注入

2、第二处

经过分析第二个调用的方法是不存在注入的,我们查看第三个调用

可以看到目录为{categoryid},可以看到下面存在一个@PathVariable String categoryId,@PathVariable 是一个 Spring 框架中的注解,用于将 URL 中的模板变量绑定到控制器方法的参数上。当你定义了一个带有占位符(如 {categoryId})的路由时,Spring 会将实际 URL 中这部分的值传递给带有 @PathVariable 注解的方法参数。所以在此处categoryid既作为目录又作为参数传入数据库进行操作

可以看到上述参数不存在过滤操作,尝试找到功能点进行注入。

其中这个0就是我们传入的参数,尝试注入

)

3、第三处

追踪第三处调用的方法可以看到方法大致和第二处注入一样,唯一区别是校验了传入的是否为0,所以我们需要修改一下数字

将包拿出来尝试注入

4、第四处

在DAO文件中搜索$,一个个参数的追,可以看到${categoryId},id是query,追踪是关联了哪个接口

)

可以看到最上面IContentDao为他的映射器,继续追IContentDao

进入IContentDao未查找到id为query的方法,继续看他的父类IBaseDao

追进IBaseDao,发现IBaseDao为编译好的class文件。

这个时候我们不能直接点击追踪query方法,因为当前我们所在的文件是编译好的,如果直接点击跳转容易发生错误跳转回mingsoft.cms的代码中,此时我们在的位置为ms-base-2.1.12.jar包下

根据代码分层思想,我们去找与IBaseDao对应的service

IBaseBiz:这是一个接口(Interface),定义了一组方法签名,这些方法是所有实现了该接口的类必须实现的。接口提供了抽象方法的定义,但不包含具体的实现细节,所以我们点击上述指向的1 implementation追踪是哪个接口实现了BaseBiz 接口的具体类

这时候就可以点击谁调用了这个方法

可以看到有两个调用了此方法先追踪第二个查看

可以看到未经过滤直接传入查询方法为post请求,查看接口,发现是前端可以不用登录直接访问

拼接进行访问

直接将数据包丢进sqlmap进行注入

可以看到注入成功。

5、第五处

点击第一个contentaction

可以看到和第四处的注入差不多,只不过这个注入是后台的注入

这里就不做演示了,和第四处一样,只不过需要登录的情况下。

同理此处也存在,依旧是query方法,只不过传入的参数不同,这里所接受的参数是contentType

6、第六处

在审计的时候发现存在一个<include refid="net.mingsoft.base.dao.IBaseDao.sqlWhere"></include>,这个标签用于包含另一个SQL片段,而refid是指向这个SQL片段的ID

我们追踪这个sqlwhere,可以看到多处未进行预编译

再次追进query查看调用情况,可以看到五处调用了categoryBiz的query方法

分析后有两处传入的数据受用户控制,其中一个接口为后台一个为前台,我们就分析一个前台,后台的操作是一样的

根据接口分析,传入的数据直接受用户的影响

拼接URL进行访问

查找sqlwhere的来源,追categoryEntity发现不存在,发现继承了BaseEntity

跟进BaseEntity查看,

xml文件里验证的sqlwherelist,查看sqlwherelist来源于哪里

在BaseEntity可以看到sqlwherelist来源于sqlwhere

所以我们传入的参数应该为sqlwhere,根据需要的数据进行构造,可以看到首先是校验了item.el然后multiple,最后查询了item.value和item.field

那么我们就需要4个参数,构造测试(在这里构造的时候出了很多问题,在进行post方法传参数的时候不知道为什么直接把数据包放下面不行,只能放在上面的时候才能测试)

而我将这个数据包放在URL的时候就可以进行注入了

另一处后台相同注入的就可以通过第一种方式进行注入

看代码除了一个可以用get和post两种方法,一个只能用post方法,就没什么区别了。

这个sqlwhere存在的地方利用点的方法都差不多,这个存在于编译好的idictDao.xml文件里了

追踪到IDictDao.class后续就需要自己手动查找了,根据分层思想可以最终找到他的应用层DictAction.class

未经过过滤直接接收

构造语句进行注入

相同的id为query的也存在,过程一样就不做演示了

7、第七处

在审计sqlwhere时候看到存在一个orderBy

根据上述的流程,直接构造注入尝试是否成功

除了queryExcludeApp还有query也存在,审计方法一样就不做演示

8、第八处

全局搜$在ibaseDao文件里发现第一处$

可以看到id为queryBySQL,再次朝上追,查看调用情况

到这一步无法点击追踪了,只能根据分层思想手动往上查找

经过手动查找调用了queryBySQL的应用层查询到在基础应用层的父类里存在方法调用

在这里我们需要再次手动查找谁调用了validated,一共查到两处调用

根据分析第一张图片不存在注入,因为根据追踪,最后在xml里处理的wheres来源于where.put(fieldName, fieldValue);所以对应的key应该就是fieldName,而第一张图片的的key为写死了的,所以无法注入

查看第二处可以看到参数均来自于用户输入

构造语句进行测试

ibaseDao其他处也存在$,但是由于过多且不好搜索,大致审计过程如上图所示。

2、xss

可以看到存在xss过滤器,进去看一下过滤器怎么写的

过滤器要不走xss过滤则需要(!this.handleIncludeURL(req, resp))为true,那么说明this.handleIncludeURL(req, resp)返回的信息需要为false才能进入下一个过滤器,从而不走xss过滤那么分析如何让返回的结果为false,可以看到有两种方式返回结果为false,首先是最外层检验includes是否为空,那么追踪这个includes来源

追踪includes来到webconfig配置文件

可以看到includes添加了*/search.do,所以这一步无法走到false

回到刚才,查看第二种情况,可以看到就是确认请求的路径是否是includes里面的,如果是返回true,否则返回false,所以这个过滤器存在问题,只对search.do进行了过滤

那么我们去访问查看一下search.do页面,那么既然拦截了search.do页面的请求,那么我们在其他页面插入xss即可绕过

修改标题

3、文件上传

1、第一处

全局搜索upload查看结果

查看/file/upload接口

可以看到上述代码未有对上传进行限制,查看配置文件可以看到禁止上传此类后缀结尾

但是在后端发现存在基础接口解压接口

zip结尾的不在该配置文件中,可以上传一个带有jsp文件的压缩包,再调用此接口解压

上传成功,查看如何进行解压,get方式传入我们上传的路径即可进行解压

查看解压后内容

但是尝试了访问都不解析,了解到如果是springboot启动的情况,虽然能访问到jsp⽂件但是不能解析,只有在tomcat部署的情况下才能解析成功。其中有一处上传为此接口后台上传基础接口,就不复现了。

2、第二处

在审计第一处上传时发现,存在写入文件内容的地方

发现getRealTemplatePath,追进去查看

发现该方法里也存在校验路径是否存在../等路径,黑名单包括了exe,jsp,抓包测试,发现可以构造WEB-INF/manager/cms/category/form.ftl,覆盖模板文件form.ftl,然后执行我们的危险命令%3C%23assign%20ex%3D%22freemarker.template.utility.Execute%22%3Fnew%28%29%3E%24%7Bex%28%22ipconfig%22%29%7D(<#assign ex="freemarker.template.utility.Execute"?new()>${ex("ipconfig")})

这种情况需要我们进行热部署,大多数系统都是采用的热部署,热部署可以在进行部署时,不需要关闭整个应用程序,从而减少了因部署引起的停机时间。可以随时进行更新,无需计划性的维护窗口。

我们上传成功后就要去找到哪个action使用了该form

访问该接口

我们的命令执行成功。

2、第三处

不存在过滤,直接点击进行上传

同理访问此文件

4、Fastjson反序列化

可以看到导入了fastjson的jsonobject包

查找是否存在调用fastjson的地方,可以看到JSONObject.parse直接解析了jsonconfig,构造路径发现路径需要知道版本,查找到相应的版本

构造数据包进行测试

不知哪一步问题,未能接收到dnslog请求,但是根据代码确实存在反序列化。

附件:
0 条评论
某人
表情
可输入 255