MCMS全面审计
MCMS 是 J2EE 系统,完整开源的Java CMS,基于SpringBoot 2架构,前端基于vue、element ui。为开发者提供上百套免费模板,同时提供适用的插件(文章、商城、微信、论坛、会员、评论、支付、积分、工作流、任务调度等...),一套简单好用的开源系统、一整套优质的开源生态内容体系。
Mcms的5.2.8是非常经典的版本,我们今天就对他进行审计,争取把所有出现的洞都拿下
搭建过程
官方网址: https://gitee.com/mingSoft/MCMS
先将依赖下载下来
将数据库名字改一下
加载一下mysql文件
导入 doc/mcms-5.2.8.sql
运行MSApplication.java main方法
这里我运行的时候,端口冲突了,改端口的文件在application.yml
然后运行MSApplication.java main方法
出现这个是正常的,后面加上路径/ms/login.do
http://localhost:8000/ms/login.do
输入官方给的用户名密码msopen/msopen,登录。
第一个漏洞:前端反射性xss
http://localhost:8000/mcms/search.do
我们找到 net.mingsoft.basic.filter.XssHttpServletRequestWrapper并添加断点,再次触发漏洞,看到一个完整的调用栈, net/mingsoft/basic/filter/XssHttpServletRequestWrapper.java
throw new BusinessException("参数异常:"+ content);此处直接将content原样拼接到报错中
第二个漏洞:文件上传第一处
/ms/file/uploadTemplate.do 打包jsp文件为zip,上传模板即可,前端时间监听,会发送解压请求
修改上传
返回了路径地址
http://localhost:8000/template/1/1709723660886.txt
在源文件中也能找到
路径:src\main\webapp\template\1\
net.mingsoft.basic.action.ManageFileAction#uploadTemplate
定位到上传的地方
我们看到虽然存在非法路径过滤函数,查看函数内容,仅仅是对 ../ 进行了校验,通过绝对路径仍然可以绕过
上传点不严谨
net.mingsoft.basic.action.BaseFileAction#uploadTemplate
所以绝对路径可以绕过
第三个漏洞文件上传第二处,网站logo
相同的操作
回显了路径
http://localhost:8000/upload/1/appLogo/1709726541909.txt
并且在后台找到
第四个漏洞命令执行
在后台除了上传模版,还有编辑模版
我们看到参数 fileName 通过绝对路径指定了文件名,所以我们可以通过修改 fileName 来实现绝对路径写入
在后台我们也找到了\src\main\webapp\template\1\default
漏洞分析
net.mingsoft.basic.action.TemplateAction#writeFileContent
我们可以通过fileName写入文件
第五个SQL注入漏洞
1;select if(substring((select database()),1,4)='mcms',sleep(5),1)//and//1&fieldValue=1&id=1&idName=1\
mybatis框架这里就全局搜使用$进行拼接的
路径如下
/net/mingsoft/ms-base/2.1.13/ms-base-2.1.13.jar!/net/mingsoft/base/dao/IBaseDao.xml
进一步跟进queryBySQL
然后在/net/mingsoft/base/biz/impl/BaseBizImpl.java这里进行了重写queryBySQL,然后调用getDao().queryBySQL
然后发现在/net/mingsoft/basic/action/BaseAction.class#validated 验证的时候进行调用
父类validated方法的处理逻辑
validated方法中新建了一个HashMap,然后将字段名filedName和字段值filedValue以键值对的格式加进了map中。然后调用了appBiz.queryBySql方法
继续跟,这时候只要找到前端路由中能调用validated就可以了,然后发现在/net/mingsoft/ms-mdiy/2.1.13.1/ms-mdiy-2.1.13.1-sources.jar!/net/mingsoft/mdiy/action/PageAction.java#verify
调用了validated方法
从源码中发现,在调用vertify方法时传入了String fieldName/String fieldValue/String id/String idName这四个参数。然后调用了父类的validated方法。
验证一下
http://127.0.0.1:8000/ms/mdiy/page/verify.do?fieldName=222&fieldValue=b&id=c&idName=21
四个传入到参数里面
进行布尔判断:
如果verify = false就进如if
如果verify != false就进如else
第六个漏洞,shiro反序列化漏洞
找到key
d3d3bWluZ3NvZnRuZXRtcw==
net.mingsoft.basic.action.TemplateAction通过fileName可以指定目录删除,并且不像文件上传等对…/进行限制,这就导致我们可以通过…/进行任意目录删除
黑盒复现
第七个漏洞文件上传
这个漏洞是在第一个后台命令执行的基础上发现的,两个类位于同一个文件内
net.mingsoft.basic.action.ManageFileAction#upload
虽然存在非法路径过滤函数checkUploadPath,查看函数内容,仅仅是对 ../进行了校验,通过绝对路径仍然可以绕过。
对文件的上传是利用了
net.mingsoft.basic.action.BaseFileAction#upload
漏洞复现
POST /ms/file/uploadTemplate.do HTTP/1.1
Host: localhost:8000
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Content-Length: 506
Accept: */*
Accept-Encoding: identity
Accept-Language:zh-CN,zh;q=0.9
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryz3nUf5Hws24R3B3A
Cookie:rememberMe=8g+sOxE9YAfFQLrPf68EhVu1O3t+ojzqle9v7niIoBPQcprUFAgL/jCiAMmN7nRxi/C3wC/aw4FjsSsoYIIPw32OVU40rLZ6t9anywaQH+CdgKAFtdSXWuDYlMFLLNMYg2EBiL5Sa/MvLhZpJoSDVA4qVHNpOL5hH3eT4oqECz9dyEtrm0y5DLAXoedMR9UbeCPkGM9x2E72KaHr/l2DZqQo6aMO3Z5MvZk4Sn/GTZ8I4kbRQH35M9QVYmBFtWMZaNob1XnrKUN3kmwnjMhu1CsdhzR/Uuo/VX4tmzbsGlV0s/JpSu74u+IZwZXT7pyDGZ7XCbTMCPAt2SWxGbaQwIQ9MbHn/Yatqy0C5WBQ7/PXyFXHRIPC/fJ0xkD5Rk2xgXzYrZE4c5ufWdpNF1OZ6W187Vo63dalpy7TGVPk95XKIfxbXXX7VCRV/JQhCRhjPR08Nz9+YzO72oMY+E3zNLmRuQ7oiI1V2f9IazgTQrTIa9DAS+MWlJQX4qZsDVR9WBGxmjp91ozaMIQRoEU5RqRC1xvZKK/oo/KqUWZE/yCaYVh0oi1UPf2wBlhN+XnabPb1CXiqxc0o/7BE2jMGGhakgaCeUxQzdT8ij6b1j56a+GKtnzNL73wzbbLEnc9bjy0Mnlz2222YthPx/32UselOOTDvXyBjyeZMeeEuBpSP2weHyjxwtcWEVJHvMDbA/nK36h9ZVZ1Y2n+B7aRUW8JhWPiEvovXSntp+NKXmNapXO8xsNDNpoU6so1EJz3dV4wFW8aaL7scqA6YRCbXrCYPlHB9MSzdr59iGUmdMTqj5zS3wvTfxxrk7UjA6jIfyo9NuTYKu7d9rsz9txf5Kf8KF9C4s6y4nKHcTU2pinIDBAy7hizEWh5IYHE+3Td3HBvXWEWeKlagfLwFEVm0wiyUK1z+v3qyrssq5bZ9lljRg7DQDYI+rOivpyGLFQk3pKlLtk5dX1Q4eWtZQLJA/u1jOEGyOQLlI8LSidqYgj8A9YEcIcE9Q+2iEi8lMn87k//ATAybXKL9UzlJgcn3X5ZtLxS1M/tuXi+iXr/TCnFIMobiRzBKLtZqVrzPOpUYwYI1Qx2hmpNJY0qz8p5J9TnvPiHtwF/QHbdQVTyKDZb6itpvdohGd0/36Z2ae7X67CrgG1hIbvLvsqK1ofS+u/MTQv3stS+qVfJ50bAMwnt2xk/2qTStUAsBa4dsZp0jAjDBlNpXU5xRYBrTtvsjwJkqhCtMUIYRwtBmEKYfpICg8o5ZUsyCpHDwnOGL2VckFD9bLbYWqC3omWtreGFxO7EsGpbbJSRAQPOy6nFhwOyshAcd4A9B4O9T68G7on10Q3X4Bs5x7ikFMJw=; SHIRO_SESSION_ID=49ef8d09-968d-4c42-86c8-0ff9019475e1
Origin: http://localhost:8000
Referer: http://localhost:8000/ms/template/list.do?template=1/default
Sec-Fetch-Dest: empty
Sec-Fetch-Mode:cors
Sec-Fetch-Site: same-origin
sec-ch-ua:"Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
------WebKitFormBoundaryz3nUf5Hws24R3B3A
Content-Disposition: form-data; name="uploadPath"
/
------WebKitFormBoundaryz3nUf5Hws24R3B3A
Content-Disposition: form-data; name="uploadFloderPath"
true
------WebKitFormBoundaryz3nUf5Hws24R3B3A
Content-Disposition: form-data; name="rename"
false
------WebKitFormBoundaryz3nUf5Hws24R3B3A
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/html
test
------WebKitFormBoundaryz3nUf5Hws24R3B3A--
同理在后台我们也找到我们刚刚上传的文件
第八个漏洞文件上传
在文章管理地方新建文章,上传的文章缩略图
上传一个zip
http://localhost:8000/ms/template/unZip.do?fileUrl=/upload/1/cms/content/1709908541074.zip
漏洞分析,通过全局搜索
form.ftl这里有个文章缩略图上传点看看他有没有做过滤然后根据action找到上传接口
经过查找在FileAction.class这里只判断了../防止目录跳跃,继续往下看点击继承的那个类
BaseFileAction.class发现他这做了后缀判断,接着看是在哪调用的过滤
denied进行全局搜索
发现只有过滤了.exe,.jsp,.jspx,.sh这些后缀名,说明除了这四个其他均可上传
经过查找TemplateAction.class里有个专门解析zip的接口
那么结合上面的过滤,可以构造以下利用链:上传zip,可以通过zip包含jsp恶意文件上传,然后调用这个接口去解析zip从而解析成jsp,导致恶意jsp文件上传成功
第九处:SSTI注入:Freemarker注入
源码位置在:net\mingsoft\base\util\FtlUtil.java
如果加上cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);漏洞就可以修复
访问指定路由后,会调用generate()生成主页\
其中主要是对map进行一些初始化操作,并通过rendering()进行渲染
最后调用process()进行渲染,造成代码执行
漏洞复现
在模版管理的index.html中打入payload
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
在静态化中生成主页
第十处漏洞Fastjson<=1.2.80反序列化存在但难以利用
在源码中存在fastjson组件
发现存在漏洞,但是难以利用
第十一处漏洞sql注入
思路:MCMS是使用mybaits作为持久层框架的,当在与接口对应的xml文件中使用 ${String} 时,会将String拼接进SQL语句。还有 <include></include>标签,会将一段复用的SQL代码拼接入当前语句中。mybatis的xml有对应java代码interface,xml中的id对应interface中的方法先给出明确的脉络,方便之后跟进不少的代码从xml->interface->impl(实现)->Controller->抓包构造,全局搜索 ${
进入\CategoryDao.xml
跟进include中的net.mingsoft.base.dao.IBaseDao.sqlWhere,文件为net/mingsoft/base/dao/IBaseDao.xml 在jar包中 这里需要注意:
$(item.field)被直接拼接在SQL语句
${item.field},item是collection="sqlWhereList"的别名,也就是${sqlWhereList.field}
传递的参数\sqlWhereList,需要构造的是其中的field
思路回到ICategoryDao.xml,找select id="query"对应的接口
找实现query的类,有三个,但只有一个接收传参 net/mingsoft/base/biz/impl/BaseBizImpl.java
public abstract class BaseBizImpl<M extends BaseMapper<T>,T> extends ServiceImpl<M,T> implements IBaseBiz<T> {
@Override
public List<T> query(BaseEntity entity) {
TODO Auto-generated method stub
return getDao().query(entity);
}
...
}
这是个抽象类,继续找,红线上为库中的,红线下为项目中的,先看项目中的
以net/mingsoft/cms/action/CategoryAction.java为例,获得地址:url为
@RequestMapping("/${ms.manager.path}/cms/category"),
${ms.manager.path}`在配置中为ms
找到了query,categoryBiz.query;还没找到它的参数sqlWhereList,看看CategoryEntity,没有
看父类BaseEntity,找到sqlWhereList和SQLwhere,SQLwhere是[{}]形式,这里还看到一个眼熟的东西,JSONObject.parseArray,
public abstract class BaseEntity implements Serializable{
...
/
自定义SQL where条件,需要配合对应dao.xml使用
/
@JsonIgnore
@XmlTransient
@TableField(exist = false)
protected String sqlWhere;
@JsonIgnore
@XmlTransient
public String getSqlWhere() {
return sqlWhere;
}
public void setSqlWhere(String sqlWhere) {
this.sqlWhere = sqlWhere;
}
@JsonIgnore
@XmlTransient
public List getSqlWhereList() {
if(StringUtils.isNotBlank(sqlWhere)){
try {
return JSONObject.parseArray(sqlWhere,Map.class);
}catch (Exception e){
e.printStackTrace();
}
}
return Collections.EMPTY_LIST;
}
...
}
漏洞复现:
对field进行报错注入,将其替换就可以了
第十二处SQL注入
该cms的mybatis映射文件直接写在了dao层,由于${}会有SQL注入问题,所以直接搜索${},发现query方法中,categoryId参数可能存在SQL注入
根据namespace可知该语句的映射接口类是net.mingsoft.cms.dao.IContentDao 在IContentDao中并没有发现query方法,但他继承了IBaseDao,而query就在其中
下面就要看哪里实现了query方法,根据分层逻辑,业务层对应接口类为:net.mingsoft.cms.biz.IContentBiz ,而他的实现类是net.mingsoft.cms.biz.impl.ContentBizImpl而其中没有调用该方法,所以向上找他的父类BaseBizImpl
同样根据分层逻辑,找到了控制层的net.mingsoft.cms.action.web.ContentAction,该层的接口为/cms/content 该类的list方法中,会调用contentBiz属性的query方法参数为content contentBiz是IContentBiz类型的,IContentBiz的实现类是ContentBizImpl,接着他的父类BaseBizImpl实现了query方法 content属性是ContentBean类型的,而ContentBean的父类是CategoryEntity它定义了categoryId属性,并实现了对应的getter、setter方法 这里未对前端用户输入的参数进行过滤,另外该CMS全局也没有针对SQL注入的过滤,所以只需要传入categoryId参数,将查询语句闭合即可导致SQL注入
漏洞复现
由于定义的是PostMapping,因此需要post传参
categoryId=1'and updatexml(1.concat(0x7e.user().0x7e).1)and'1