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.
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.
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.
MySQL JDBC Client Deserialization Vulnerability
Actually the MySQL JDBC deserialization vulnerability was firstly mentioned by Thijs Alkemade
in 2017.
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.
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 toServerStatusDiffInterceptor
, auditingServerStatusDiffInterceptor
code, you can see that this interceptor allows the client to send specific queries to the server, In addition, thegetObject
method is used to process the returned column.
- Secondly, in the
getObject
method, the driver will directly call thereadObject
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.
Of course, the class and property names of interceptors are different in various version of JDBC driver as shown in the table.
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
.
Eventually, I construct the PoC like this
1 | <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
.
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
.
Here is the illustration in SpringBoot H2 console
First of all, we have to review the source code to figure out why we use the RUNSCRIPT FROM
statement.
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 | 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') |
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.
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.
It is triggered by parseClass
method.
Finally we can execute malicious java code in assertions.
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.
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.
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.
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
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
The malicious connection PoC in the following code
And the evil slave sever code is as follows
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?
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
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.
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?
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.
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.
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.
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
.
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.
However, RCE is the final objective. Here is the PoC
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.
Otherwise, Apache DolphinScheduler
removes sensitive properties from parameters.
- 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.
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.
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
.
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.
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.
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.
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.
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.
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.
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.
The malicious HTTP service code is shown as follows.To constrocut the mailicious XML Documnet is easy.
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.