Featured image of post 1219 FineReport导出excel处sql注入漏洞复现

1219 FineReport导出excel处sql注入漏洞复现

初始信息

已知export/excel端点具有sql注入,先使用jadx反编译,全局搜索export/excel,找到com/fr/nx/app/web/v9/handler/handler/largeds/LargeDatasetExcelExportHandler.class

官方文档: https://help.fanruan.com/finereport/doc-view-2597.html

@override 基类的handle方法
 protected void doHandle(HttpServletRequest var1, HttpServletResponse var2, String var3) throws Exception {
        TemplateSessionIDInfo var4 = (TemplateSessionIDInfo)SessionPoolManager.getSessionIDInfor(var3, TemplateSessionIDInfo.class);
        Calculator var5 = this.getCalculatorFromPara(var1, var2, var3);

        try {
            WorkbookDataCreator var6 = this.initCreator(var5, var1, var2, var4);
            DirectExcelExportPool.getInstance().export(var6, var2.getOutputStream());
            MetricRegistry.getMetric().submit(FocusPoint.newBuilder().id("function_plugin_com.export.ds").text(var4.getRelativePath()).source(Original.EMBED).username(var4.getWebContext().getUserName()).ip(WebUtils.getIpAddr(var1)).build());
        } finally {
            ExportSessionManager.getInstance().removeExportSession(var3, "excel");
        }

    }


private Calculator getCalculatorFromPara(HttpServletRequest var1, HttpServletResponse var2, String var3) {
        TemplateSessionIDInfo var4 = (TemplateSessionIDInfo)SessionPoolManager.getSessionIDInfor(var3, TemplateSessionIDInfo.class);
        if (var4 != null) {
            Calculator var5 = var4.createSessionCalculator(var1, var2);   //跟入发现request未使用
            var5.pushNameSpace(ParameterMapNameSpace.create(WebUtils.parameters4SessionIDInfor(var1))); // 收集request中的参数信息到一个hashMap里,方便后续统一调用
            return var5;
        } else {
            return null;
        }
    }





private WorkbookDataCreator initCreator(Calculator var1, HttpServletRequest var2, HttpServletResponse var3, TemplateSessionIDInfo var4) throws Exception {
        String var5 = WebUtils.getHTTPRequestParameter(var2, "__parameters__");
        Map var6 = this.getParamsMap(var5);   
	// Json格式解析__paraemters__参数
        LargeDatasetExcelExportJavaScript var7 = this.getEntity(var2);
	// 获取http请求中params参数作为xml字符串生成xml(已过滤外部实体注入) 其中会根据输入的xml为JavaScript对象赋值。
	// 映射如下 xml->javascript_obj : {exportFilename:filename,dsName:DsName,colNames:ColNamesMap}
        String var8 = var7.getFileName();
        String var9 = var7.getDsName();
        LinkedHashMap var10 = var7.getColNamesMap();
	// 以上变量均可控
        DirectExcelExportModel var11 = new DirectExcelExportModel();
        TableDataSource var12 = var4.getTableDataSource();
        if (StringUtils.isNotEmpty(var8) && var8.startsWith("=$")) {
            var8 = (String)var6.get("__filename__");
        }
	// var8可控
        if (StringUtils.isEmpty(var8)) {
            var8 = getDefaultFileName(var2, var4, var9);
        } else if (var8.endsWith(".xlsx")) {
            var8 = var8.substring(0, var8.lastIndexOf(".xlsx"));
        } // 文件名处理,无视

        this.setRes(var3, Browser.resolve(var2).getEncodedFileName4Download(var8));
	// 设置response body参数
        var11.setDataSource(var12);
        var11.setSessionID(var4.getSessionID());
        if (StringUtils.isEmpty(var9)) {
            throw new Exception("No datasource name specified for exportation.");
        } else {
            var11.setDsName(var9);
            this.dealParam(var11, var1, var7, var2, var4, var6); //往下看
            if (!var10.isEmpty()) {
                var11.setColNamesMap(var10);
            }

            return WorkbookDataCreator.build(var11);  // 根据模型查表,构建数据导出
        }
    }


private void dealParam(DirectExcelExportModel var1, Calculator var2, LargeDatasetExcelExportJavaScript var3, HttpServletRequest var4, TemplateSessionIDInfo var5, Map<String, Object> var6) throws UtilEvalError {
        String var7 = WebUtils.getHTTPRequestParameter(var4, "functionParams");
        Map var8 = this.getParamsMap(var7);  // 可控Map
        ParameterProvider[] var9 = var3.getParameters();
        ParameterMapNameSpace var10 = ParameterMapNameSpace.create(var9);
        NameSpace var11 = SessionIDInfo.asNameSpace(var5.getSessionID());
        this.addNameSpace(var4, var2, var11, var10);
        Parameter[] var12 = Parameter.providers2Parameter(var9);
        LinkedHashMap var13 = new LinkedHashMap(16);

        for(Parameter var17 : var12) {
            Object var18 = var6.get(var17.getName());
            if (var18 != null) {
                var13.put(var17.getName(), var18);
            } else {
                JSONObject var19 = (JSONObject)var8.get(var17.getName());
                if (var19 == null) {
                    if (var17.getValue() instanceof Formula) {
                        Object var24 = var2.evalValue(String.valueOf(var17.getValue()));
                        var13.put(var17.getName(), var24);
                    } else {
                        Object var25 = var17.getValue();
                        var13.put(var17.getName(), var25);
                    }
                } else {
                    String var20 = String.valueOf(var17.getValue());
                    Map var21 = var19.toMap();

                    for(Map.Entry var23 : var21.entrySet()) {
                        var20 = var20.replaceAll("\\$" + (String)var23.getKey(), "\"" + var23.getValue().toString() + "\"");
                    }

                    Object var26 = var2.evalValue(var20);
                    var13.put(var17.getName(), var26);
                }
            }
        }

        var1.setParameters(this.dealWithAuthParam(var13, var5));
    } 

com/fr/io/exporter/excel/direct/WorkbookDataCreator.class

private	void init() throws Exception {
        if (this.model != null) {
            TableData var1;
            if (this.model.getDataSource().getTableData(this.model.getDsName()) != null) {
                var1 = this.model.getDataSource().getTableData(this.model.getDsName());
            } else {
                var1 = TableDataAssist.getTableData(this.model.getDataSource(), this.model.getDsName());
            }

            if (var1 instanceof DBTableData) {
                this.tableData = (DBTableData)var1;
                Calculator var3 = Calculator.createCalculator();
                NameSpace var4 = this.prepareParameterNameSpace(var3);
                String var2 = this.renderSql(var3, var4);
                Connection var6 = ((DBTableData)var1).getDatabase();
                if (var6 instanceof NameDatabaseConnection) {
                    var6 = ((NameDatabaseConnection)var6).createDatabase();
                }

                String var7 = this.model.getExportFormat();
                String var5 = this.model.getEncodeFormat();
                this.option = new WorkbookDataCreatorOption(var2, var6, this.prepareColumnNames(), var7, var5);
            } else if (var1 == null) {
                throw new Exception("[LARGE DATA EXPORTER]tableData is null");
            } else {
                throw new Exception("[LARGE DATA EXPORTER]" + var1.getClass().getSimpleName() + " is not support to export by this way!");
            }
        }
    }

implements\s+TableDataSource 全局搜索实现接口,也没发现数据库连接,等有空再看看。

使用 Hugo 构建
主题 StackJimmy 设计