Java代码审计-MCMS
0X01 环境搭建
项目地址下载:https://gitee.com/mingSoft/MCMS
当前版本:5.2.5 也可下载的releases版本 5.2.4
PS:注意Mysql版本需大于5.6以上
创建一个mcms数据库,并且导入doc/数据库文件
在application-dev
中修改数据库账户密码
运行MSApplication.java main
方法,如下图,环境无问题
管理员:msopen/密码:msopen
外置环境配置
搭建Tomcat环境
注意:环境最好使用1.8版本,作者使用15版本,打包war时一直报错,版本不对,最后将1.8设置默认版本后才成功。
编辑 pox.xml 修改打包方式为 war 包
<!-- 打包成war包 --> |
添加依赖
<dependency> |
修改启动类 src\main\java\net\mingsoft\MSApplication.java
@SpringBootApplication(scanBasePackages = {"net.mingsoft"}) |
启动类爆红,导入包路径即可。
使用maven打包,点击如下图标,设置
新建 tomcat server ,导入即可
搭建成功,如果登录后提示分页插件出错
原因:参考其他大佬的
MSServletInitializer.java 文件从项目中删除即可,因为该类也继承了SpringBootServletInitializer类,所以导致PageHelper插件被配置了两次
参考链接:https://www.zhihu.com/question/330677156
项目目录
其中我们主要关注红框的三个目录
- Action 控制器层
- biz 业务逻辑层
- dao 数据库交互层
并且在cms中并未发现全局过滤文件。
作者后端技术框架
0X02 SQL注入漏洞
MyBatis SQL 注入前置知识
根据作者发布的配置文档可知, 后端使用的是 ORM框架 MyBatis
MyBatis 的 SQL 语句可以使用注解方法写在类方法上,也可以使用 xml 的方式,而使用的最多的方式就是将语句写入 xml 文件中。
编写 xml 文件时,MyBatis 支持两种参数符号
$
– 拼接 SQL#
– 使用预编译
当语句使用 $
拼接时,则会产生 SQL 注入漏洞。
参考链接:https://www.freebuf.com/vuls/240578.html
漏洞分析
我们关注 dao 目录,可以发现作者使用的是 xml 文件方式写入的
右键点击 Find in Path 选中 File mask 在 dao 目录下搜索 xml 文件
$
搜索,发现有两条 select 语句均使用了 $
拼接参数 categoryId,跟进
漏洞点:src/main/java/net/mingsoft/cms/dao/IContentDao.xml
可以发现 这条 select 语句是属于 select 标签 id 是 query,说明是 query 方法调用
IContentDao.xml 对应的映射接口类是 net.mingsoft.cms.dao.c,此文件中并未找到 query 方法,此时我们继续跟进到父类 IBaseDao(net.mingsoft.base.dao.IBaseDao)
在父类 IBaseDao 中找到 query 方法,IContentDao 接口类 继承父类 IBaseDao,所以可调用 query 方法
此时,我们需要去寻找 query 方法的对应的实现类。
IContentDao 对应的业务接口是 net.mingsoft.cms.biz.IContentBiz,而实现类是net.mingsoft.cms.biz.impl.ContentBizImpl,同理,跟进 ContentBizImpl 的 父类 BaseBizImpl(net.mingsoft.base.biz.impl.BaseBizImpl)发现父类中实现了 query 方法,调用了 dao 层的 query 方法
现在我们只需要跟进控制器层代码,寻找 categoryId 可控点,即可完成整个调用链。
对应的控制器文件 src/main/java/net/mingsoft/cms/action/ContentAction.java 104行处
@ApiImplicitParam
注解中定义了 categoryId 参数对应的是所属栏目,使用的方法是 query
而 127行 对应的调用处是 list 并且使用 post 方法获取到上述所有需要查询的参数并放到 content 中,通过 query 方法去查询对应的数据
此时我们可以去登录,验证漏洞是否存在
漏洞验证
功能大全->文章管理 抓包
在 pageSize 后面添加 categoryId 参数
Payload: categoryId=1' and updatexml(1,concat(0x7e,databases(),0x7e),1) and '1 |
成功,同时可以使用 Mysql 数据库监控程序,查看和数据库交互的语句
注入的语句
select count(0) from ( select ct.* from ( select ct.*,cc.category_path from cms_content ct join cms_category cc on ct.category_id=cc.id WHERE ct.del=0 and (ct.category_id='1'' and updatexml(1,concat(0x7e,user(),0x7e),1) and ''1' or ct.category_id in (select id FROM cms_category where find_in_set('1' and updatexml(1,concat(0x7e,user(),0x7e),1) and '1',CATEGORY_PARENT_IDS)>0)) )ct ORDER BY ct.content_datetime desc,content_sort desc ) tmp_count |
抓包时,还发现了华点。。
rememberMe=rWugSNNLZQzRFHaq7ak+mgedIxoeZBy4qAquhNineUSo8u1NL/L4YCkX4vhDD9i1d/Hm2oBNc4ou74W/ALQNI5GYQH/tGQcYkffbwtjd4VZuoXD4D73yWZ3IGNIbVxXLJgk5WCc0VkOIizpA529fHxCurk4RLpC5K6EeX+J/TK8uGYnNtZyDz50BnbtF0UgntjTpRx++tnbB1XEpiUm4T/+AR1jH3U3RI/BCRXpAwg08lsFEBTYloTj+CqAHKMFCXOBIAdS4F/2pnHJfba7KU2JKNap0xu8maiRCs3+qYPncV3dpwzA8KyVOvnUIaNxOBSMEclpiQi1ykpwd32e4eqcC4Zey9lTJIp+znimCIszR5Qrn3SGewegH7BOrzdSkyHpDJxPWwejq9UYJ6ZkthidmrM8YVRHD8+kJGpkt/po42qP4JmGW6JP4Wt4StgsJZEO+bS/HjFwOpbDkJ3nnqZGaZzoAUmI+TaF8yPE9fmHNg01HtbiJsR8Ml6YcDXJdTOZ0xzPaGnUanggXwlL2x3DEndd0DKHfMHe8NNEe0KTMPoI+/ScWkF+Ogjomuo2mRqVMVWdYMsimMB73MFfW+9U/ugVVREqzSuH7Ym1KdwvtsCrQBMjLBz7P6d10Uvi2zL8g86u8wKhKoBg5gu8jmO8tQJkdb53VMpYNpyc8SnujThz/CQgyRjPRJ6v9lINR7rRQ0SjTU2wHIJfToaIOerDgxj2wTcw2tcLJKj8fj0DamOAdXrNhayx1xHYS16bU9PO3TteshfPCPOBrC31SKKY1RxTMBcMFrDTApI0n9eSOhDttMHp879iibCXXgH17TfrXIsymd+5iSCpkKJCruhwZJyi8TtvShgotzE0OumKObevO2p1/D2lWX1yuZ2zL2wSMtKReGo5g8ONab5bOQsp+B++LjFhLUsBLqWiBT1L8wNwJl5yTy1jgdpC2tmkz0judbdp/h9E6J9pDEhUsUllYeVyzadnCYugRDMrNbcDFusdUKOUZrz8yBLTuHcPopP1hmDJnotSnvKuAdg36mUzuipolOMGHz49OkAzQE9y8aV1S33hBMs8dCF+1bL+VZYKlpPriP5LWrEiKzf3v/L9sojsDrLzbhZjbBaZ2zkUdh9O/dyWyw4hBSNVUYmO3pjTjIWBbhiBFKjc12S6jcTP/QPkY1i9deoMhMhHuP0gYlNiL4YLpsnXnAwaElaTutHWPbQcHQhXQounbYJRIVGiXCj3aLNQ4wgOlZdLMAvcAoyRljrIrjQJcfInuQNCKMYf5d9ZbtCfB3NJyZ3H3ff56pS+ipZfJ6RMIijqMFHs448CGn0lqJjpFD/bH+CTQIEvwF7tHJw== |
网站还使用 shiro 框架,后续可以跟踪一下此漏洞。
0X03 shiro反序列化
漏洞分析
shiro配置文件:
repository/net/mingsoft/ms-basic/2.1.10/ms-basic2.1.10.jar!/net/mingsoft/config/ShiroConfig.class |
mcms作者将核心一些配置文件封装成 ms-basic2.1.10.jar
打个断点到 ShiroConfig 的 rememberMeManager
可以看见获取到默认 key 值 4AvVhmFLUs0KTA3Kprsdag==
这里获取的 MCMS-master/src/main/resources/application.yml
配置文件中的值
之前我们跟过 shiro 反序列化漏洞,知道触发点在
org/apache/shiro/shiro-all/1.8.0/shiro-all-1.8.0.jar!/org/apache/shiro/mgt/AbstractRememberMeManager.class
跟进 getRemeberedPrincipals
, this.getCookie().readValue
获取cookie值
再对比 cookie 值是否为 deleteME ,不匹配,走到 decoded = Base64.decode(base64) 解密 base64
返回一串 AES 加密的 bytes 值,走完后,会跳回到 getRemeberedPrincipals
执行下一个方法
convertBytesToPrincipals
这个方法是解密并进行反序列化
跟进 decrypt , getCipherService AES加密类,说明当前使用的加密是 AES/GCM/NoPadding 分组加密不填充模式
cipherService.decrypt(encrypted, this.getDecryptionCipherKey())
解密,getDecryptionCipherKey
获取密钥,密钥是在配置文件中获取的
通过 setCipherKey
设置密钥,rememberMeManager
返回的是 CookieRememberMeManager
对象,而 CookieRememberMeManager
继承 AbstractRememberMeManager
类
所以 AbstractRememberMeManager
构造方法接收到了密钥 4AvVhmFLUs0KTA3Kprsdag==
解密后,返回一串 序列化值
调用 deserialize 反序列化
反序列化出来的内容,就是我们当前用户的信息。
打个 payload 看看反序列的内容是不是命令执行。
payload 成功反序列化出来
漏洞验证
0X04 文件上传漏洞
漏洞分析
文件上传接口
repository/net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/ManageFileAction.class
当我们上传文件时,upload
获取上传的文件信息
而 this.upload
处理上传操作,跟进
MCMS-master/src/main/resources/application.yml
配置文件内容
uploadFileDenied
获取配置文件的中后缀名赋给 errType 是黑名单 ,而这里只获取了 .exe
和 .jsp
, 继续跟代码,查看是否有做二次验证。
后续并没有进行二次验证,在 40 行截取上传文件后缀名,在 59 行,System.currentTimeMillis()
生成一段14位的随机数和后缀名拼接组成新的文件名 ,并在 66 行循环对比后缀名是否和黑名单中相匹配。
后缀名不在黑名单后,就会执行保存操作,这段验证操作比较简单,主要是对比黑名单,而作者的黑名单后缀名过滤不够完整,用 jspx
即可绕过。
漏洞验证
栏目管理 -> 上传缩略图
文件管理 -> 上传缩略图
系统设置 -> 网站LOGO
均调用 upload
代码,只是上传目录不同。
上传 jspx
文件,跟踪调用链
可以看见 jspx
成功上传
0X05 ZIP自解压(上传webshell)
漏洞分析
ZIP文件上传点
net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/ManageFileAction.class
整个ZIP文件上传流程和前面分析的流程一致。
自解压
repository/net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/TemplateAction.class
上传一个zip,然后抓包,上传成功后,会返回一个zip包上传路径
随即会自动调用 unzip 解压文件,fileUrl 是我们上次的zip文件地址
断点打在 unzip
,也没什么复杂的处理,得到文件路径,判断路径中是否有 ../
或 ..\\
zip文件,再根据路径获取 zip 文件。
unzip 解压 , 跟进,可以发现是遍历 zip 文件中的所有文件
返回解压后的所有文件,del
判断是否解压,解压完成后,删除压缩包文件。
中间也没有做任何的文件过滤,判断啥的,后缀名也是使用的黑名单,使用burp可以修改后缀名上传webshell。
漏洞验证
生成一个哥斯拉 webshell
, 将webshell放到 zip 压缩包中
上传压缩包
访问路径
http://localhost:8081/ms_mcms_war/template/1//default/2.jsp
哥斯拉连接
绕过后缀名
0X06 模板注入
漏洞分析
该CMS使用的模版引擎是freemarker,该模版引擎是存在模版注入的
登录管理后台,在后台模板管理界面,是可以修改模板文件内容的
编辑模块调用的是
/net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/TemplateAction.class writeFileContent
可以看见这段代码中也没有对内容进行过滤,只是获取了内容,名字,然后将编辑后的内容和原内容对比,然后修改成功。
生成主页
MCMS-master/src/main/java/net/mingsoft/cms/action/GeneraterAction.java
跟进 generate
方法
rendering
解析模板文件
正常解析模板文件,当注入恶意代码时,漏洞被成功触发。
漏洞验证
修改模板文件
payload: |
保存,点击内容管理 -> 静态化
模板选择对应修改的文件,生成主页
触发成功。
OXO7 任意文件删除
漏洞分析
漏洞点
net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/TemplateAction.class
代码无任何过滤,获取要删除的文件名,deleteDirectory
执行删除
漏洞验证
修改为 ../1
, 成功删除