帆软报表FineReport历史漏洞分析(一)
字数 5203
更新时间 2026-03-18 14:21:46
帆软报表FineReport历史漏洞分析教学文档
1. 项目概述与调试准备
1.1 项目结构
帆软报表FineReport是一个Java Web报表系统,其核心目录结构如下:
bin/: 包含启动脚本(如designer.vmoptions)。jre/: Java运行环境(版本通常为8u191)。server/: 包含Tomcat服务器相关依赖。webapps/webroot/WEB-INF/lib/: 存放服务器核心JAR文件,版本更新和漏洞分析主要关注此目录。webapps/webroot/help/: 包含默认的测试数据库(SQLite)。plugins/: 存放插件。
1.2 调试环境搭建
- 远程调试: 在
bin/designer.vmoptions文件中添加远程调试参数(如-agentlib:jdwp=...)。 - 行号信息: 使用工具(如
ClassLinefix)为WEB-INF/lib下的JAR文件添加行号信息,以便在IDE中准确调试。 - 库配置: 将服务器JAR和Tomcat的JAR添加到IDE的库依赖中。
- 断点验证: 在关键类(如
com.fr.third.springframework.web.servlet.DispatcherServlet#doDispatch)设置断点,访问http://localhost:8075/webroot/decision/system/info测试是否成功断住。
1.3 版本识别
- 方法1: 访问
http://{host}:{port}/webroot/decision/system/info,查看返回信息。 - 方法2: 访问
http://{host}:{port}/webroot/decision/login,查看网页源代码中的版本信息。 - 官方文档: 历史版本和补丁信息可查阅官方文档。
2. 核心机制分析
2.1 Servlet注册逻辑
Servlet(如ReportServlet)的注册不依赖web.xml,而是通过activator机制。
- 启动入口: 容器启动时,会调用
com.fr.startup.FineWebApplicationInitializer#onStartup。 - 模块加载: 框架解析配置文件
com/fr/config/starter/server-startup.xml,其中定义了各个模块的activator类。 - 实例化与启动: 通过反射创建
activator实例(如ReportActivator),并调用其start()方法。 - Servlet注册: 在
activator的start()方法中(例如ReportActivator#initReportServlet),通过ServletContext的原生API(addServlet)完成Servlet的注册,并指定映射路径(如/ReportServer/*)。
2.2 路由分析
系统存在两套主要请求处理入口:
- Servlet入口: 以
/webroot/ReportServer开头的请求,由ReportServlet处理,最终进入com.fr.web.core.ReportDispatcher#dealWithRequest。此路径不过Spring Interceptor,可能绕过部分鉴权。 - Spring MVC入口: 以
/webroot/decision开头的请求,由Spring的DispatcherServlet处理,经过完整的Spring MVC流程(包括Interceptor),最终也可能路由到ReportDispatcher#dealWithRequest。
获取完整路由映射的方法:
- 在调试环境中,于
HttpServletRequest参数相关方法内执行表达式代码,可导出所有Servlet映射。 - 在Spring MVC上下文中,可从
RequestMappingHandlerMapping中获取所有Controller的映射关系。
3. 漏洞原理与利用分析
漏洞的根本原因在于模板表达式注入。FineReport内置了${...}表达式解析功能,并提供了sql()等函数。当用户可控的输入(如参数、SessionID)被拼接进待解析的字符串并最终交给TemplateUtils.render()等方法处理时,攻击者注入的表达式(如${sql(...)})就会被执行,导致SQL注入。
3.1 漏洞一:/print/ie/pdf SQL注入写文件 (CVE-2023-...)
- 影响版本: 11.0 <= version < 11.0.24
- 漏洞路由:
GET /webroot/decision/nx/report/v9/print/ie/pdf - 漏洞分析:
- 入口: 请求由
NXController.pdfPrintForIEV9处理,最终调用PDFPrintPrintForIEHandler.handleRequest。 - 参数获取: 从HTTP Header中获取
sessionID参数,经过NetworkHelper.getHTTPRequestEncodeParameter处理。 - 关键处理: 该函数会对输入进行
CJK解码(将[41][42]格式的十六进制转回字符)和URL解码。这常被用于绕过WAF。 - 注入点: 攻击者控制的
sessionID值经过字符串拼接,传入TemplateUtils.render()进行解析。如果sessionID包含${sql(...)}表达式,其中的SQL语句将被执行。 - SQL函数:
sql(datasource_name, sql_statement, ...)函数用于执行SQL查询。默认存在FRDemo这个SQLite数据源。 - 利用目标: 通过SQLite的
ATTACH DATABASE、CREATE TABLE、INSERT语句,将恶意内容写入JSP文件,从而获取WebShell。
- 入口: 请求由
- WAF绕过技巧:
- CJK编码: 将载荷转换为
[HEX][HEX]格式。 - URL编码: 结合FineReport内置的
DECODE()函数进行URL解码。 - 零宽度空格(U+FEFF)绕过SQL黑名单: 在SQL关键字前添加
%EF%BB%BF(U+FEFF的URL编码),在解析时该字符被移除,但WAF检测的关键字(如" ATTACH ")因前后缺少空格而绕过检查。
- CJK编码: 将载荷转换为
- POC示例:
GET /webroot/decision/nx/report/v9/print/ie/pdf HTTP/1.1 Host: 127.0.0.1:8075 sessionID: {{urlesc(${sql('FRDemo','SELECT sqlite_version()',1)})}} - 修复方案:
- 对
sessionID参数进行有效性校验,禁止恶意表达式。 - 在SQL语句安全检测中,增加了对
U+FEFF字符的过滤处理。
- 对
3.2 漏洞二:/view/ReportServer? SQL注入写文件
- 影响版本: 11.0.1 <= FineReport <= 11.0.28
- 漏洞路由:
GET /webroot/decision/view/ReportServer?... - 漏洞分析:
- 入口: 请求由
ReportServlet处理,最终进入ReportNoSessionDispatcer.doPost。 - 参数获取: 通过
request.getQueryString()直接获取未经解码的原始查询字符串。 - 注入点: 查询字符串被直接拼接到模板字符串中进行解析。由于Tomcat对URL有字符限制,需利用
DECODE()函数对载荷进行编码。 - 利用链: 与漏洞一类似,利用SQLite写文件。同样可借助
U+FEFF绕过早期版本的黑名单检测。
- 入口: 请求由
- POC思路:
GET /webroot/decision/view/ReportServer?${sql('FRDemo',DECODE('%EF%BB%BFATTACH%20...'),1,1)} HTTP/1.1 - 修复方案: 修改逻辑,不再对
request.getQueryString()获取的参数进行模板渲染。
3.3 漏洞三:export/excel SQL注入写WebShell
- 影响版本: 影响多个版本,具体需参考官方公告。
- 漏洞路由:
GET /webroot/decision/nx/report/v9/largedataset/export/excel - 前置条件: 该接口需要有效的
sessionID。 - SessionID获取:
- 早期方法:
/webroot/decision/view/report?op=getSessionID&reportlets=[{'reportlet':'/'}] - 后期方法 (绕过Interceptor):
/webroot/decision/view/report?op=getSessionID&viewlets=[{'reportlet':'/'}]。此请求可由ReportGeneralRequestChecker处理,其checkRequest默认返回true。 - 直接访问: 直接访问
/webroot/ReportServer?op=getSessionID&...可绕过Interceptor,但需注意参数格式。
- 早期方法:
- 漏洞分析:
- 入口: 请求由
LargeDataSetExportController.exportExcelV9处理。 - 参数解析: 请求体中的
params参数是一个XML字符串,其中定义了Parameters和LargeDatasetExcelExportJS结构。Parameter的value可通过t="Formula"指定为一个表达式。 - 注入点: XML解析后,表达式(如
sql(...))被提取并交由Calculator计算执行。 - 利用链: 利用SQLite的
VACUUM INTO语句将整个数据库写入JSP文件。为生成干净的WebShell,需要先清空或备份原数据库。
- 入口: 请求由
- 新版SQL黑名单变化: 在后续修复中,
CREATE、DROP等关键字被移出黑名单,但VACUUM被加入。 - POC结构:
HTTP请求需将上述XML进行URL编码后放入<R> <Parameters> <Parameter> <Attributes name="p1"/> <Object t="Formula"> <!-- 备份原数据库 --> <Attributes>sql('FRDemo', DECODE('VACUUM INTO \"./frdemo.db.bak\";'),1,1)</Attributes> </Object> </Parameter> <Parameter> <Attributes name="p2"/> <Object t="Formula"> <!-- 清空sqlite_master,准备写入新数据 --> <Attributes>sql('FRDemo',DECODE('PRAGMA writable_schema=1; DELETE FROM sqlite_master;'),1,1)</Attributes> </Object> </Parameter> <Parameter> <Attributes name="p3"/> <Object t="Formula"> <!-- 创建表并插入WebShell内容,最后用VACUUM INTO写出 --> <Attributes>sql('FRDemo',DECODE('CREATE TABLE t(p text); REPLACE INTO t VALUES(''<% ... %>'); VACUUM INTO \"../webapps/webroot/shell.jsp\";'),1,1)</Attributes> </Object> </Parameter> </Parameters> <LargeDatasetExcelExportJS dsName="ds1" ... /> </R>params参数,并携带有效的sessionID。 - 修复方案:
- 在SQL黑名单中加入了
VACUUM关键字。 - 加强了
sessionID的获取鉴权逻辑,ReportGeneralRequestChecker的检查列表不再为空,阻止了未授权获取。
- 在SQL黑名单中加入了
4. 通用利用与防御要点
4.1 利用链关键点
- 表达式注入: 寻找用户输入点,其值最终会进入
TemplateUtils.render()或类似表达式解析函数。 - SQL函数: 利用
sql(datasource_name, sql_statement, ...)函数执行任意SQL。 - 默认数据源: 利用内置的
FRDemo(SQLite) 数据库。 - 文件写入: 利用SQLite的
ATTACH DATABASE ... AS+CREATE TABLE ... AS SELECT 'shell'或VACUUM INTO语句写文件。 - JSP解析: 写入JSP文件后,需访问
/webroot/decision/file?path=org.apache.jasper.servlet.JasperInitializer&type=class来加载JSP解析器。
4.2 防御与修复建议
- 输入校验: 对所有用户输入的参数进行严格的白名单校验,特别是用于拼接模板表达式的参数。
- 禁用危险函数: 在生产环境中,应禁用或严格限制
sql()等可执行数据库操作的表达式函数。 - 权限最小化: 运行FineReport的数据库用户应遵循最小权限原则,避免拥有文件写入权限。
- 安全更新: 及时关注官方安全公告并更新到已修复的版本。
- WAF规则: 部署WAF,针对
${、sql(、ATTACH、VACUUM INTO等关键字进行检测,并注意绕过手法的识别。
5. 后续研究方向
本文未详尽分析的漏洞方向包括:
/remote/design/channel反序列化漏洞及其多次绕过手法(涉及TreeBag、HashMap、ImmutableSetMultimap等gadget)。- FVS插件相关的漏洞。
- 任意文件读取漏洞。
附录:参考资源
- 官方更新日志与安全公告
- FineReport 设计器函数文档
- 相关安全研究人员的技术分析文章
相似文章
相似文章