Java代码审计-MCMS
source link: https://timeshu.github.io/2022/01/15/Java%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1-MCMS/#%E6%BC%8F%E6%B4%9E%E9%AA%8C%E8%AF%81-5
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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
, 成功删除
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK