官方通告
https://help.fanruan.com/finereport/doc-view-4833.html

从官方通告我们可以知道漏洞点在某个 export/excel 接口
漏洞分析
我们这里以11.0版本为例


一共找到两个版本,分别调用的Handler如下:
1 | com.fr.nx.app.web.handler.export.largeds.LargeDatasetExcelExportHandler |
分别跟进看了一下,解析的逻辑大差不差


暂且不管,随便调一个往下看,这里先看v9
官方说是SQL注入漏洞,那么我们重点关注sql相关语句,跟到doHandle方法下个断点

前两行分别是从请求当中的sessionID来获取一个模板对象以及对应的Calculator,主要是用于处理各种公式运算

这里的sessionID是每个表格每次导出所对应的sessionID值,随便导出一个示例表格可以得到
随后跟进initCreator方法

该流程一共需要三个参数(排除sessionID):
_parameters_
从参数中获取,json格式,结构满足如下条件:
params
从参数中获取,xml格式,结构满足如下条件:

functionParams
从参数中获取,json格式,结构满足如下条件:

可以看到这3个参数都是可控的,而其中涉及到SQL查询的地方在下面位置
com.fr.io.exporter.excel.direct.WorkbookDataCreator#build
com.fr.io.exporter.excel.direct.WorkbookDataCreator#init


而init方法中有个很熟悉的字眼:renderSql方法,跟进


这个方法是某个nday中的漏洞点,可以处理一些帆软内置的函数,其中第二个参数为要处理的字符串,按道理来说只要能控制this.tableData.getQuery()的值就能执行内置函数,但我们跟进到这里会发现,query的值是不可控的



这里的TableData只能从数据源中获取,而数据源我们只能控制dsName,所以这里这个方法是用不了的,那么还有哪里可以控制呢
回头看还有一个熟悉的字眼:
com.fr.nx.app.web.v9.handler.handler.largeds.LargeDatasetExcelExportHandler#dealParam

巧了,正好就是某nday的触发点
com.fr.script.Calculator#evalValue(java.lang.String)

刚刚我们提到com.fr.script.Calculator是处理公式运算,其中包含一些帆软内置函数的处理,这里使用的是帆软自己定义的一套表达式引擎

继续跟进


通过parse方法对表达式进行解析,最后查找到对应的function类来进行处理
而我们从官方文档可以知道,帆软有个内置的sql函数可以用来执行语句,因此我们只需要控制这里的evalValue参数即可

到这里我们其实可以看出来,这个漏洞并不像真正意义上的sql注入,而是帆软内置函数和表达式执行的问题,下面我们尝试利用
利用流程
控制evalValue参数
刚刚分析了整个流程,回头看一下怎么控制evalValue的参数
com.fr.nx.app.web.v9.handler.handler.largeds.LargeDatasetExcelExportHandler#dealParam

共有两种情况,都与var17有关,而var17的来源是var3.getParameters(),即LargeDatasetExcelExportJavaScript.getParameters(),往回溯源


即从params参数中获取,也就是我们最开始分析到的3个参数,我们依次看一下怎么构造
__functionParams__构造

没什么特殊的要求,满足json格式即可:{}
functionParams和params构造

这里我们需要综合考虑,以及两种情况
第一种情况:
令var19为null,走if分支,还要保证var17的值是Formula的实例,这一步比较麻烦,所以我们优先考虑下面情况
第二种情况:
令var19不为null,走else分支,只需要让var17通过replaceAll处理后就可以执行
那么这种情况下的functionParams和params需要满足:
1 | JSONObject var19 = (JSONObject)var8.get(var17.getName()); |
- var8中存在键名和var17.getName()同名的键值对,并且键值也要满足json格式,不能为null
- var17是遍历LargeDatasetExcelExportJavaScript实例中的parameters,所以xml需要存在Parameters节点
functionParams
满足json格式随便给个键值对:{“p”:{“x”:2}}
params
通过LargeDatasetExcelExportJavaScript获取parameters,而参数是在下面位置设置的



readXMLObject方法是一个用状态机 + 回调的方式处理xml格式数据的框架方法,用于处理xml各个节点的数据,我们可以通过帆软本身的GeneralXMLTools.writeXMLableAsString方法来生成
1 | import com.fr.base.Formula; |
其中Parameter中的name值和我们前面给的functionParams的值对应(p),内容为一个表达式(=1+1),dsName没有要求,默认有个ds1,实测随便传都可以
请求包构造
总结一下我们的三个参数以及sessionID:
1 | sessionID:自行生成或通过sql注入获取 |

构造请求包如下:
1 | GET //webroot/decision/nx/report/v9/largedataset/export/excel?functionParams=%7B%22p%22%3A%7B%22x%22%3A2%7D%7D&__parameters__={} |
下断点发包


跟进dealParam

此时var19不为null,进入else分支,获取到的表达式为:=1+1


成功执行普通表达式,使用带内置函数的表达式方法类似,例如sql函数:

过滤
在表达式执行的过程中,如果涉及到sql执行,会进行单独的校验处理


com.fr.cbb.dialect.security.JDBCSecurityChecker#check

处理方式大家参考某nday就行了
结果

EXP出于某些已知原因就不给出,xdm可以自己审一下