What is SQL Injection
SQL injection is a code injection technique that exploits a security vulnerability occurring in the database layer
of an application. The vulnerability is present when user input is either incorrectly filtered for string literal escape
characters embedded in SQL statements or user input is not strongly typed and thereby unexpectedly executed. It is an
instance of a more general class of vulnerabilities that can occur whenever one programming or scripting language is
embedded inside another.
0x00 - Intro
All the information contained in the article is from personal
experience, if I don't go over something that you currently do or have
seen
in SQL injections, its because I do not use it; not saying I'm right
just that's how it is. As you should already know, extracting database
information from a server without administration approval is illegal and
I cannot be held accountable for any malicious actions executed after
reading this acticle.
0x01 - What is MySQL
"SQL" stands for "Structed Query Language," which simply allows users to
send queries to the server database.
There are different types of SQL such as MSSQL, which is Microsoft's
version of the language and also has some different commands as well
as syntax.
0x02 - Finding SQL Injections
Before jumping into this topic I want to explain to you about comments
in MySQL. There are three variations to a comment in this language:
As you should already know a comment just blocks out a section so it will not be executed through the query. Typically,
anytime you see a page from a website that takes in a paramater such as:
(not saying injections are narrowed down to only id parameters but they
are quite common) you may want to test the page for a vulnerability.
The simplest way I know of to check for a vulnerability is to add:
to the end of the url and see if the contents of the page change, even the slightest bit, if they don't then add
(it doesnt have to be 1=1 or 1=0 just something that returns true for
the first statement and false for the second) and see if it
changes after the second. If the contents change after the second query
then you have a vulnerability.
0x03 - Gathering Information
To make your job or life a little easier you should look around the site
some to gather information on what you are trying to retreive.
For instance, if the site has a user registration look at the source
code for the page and take note of the field names they use (most
developers are lazy and use the same names for simplicity); you can also
look around the site for more vulnerabilities. Alright so once you
have found some good information to look forward to, its time to find
out how many columns are being selected from the database from the
original query. This is an important step because if number of columns
you "select" and the number from the original are not identical,
the injection does not work! To find out the number of column you simply
add "order by x" on the end of your vulnerable url replacing "x"
with a increasing number until you get an error
http://www.site.com/vulnerable.php?id=4 order by 9-- |
the number of columns being selected is the value of x before the error.
0x04 - The Injection
I suppose this is where some people get confused. In MySQL in order to
combine two query statements you can use the keyword "union",
you can also include the keyword "all" which will dislay all results
(default property of union is to remove duplicate results from display).
After your "union all" you also need to inlcude the keyword "select"
since we are going to want to select database information and display it
on the screen so far you should be looking at something similar to:
http://www.site.com/vulnerable.php?id=4 union all select
|
Continueing the injection like the previous example will work fine, but
it will also display all the original results as well as our new
results, typically to bypass this I, as well as most of the other people
exploiting sql injections, relace the id value, in the case of our
example it would be 4, with one of the following:
or any result that would not be in the database, this way the original
select query will not result anything but our new injected
select query will display. In SQL each column being selected must be
seperated by a comma(,) so if your vulnerable site is selecting
4 columns with the original statement (which was found earlier when we
were gathering information using the "order by") you would just
concatinate those on your injection; I like to set each column to a
different numeric value that way i can keep track of which columns
are actually being displayed on the screen. So far, if everything has
been going ok, you should have an injection url looking something like:
http://www.site.com/vulnerable.php?id=-1 union all select 1,2,3,4--
|
If not then go back and keep reading it until you figure it out. The
last part of our injection setup is the telling the query which table to
"select" the information from; we do this with the keyword "from
table"...pretty self explanitory right? So for example, we have a
vulnerable
site that has 4 columns being selected and we want to look at the
"users" table we can have a set up such as:
http://www.site.com/vulnerable.php?id=-1 union all select 1,2,3,4 from users-- |
Easy enough so far, now is where it gets a little more difficult, but not too much.
0x05 - Tables and Columns
Depending on the version of MySQL the administrators are running on the
server, finding table and column names can be very easy
or somewhat irritating. There is an easy way to figure out what version
is running on the server, can you guess? If you did not
guess version(), why the hell not, its like one of the easiest and self
explanitory things ever! Anyways, replace one of the columns
in your injection that displays on the screen with the function call
version() and this will tell you which typically its either 4.x.x or
5.x.x.
If they are running some form of version 4 then you're basically on your
own when it comes to figuring out table and column names (i'll post
some
examples of common names later); though if version 5 is implemented then
your life is easy. As of version 5.1 of MySQL the developers began to
automatically include a master database on the server called
INFORMATION_SCHEMA. Within information_schema there are tables that give
information
about all the tables, columns, users, etc on the entire sql server (to
find more about the structure of information_schema and the table/column
names
visit http://dev.mysql.com/doc/refman/5.0/en/information-schema.html).
Once you figure out a table name and some column names within that table
you want to look at just place them into our injection setup from
before; suppose we have a site that has
a "users" table and columns "user" and "pass" and the second and third
columns are displayed onto the screen, we could view these by an
injection such as:
http://www.site.com/vulnerable.php?id=-1 union all select 1,user, pass, 4 from users--
|
This example will display both the user and pass onto the screen in the given positions, though what happens if only one
column is selected or displayed? In MySQL there is function called concat() which simply concatinates fields together so to
simplify our privious example we could have:
http://www.site.com/vulnerable.php?id=-1 union all select 1, concat(user,0x3a, pass), 3, 4 from users--
|
"0x3A" is just a colon(:) in hexidecimal, simply to seperate the two fields for my own viewing.
0x06 - Narrowing down the Selection
Typically when performing a SQL injection there are multiple results you
want to look at or possibly just one individual.
There are a couple of ways to narrow down your selection first way is to
use the "where" keyword is just takes a logical
parameter such as "where id=1" which would look in the id column in the
table and find which row is equal to 1. The next way to to
use the "limit" keyword; this way is a little more useful since you do
not need to know an additional column name to increment through
the selections limit takes two parameters, where to start the selection
and how many to select. So in order to select only the very first
"user" from the table "users" using the "limit" keyword you could have:
http://www.site.com/vulnerable.php?id=-1 union all select user from users limit 0,1--
|
to look at the rest of the users individually you just increment the 0
up until you get an error. In order to look at all
the results in a single swipe you can use the function group_concat()
which works very similarly to concat() except it displays all the
results
for the given column(s) seperated by a comma(,) (the comma is just the
default, you can change it by using the "separator" keyword and indicate
a symbol to use).
0x07 - Obstacles
Excluding the fact that version 4 in general is an obstacle, there are a
few different things web developers can do to try
and make sql injections a little more difficult. The most common of
these annoyances would be magic_quotes; basically magic
quotes disallows any type of quotation marks and breaks it by adding a
back-slash(\), which of course is going to mess up your
injection. To get around this there is the nice little function char();
char() takes ascii values and generates the corresponding
character value, thus eliminating the need for a quote. Example
time...say we want to look at the "pass" column FROM the table
"users" but only WHERE the "user" column is only equal to "admin" and
the site only selects one column from the original query,
easy enough right? we learned this earlier
http://www.site.com/vulnerable.php?id=-1 union all select pass from users where user="admin"--
|
curve ball! the developers have enabled magic_quotes therefore your
"admin" will not work properly...i know its sad. To fix it we simply
take the ascii values of each character
(http://crashoverron.t35.com/ascii.php) so now we get
http://www.site.com/vulnerable.php?id=-1 union all select pass from users where user=char(97,100,109,105,110)--
|
TA-DA! injection fixed. Also another safety feature they try to block us
with is regular expressions to search our input, but often times
they have their expressions set to such narrow possibilities that you
can bypass them by simply changing the case, the comment symbol, or
replacing spaces with "+" (SQL is not case sensitive, it also sees "+"
as a space filler much like a space).
0x08 - Additional opportunities
Although I said before version 4 was a pain in the ass, I have also
noticed a nice feature common to
version 4 vulnerable sites I have come across in my adventures; this
feature would be the function load_file(),
not saying the function is exclusive to version 4 but from my experience
it is most commonly enabled for current users
by developers for some reason in this version. load_file() acts just as
file_get_contents() from PHP in that it returns the
contents of the file into a string format. If enabled this allows for
more than just SQL styles hacks on the server, it now
allows for LFI vulnerabilities as well. Although, load_file() needs to
have the exact full path to the file you are trying to
open, for example: /home/CrashOverron/Desktop/file, and if input as a
literal string then it must be encased in quotes, which
brings back the issue of magic_quotes but as before just use the char()
function. The next interesting feature that is hardly
ever possible, but I have seen happen, is the use of the "INTO OUTFILE"
keywords. This is the exact opposite of load_file(), in
order to use either of these features the current user that MySQL is
running as must have the FILE privilege on the server. Again,
the full path is needed for the output file, which cannot be an existing
file, though unlike load_file() the char() function does
not fix magic_quotes. Time for an example of both, here is the
situation: vulnerable site has 1 column selected also has a "users"
table.
load_file no magic_quotes:
http://www.site.com/vulnerable.php?id=-1 union all select load_file('/etc/passwd')--
|
load_file with magic_quotes:
http://www.site.com/vulnerable.php?id=-1 union all select load_file(char(47,101,116,99,47,112,97,115,115,119,100))--
|
INTO OUTFILE:
http://www.site.com/vulnerable.php?id=-1 union all select "test" INTO OUTFILE "/etc/test" from users--
|
0x09 - Blind SQL Injection
Blind SQL injection occurs when the original select query obtains column
information but does not display it onto the screen. In order
to continue through a blind sql injection you must basically brute-force
any value you want to know. There are a few functions we can
use in conjuction with each other that make this quite easy yet tedious,
those would be the mid() and the ascii() functions. mid() is MySQL's
substring function and ascii() does the exact opposite of char() it
takes a character and exchanges it with the corresponding ascii numeric
value.
Doing this allows us to determine the range each of our desired value is
in on the ascii chart, thus narrowing each down until we find a match.
Example situation; we have found a site that is vulnerable to blind sql
injection and we want to figure out which user MySQL is currently
running
as, our injection sequence could look something like:
http://www.site.com/vulnerable.php?id=1 and ascii(mid(user(),1,1)) < 97--
|
(this will tell us if the first letter in the user is above/below "a"
then we can change the 97 to a different value until we find the
character to the first letter)
http://www.site.com/vulnerable.php?id=1 and ascii(mid(user(),2,1)) < 97--
|
(just repeat as before and keep incrementing through the letters and you will eventually have the current user)
0x10 - Login Bypass
Ok, I left this for towards the end because it is not really very common
anymore but I will through it in because I suppose you may run
across it some day (I have only ran across this vulnerability once in
real world). The concept behind the SQL login bypass is quite
simple; in order to execute the exploit you input a username into the
user field then in the password field of the form
you put:
this just ends the current password field and includes the logical OR with a constant true statement. A
simple MySQL login script could look like:
<?php
$user = $_POST['user'];
$pass = $_POST['pass'];
$ref = $_SERVER['HTTP_REFERER'];
if((!$user) or (!$pass))
{
header("Location:$ref");
exit();
}
$conn = @mysql_connect("localhost", "root", "blah") or die("Could not connect");
$rs = @mysql_select_db("db", $conn) or die("db error");
$sql = "SELECT * FROM users WHERE user=\"$user\" AND pass=\"$pass\"";
$rs = mysql_query($sql, $conn) or die("query error");
$num = mysql_numrows($rs);
if($num != 0)
{
echo("Welcome $user");
}
else
{
header("Location:$ref");
exit();
}
?>
so if we input the user "admin" and "" or 1=1--" as the password the query sent to the server is going to look like this:
"SELECT * FROM users WHERE user="admin" AND pass="" or 1=1--"
|
so the server is going to select row where the "user" equals "admin" and
disregard if the "pass" is correct because it is asking if the
pass OR 1=1 are true, since 1=1 is always true you bypass the pass
section.
0x11 - Useful Keywords/Functions
UNION ALL SELECT
AND/OR
ORDER BY
WHERE
LIMIT
LIKE
INTO OUTFILE
char()
ascii()
mid()
concat()
group_concat()
load_file()
user()
database()
version()
Written by CrashOverron
Please try to comment if i have done well or if you have finished learning from the blog
EmoticonEmoticon