Code execution and privilege escalation – Databases
In this article, we will have an in depth at some very uncommon techniques for gaining a remote code execution on uncommon databases and escalating privileges to admin/System level.
What should you learn next?
Overview
We all know SQL injection vulnerability. However, in this article we are not going to talk about that, we are going to look at ways an attacker can abuse a simple SQL injection to code execution and elevate the same to gain admin privileges on the system. In this article, I am going to cover following three databases:
- MySQL
- SQLite
- Redis
The test bed used for this article can be download from here.
MySQL
MySQL is a very popular open source database management system which is now developed, distributed and maintained by Oracle Corporation. MySQL server is very scalable, fast and reliable to handling database queries, thus making it a perfect candidate for being used by many giant organizations. However, when not configured properly which includes segregating database user roles and running the application with controlled privileges, it leads to series of security threats and in turn compromises.
We will be looking at two such misconfigurations such as:
- Normal Database User with File System Access
- Database User Running with Root Privileges
Normal database user with filesystem access
I have seen a lot of such misconfigurations during my day to day pentest engagements where a normal database user can write files to directories with executable permissions thus creating an opportunity for code execution. However, the only drawback we can see here is we will need a full path of web root for writing files, which is not a difficult task in case we have error based SQL-injection.
How to exploit?
Here, we will be exploiting a simple error based SQL injection and upload our backdoor to a writable directory.
NOTE: We will be skipping Basic SQL injection exploitation concepts.
As can be seen, in the following screenshot we have already guessed the number of columns.
Now the question is finding a writable directory?
Getting back to original SQL syntax error, as can be seen, it is disclosing the full path of the web root.
Let's use above disclosed path and try to upload our backdoor via dump file function in MySQL.
NOTE: There is another function to execute the same attack i.e. outfile. The only difference is with dumpfile we can only dump one row at a time, but with outfile we can dump more than one row. In our case, as our query will return only one row, so anyone of mentioned functions will work.
As can be seen in the following screenshot we could write our backdoor to file system.
Accessing our backdoor and executing system commands on the system.
Database user running with root privileges
I have seen a lot of time, staging servers exposing their phpMyAdmin interface to the public networks thereby putting their databases on the risk of compromise. But the question is:
Can use this vulnerability to elevate our privileges to the system?
MySQL made this answer as simple as saying "Hell Yeah !!" by introducing plugin feature i.e. any user having access to write to its plugin directory can load dynamically linked libraries in the current process context thereby allowing malicious attackers to execute code in memory without even touching the file system.
Now the privilege escalation part: If you look at the windows installation of MySQL, you will find it running as a service with system privileges, so any code executing in MySQL process context is executing code as SYSTEM user.
How to exploit?
Here we are assuming we have got an "Unauthenticated phpMyAdmin Interface" vulnerability. Even if you don't have access to phpMyAdmin interface and somehow managed to write files to MySQL plugin directory, this technique will still work.
To elevate our privileges to the system, we need to know the path of plugin directory and further we will be using a dumpfile function to write files to plugin directory.
Step 1: Enumerate MySQL's plugin directory.
Select @@plugin_dir;
Step 2: Convert the plugin/DLL file contents into hex equivalent. This can be done using following python's one liner.
python -c "from binascii import hexlify; print 'select 0x'+hexlify(open('udf.dll','rb').read())+' into dumpfile 'c:wampbinmysqlmysql5.5.20libpluginudf_test.dll''"
Above command will automatically generate SQL query for us to be used in next step.
Step 3: Write to plugin directory using dumpfile function only.
Step 4: Create sys_eval function to be used in current memory context.
Step 5: Code Execution as SYSTEM
Attentive user must have understood why we only used dumpfile and why not outfile in the technique shown above.
SQLite
SQLite is another relational database management system contained within a C-library which means you don't require a traditional installation and server for hosting a SQLite database. Due to its portability across various operating systems, it is most widely used in embedded systems, mobile devices, mobile applications, web browsers, etc. Therefore, the attack surface for SQLite databases is quite large. To host a SQLite database, we just need the main C-library and interface to it.
In this part, we will be looking at Code Execution via SQLite Injection. As the enumeration phase for SQLite injection is exactly as same as traditional SQL injection, so will be skipping that part as there are tons of online resources that have covered it very briefly.
How to exploit?
SQLite allows attaching database file dynamically by specifying the file path. Also, the web server process must have write access to the specified path. We will be exploiting this feature to write an arbitrary PHP code to filesystem and gain code execution.
Note: Above technique will only work with stacked queries.
We will be using the following piece of PHP vulnerable code for demonstration purpose.
Also, the database schema looks like following:
The following query can be used to write a basic PHP shell to file system.
ATTACH database "/var/www/sqlite_test/bd.php" as pwndb;CREATE table pwndb.pwn(datax TEXT);INSERT into pwndb.pwn(datax) VALUES('<?php echo system($_REQUEST["c"]); ?>');--
Explanation:
As you can see that the above query is based on stacked queries where we are attaching a database with php extension. Further we are creating table and column entries to write an arbitrary php code.
Complete URL looks like following, we triggered the URL but got no response body from the server. This is because of the exec() function shown in above code.
We further browse to our PHP shell to gain code execution as shown below.
Redis
Redis is another popular in-memory or NoSQL database structure store which allows storing data in various data types such as list, dictionaries, sets and lot more. It also provides persistence by saving data to the filesystem or called as snapshotting, becoming increasingly popular among the organizations dealing with large data sets.
In this part, we will be looking at gaining code execution via unauthenticated Redis terminals. Redis allows saving current database to the disk using some minimal commands. We will be exploiting this feature to overwrite some of the system files and gain system access.
We will be using a boot2root VM named "kevgir" for demonstration. You can download the same from here.
How to exploit?
Here we are assuming, we have already gained access to Redis terminal, and can write to system files. Our end goal here is to overwrite the ssh authorized_keys file and gain system access with username "user" privileges.
Before moving any further, let's examine what permissions does a current instance of Redis have.
As can be seen, current instance is running with root privileges.
As SSH server is also present and listening on port 1322, we quickly create a pair of public and private keys using ssh-keygen command and write our public key to a keys.txt file in a proper format. We login into Redis terminal and set data from keys.txt file to a new Redis key value using the following command.
cat keys.txt|redis-cli -h <redis-server-ip> -x set cmd
Note: Without putting new lines, the public key data will simply get appended to database's file predefined headers thus corrupting authorized_keys file.
Let's log in to Redis terminal and perform following steps to get our exploit working:
- Set database filename as authorized_keys using the following command.
- Config set dbfilename authorized_keys
- Set Redis directory as user's ssh directory using the following command.
- Config set dir "/home/user/.ssh/"
- Save the above changes using SAVE command.
We further SSH into the server using our public key as follows:
What should you learn next?