From SQLI to Shell
From SQLI to Shell
Table of Content
Table of Content 2
Introduction 4
About this exercise 5
License 5
Syntax of this course 5
The web application 6
Fingerprinting 8
Inspecting HTTP headers 8
Using a directory Buster 10
Detection and exploitation of SQL injection 12
Detection of SQL injection 12
Introduction to SQL 12
Detection based on Integers 14
Detection on Strings 18
Exploitation of SQL injections 20
The UNION keyword 20
Exploiting SQL injections with UNION 21
Retrieving information 24
Access to the administration pages and code execution 30
Cracking the password 30
Uploading a Webshell and Code Execution 33
Conclusion 36
2/36
PentesterLab.com » From SQL Injection to Shell
3/36
PentesterLab.com » From SQL Injection to Shell
Introduction
This course details the exploitation of SQL injection in a PHP based website and how
an attacker can use it to gain access to the administration pages. Then, using this
access, the attacker will be able to gain code execution on the server.
4/36
PentesterLab.com » From SQL Injection to Shell
License
From SQL Injection to Shell by PentesterLab is licensed under the Creative Commons
Attribution-NonCommercial-NoDerivs 3.0 Unported License. To view a copy of this
license, visit http://creativecommons.org/licenses/by-nc-nd/3.0/.
5/36
PentesterLab.com » From SQL Injection to Shell
The red boxes provide information on mistakes/issues that are likely to happen while
testing:
The green boxes provide tips and information if you want to go further.
$ ifconfig eth0
eth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:88 errors:0 dropped:0 overruns:0 frame:0
TX packets:77 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:10300 (10.0 KiB) TX bytes:10243 (10.0 KiB)
Interrupt:11 Base address:0x8000
6/36
PentesterLab.com » From SQL Injection to Shell
Throughout the training, the hostname vulnerable is used for the vulnerable
machine, you can either replace it by the IP address of the machine, or you can just
add an entry to your host file with this name and the corresponding IP address. It can
be easily done by modifying:
7/36
PentesterLab.com » From SQL Injection to Shell
Fingerprinting
Fingerprinting can be done using multiple tools. First by using a browser, it's possible
to detect that the application is written in PHP.
$ telnet vulnerable 80
Where:
8/36
PentesterLab.com » From SQL Injection to Shell
GET / HTTP/1.1
Host: vulnerable
It is possible to retrieve information on the version of PHP and the web server used
just by observing the HTTP headers sent back by the server:
HTTP/1.1 200 OK
Date: Thu, 24 Nov 2011 04:40:51 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: PHP/5.3.3-7+squeeze3
Vary: Accept-Encoding
Content-Length: 1335
Content-Type: text/html
<html>
<head>
Here the application is available via HTTP. If the application was only available via
HTTPs, telnet or netcat would not be able to communicate with the server, the tool
openssl can be used:
Where:
9/36
PentesterLab.com » From SQL Injection to Shell
The following command can be run to detect remote files and directories:
10/36
PentesterLab.com » From SQL Injection to Shell
--hc 404 tells wfuzz to ignore the response if the response code is
404 (Page not Found)
-z file -f wordlists/big.txt tells wfuzz to use the file
wordlists/big.txt as a dictionary to brute force the remote directories'
name.
http://vulnerable/FUZZ tells wfuzz to replace the word FUZZ in the
URL by each value found in the dictionary.
11/36
PentesterLab.com » From SQL Injection to Shell
In order to understand, detect and exploit SQL injections, you need to understand the
Standard Query Language (SQL). SQL allows a developer to perform the following
requests:
The most common query used by web sites is the SELECT statement which is used to
retrieve information from the database. The SELECT statement follows the following
syntax:
The string1 value is delimited by a simple quote and the integers integer1 and
integer2 can be delimited by a simple quote (integer2) or just put directly in the
query (integer1).
13/36
PentesterLab.com » From SQL Injection to Shell
2 test1 Robert
3 test33 Super
As we can see, only these values are returned since they are the only ones matching
all of the conditions in the WHERE statement.
If you read source code dealing with some databases, you will often see SELECT *
FROM tablename. The * is a wildcard requesting the database to return all columns
and avoid the need to name them all.
14/36
PentesterLab.com » From SQL Injection to Shell
Since error messages are displayed, it's quite easy to detect any vulnerability in the
website. SQL injections can be detected using any and all of the following methods.
Let's take the example of a shopping website, when accessing the URL /cat.php?id=1,
you will see the picture article1. The following table shows what you will see for
different values of id:
/article.php?id=1 Article 1
/article.php?id=2 Article 2
/article.php?id=3 Article 3
<?php
$id = $_GET["id"];
$result= mysql_query("SELECT * FROM articles WHERE id=".$id);
$row = mysql_fetch_assoc($result);
// ... display of an article from the query result ...
?>
15/36
PentesterLab.com » From SQL Injection to Shell
The value provided by the user ($_GET["id]) is directly echoed in the SQL request.
If a user try to access the URL /article.php?id=2', the following request will be
executed SELECT * FROM articles WHERE id=2'. However, the syntax of this
SQL request is incorrect because of the single quote ' and the database will throw an
error. For example, MySQL will throw the following error message:
This error message may or may not be visible in the HTTP response depending on the
PHP configuration.
The value provided in the URL is directly echoed in the request and considered as an
integer, this allows you to ask the database to perform basic mathematical operation
for you:
16/36
PentesterLab.com » From SQL Injection to Shell
17/36
PentesterLab.com » From SQL Injection to Shell
Detection on Strings
As we saw before in "Introduction to SQL", strings in an SQL query are put between
quotes when used as value (example with 'test'):
If SQL injection is present in the web page, injecting a single quote ' will break the
query syntax and generate an error. Furthermore, injecting 2 times a single quote ''
won't break the query anymore. As a general rule, an odd number of single quotes will
throw an error, an even number of single quotes won't.
It is also possible to comment out the end of the query, so in most cases you won't get
an error (depending on the query format). To comment out the end of the query you
can use ' --.
For example the query, with an injection point in the test value:
will become:
18/36
PentesterLab.com » From SQL Injection to Shell
However this test can still generate an error if the query follows the pattern below:
Since the right parenthesis will be missing once the end of the query is commented
out. You can obviously try with one or more parenthesis to find a value that doesn't
create an error.
Another way to test it, is to use ' and '1'='1, this injection is less likely to impact
the query since it is less likely to break it. For example if injected in the previous
query, we can see that the syntax is still correct:
SELECT id,name FROM users where ( name='test' and '1'='1' and id=3 );
Furthermore and ' and '1'='1 is less likely to impact the semantic of the request
and the results of with and without injection are likely to be the same. We can then
compare it with the page generated using the following injection ' and '1'='0 which
is less likely to create an error but is likely to change the semantic of the query.
19/36
PentesterLab.com » From SQL Injection to Shell
In order to find the SQL injection, you need to visit the website and try these methods
on all parameters for each page. Once you have found the SQL injection, you can
move to the next section to learn how to exploit it.
The UNION statement is used to put together information from two requests:
Since it is used to retrieve information from other tables, it can be used as a SQL
injection payload. The beginning of the query can't be modify directly by the attacker
since it's generated by the PHP code. However using UNION, the attacker can
manipulate the end of the query and retrieve information from other tables:
20/36
PentesterLab.com » From SQL Injection to Shell
The most important rule, is that both statements should return the same number of
columns otherwise the database will trigger an error.
In order to perform a request by SQL injection, you need to find the number of columns
that are returned by the first part of the query. Unless you have the source code of the
application, you will have to guess this number.
If you try to do a UNION and the number of columns returned by the two queries are
different, the database will throw an error:
21/36
PentesterLab.com » From SQL Injection to Shell
You can use this property to guess the number of columns. For example, if you can
inject in the following query: SELECT id,name,price FROM articles where
id=1. You will try the following steps:
NB: this works for MySQL the methodology is different for other databases, the values
1,2,3,... should be changed to null,null,null, ... for database that need the same type of
value in the 2 sides of the UNION keyword. For Oracle, when SELECT is used the
keyword FROM needs to be used, the table dual can be used to complete the request:
UNION SELECT null,null,null FROM dual
The other method uses the keyword ORDER BY. ORDER BY is mostly used to tell the
database what column should be used to sort results:
22/36
PentesterLab.com » From SQL Injection to Shell
The request above will return the users sorted by the firstname column.
ORDER BY can also be used to with an integer to tell the database to sort by the
column number X:
The request above will return the users sorted by the third column.
This feature can be used to detect the number of columns, if the column number in the
ORDER BY statement is bigger than the number of columns in the query, an error is
thrown (example with 10):
You can use this property to guess the number of columns. For example, if you can
inject in the following query: SELECT id,name,price FROM articles where
id=1. You can try the following steps:
23/36
PentesterLab.com » From SQL Injection to Shell
Based on this dichotomic research, we know that the number of columns is 3, we can
now use this information to build the final query:
Even if this methodology provides the same number of requests for this example, it's
significantly faster as soon as the number of columns grow.
Retrieving information
Now that we know the number of columns, we can retrieve information from the
database. Based on the error message we received, we know that the backend
database used is MySQL.
Using this information, we can force the database to perform a function or to send us
information:
the user used by the PHP application to connect to the database with
current_user()
the version of the database using version()
24/36
PentesterLab.com » From SQL Injection to Shell
In order to perform this, we are going to need to replace one of the values in the
previous statement (UNION SELECT 1,2,3) by the function we want to run in order to
retrieve the result in the response.
Make sure you always keep the right number of columns when
you try to retrieve information.
You can for example access the following URL's to retrieve this information:
We are now able to retrieve information from the database and retrieve arbitrary
content. In order to retrieve information related to the current application, we are going
to need:
25/36
PentesterLab.com » From SQL Injection to Shell
MySQL provides tables containing meta-information about the database, tables and
columns available since the version 5 of MySQL. We are going to use these tables to
retrieve the information we need to build the final request. These tables are stored in
the database information_schema. The following queries can be used to retrieve:
By mixing these queries and the previous URL, you can guess what page to access to
retrieve information:
The problem, is that these requests provide you a raw list of all tables and columns,
but to query the database and retrieve interesting information, you will need to know
what column belongs to what table. Hopefully, the table information_schema.columns
stores table names:
26/36
PentesterLab.com » From SQL Injection to Shell
You have now a list of tables and their columns, the first tables and columns are the
default MySQL tables. At the end of the HTML page, we can see a list of tables that
are likely to be used by the current application:
27/36
PentesterLab.com » From SQL Injection to Shell
Using this information, you can now build a query to retrieve information from this
table:
And get the username and password used to access the administration pages:
28/36
PentesterLab.com » From SQL Injection to Shell
29/36
PentesterLab.com » From SQL Injection to Shell
A search engine
John-The-Ripper http://www.openwall.com/john/
When a hash is unsalted, it can be easily cracked using a search engine like google.
For that, just search for the hash and you will see a lot of websites with the cleartext
version of your password:
30/36
PentesterLab.com » From SQL Injection to Shell
John-The-Ripper can be used to crack this password, most modern Linux distribution
include a version of John, in order to crack this password you need to tell John what
algorithm has been used to encrypted it. For web application, a good guess would be
MD5.
$ john
# ...usage information...
--format=NAME force hash type NAME: DES/BSDI/MD5/BF/AFS/LM/crypt
# ...usage information...
Unfortunately, the MD5 available is not the format created by the PHP function md5. In
order to crack this password, we will need a version of John supporting raw-md5. The
community-enhanced version available on the main website supports raw-md5 and
can be used.
31/36
PentesterLab.com » From SQL Injection to Shell
Now we need to provide the information in the right format for John, we need to put the
username and password on the same line separated by a colon ':'.
admin:8efe310f9ab3efeae8d410a8e0166eb2
The following command line can be used to crack the password previously retrieved:
32/36
PentesterLab.com » From SQL Injection to Shell
We can see that there is a file upload function allowing a user to upload a picture, we
can use this functionality to try to upload a PHP script. This PHP script once uploaded
on the server will give us a way to run PHP code and commands.
First we need to create a PHP script to run commands. Below is the source code of a
simple and minimal webshell:
<?php
system($_GET['cmd']);
?>
This script takes the content of the parameter cmd and executes it. It needs to be
saved as a file with the extension .php, for example: shell.php can be used as a
filename.
33/36
PentesterLab.com » From SQL Injection to Shell
We can see that the script has not been uploaded correctly on the server. The
application prevent file with an extension .php to be uploaded. We can however try:
Now, we need to find where the PHP script, managing the upload put the file on the
web server. We need to ensure that the file is directly available for web clients. We
can visit the web page of the newly uploaded image to see where the <img tag is
pointing to:
<div class="content">
<h2 class="title">Last picture: Test shell</h2>
you can now access the page at the following address and start running commands
using the cmd parameter. For example, accessing
http://vulnerable/admin/uploads/shell.php3?cmd=uname will run the command uname on
the operating system and return the current kernel (Linux).
34/36
PentesterLab.com » From SQL Injection to Shell
The webshell has the same privileges as the web server running the PHP script, you
won't for example be able to retrieve the content of the file /etc/shadow since the
web server doesn't have access to this file (however you should still try in case an
administrator made a mistake and changed the permissions on this file).
Each command is run in a brand new context independently of the previous command,
you won't be able to get the contents of the /etc/ directory by running cd /etc and
ls, since the second command will be in a new context. To get the contents of the
directory /etc/, you will need to run ls /etc for example.
35/36
PentesterLab.com » From SQL Injection to Shell
Conclusion
This exercise showed you how to manually detect and exploit SQL injection to gain
access to the administration pages. Once in the "Trusted zone", more functionnality is
often available which may lead to more vulnerabilities.
This exercise is based on the results of a penetration test performed on a website few
years ago, but websites with these kind of vulnerabilities are still available on Internet
today.
The configuration of the web server provided is an ideal case since error messages
are displayed and PHP protections are turned off. We will see in another exercice on
how SQL injections can be exploited in harder conditions, but in the meantime you can
play with the PHP configuration to harden the exercise. To do so you need to enable
magic_quotes_gpc and disable display_errors in the PHP configuration
(/etc/php5/apache2/php.ini) and restart the web server
(/etc/init.d/apache2 restart)
36/36