SQL Injection 0x03 - Blind Boolean Attacks
Introduction
Blind SQL injection are the type of SQL injections attacks wherein no database error is received from the web responses, there are either subtle or no changes to the web page upon sending injection payloads. Since these changes are either subtle or non-existent, it becomes harder to identify and exploit these vulnerabilities but are certainly not impossible.
Hi, welcome to the third part of the SQL injection series, if you haven’t read the first two posts and are a complete beginner I’d suggest you read them first - SQL Injection 0x01 - Introduction and SQL Injection 0x02 - Testing & UNION Attacks. In this blog post I have covered blind boolean SQL injection attacks, as the title suggests, in which you receive subtle changes in the responses suggesting if the vulnerability is present, and if an injection payload is working or not.
For this post I decided to use Falafel machine from HackTheBox platform as the example to explain blind boolean SQL injection. If you would like to follow along and then finally hack the machine, I’ve posted the writeup here
I will start from identification of interactable fields, test these fields, and then completely exploit it using different methods (BurpSuite Intruder and Custom Python Script)
Identification
After going through application, /login.php
was the only endpoint with which a user can interact, and with a database.
Sending admin : hasd
Looks like “admin” user is present but it tells you if the password is wrong.
Let’s send in a non-existent user to confirm our assumption.
Sending noobsec : hasd
We can definitely do user enumeration.
Testing
We’ll start with testing now.
Single-quote Test
Testing with a single-quote ('
) first.
Sending admin' : hasd
We get an error - Try again... Looks like we broke the internal query.
Comment Test
Next we will test with a comment (-- -
).
Sending admin--+- : hasd
We get an error - Try again... Looks like we broke the internal query.
Single-quote And Comment Test
Let’s test a single-quote and a comment. We’ll append the username with a single-quote and then a comment, and see if that changes anything.
Sending admin'--+- : hasd
We get the wrong password error - Wrong identification : admin. With this we can say that we have an sql injection on our hand, but let’s finish our testing.
OR Test
We will now test with an operand - OR
.
Sending admin'+OR+'1'='1'--+- : hasd
:
We get the wrong password error - Wrong identification : admin. With this we can again say that we have an sql injection on our hand, but let’s finish rest of our testing.
Let’s test OR
with a non-existent user.
Sending noobsec'+OR+'1'='1'--+- : hasd
:
Even when we send in a wrong username, we get the wrong password error for admin due to our OR
injection test, indicating that the injection is definitely working here.
AND Test
Now let’s test the field with AND
operator.
Sending admin'+AND+'1'='1'--+- : hasd
We get the wrong password error - Wrong identification : admin, great.
Let’s test by sending a false
condition.
Sending admin'+AND+'1'='2'--+- : hasd
We get the error - Try again, even though the username was correct, once again confirming that we have sql injection on this field.
Sleep Test
Let’s conclude our testing with the sleep()
test.
Sending admin'+OR+sleep(20)--+- : hasd
Not only did this not work, it turns out that there is some filter in place in order to prevent malicious users to hack this authentication mechanism. Clearly, it’s been working out just fine :)
Exploitation
Next step would be get the number of columns, but UNION is blocked regardless of what you do or try any kind of bypass. We could use ORDER BY to get the number of columns but clearly this is not an error-based SQL injection. Since we cannot use UNION, getting the number of columns does not make sense.
Although we cannot dump credentials out on the screen, it does not mean we cannot extract data out.
Since this is a SQL database, we could use substring - substring(string, position, length)
function. As the name suggests, substring function takes a “string”, or a column (like in this case), along with position, and length, and prints out the characters of a string (or column) from the position and length you specify.
Let’s test it with the username field to get a gist, since we know that the user “admin” exist
It’s important to keep in mind that when our SQL injection is working, we get the error “Wrong identification”, and when it does not, we get an error “Try again”.
Similarly, we can extract the hashes of the users present in this website.
We’ll test for [a-f0-9] (because hashes) for each character position for the password column, and if we get the error “Wrong identification”, then it would indicate that for position X the password column has that character.
Hash Extraction - BurpSuite Edition
This can be done in BurpSuite Intruder, even in Community Edition which is what I use, let’s take a look at finding the first character of the admin’s hash.
First we select a login request in BurpSuite and “Send it to intruder” and set our payload position:
Next step to set a payload, we’ll select Brute Forcer. Modify the character set, as below:
To make our life easier, we could put the “right” error string in the “Grep Match” section so that the request that matches as per our error will get marked.
We’re now ready to “Start Attack"ing. Once we do, we soon find that the first character of the admins’ hash is zero (0). We can now pause the attack since we already got what we needed from this injection.
We were successfully able to leverage BurpSuite Intruder to extract the first character of admins’ hash and can see that it is “0” (Zero).
Hash Extraction - Python Edition
Note: Link to the scripts are at the bottom
Although that was nice and we could perform a little more tweaking and get the entire hash, it would be a whole LOT faster if we whipped up a script of our own and got this done, which is what we will be doing now.
I wrote the script in python to get the admin’s hash.
Once we run the script we get the admin users’ hash.
Although this script works, it does take quite some time to run. So I created another script which would perform some extra queries to the database before it checks whether a particular range of characters are in users’ hashs’ X position or not.
The first check is to find if the character in X position is an alphabet or not. If so, it is checked if it belongs to [a-f] group or the rest. If it’s a hash, it’ll always belong to a-f group which is “alpha1” in the below code. If it’s not an alphabet, it’s checked if the number belongs to [0-4] group or [5-9]. Once the group is sent back, SQLstring is used to generate payloads for characters in only those groups for X position. This reduces the amount of requests sent to the server, and we extract the hash much faster.
These checks are done using “ord”. Ordinal numbers are just decimal numbers. We convert the output of the substring to ord and perform a check if it’s greater than 58, ascii(9) = decimal(57), thus checking if the character in that position is an alphabet.
Numbers (dec hex ascii) | Alphabets (dec hex ascii) |
---|---|
Check out man ascii
to view the entire table.
Running this script to get the admins’ hash:
By making a script with extra checks, it helped us save 38 seconds for just one account, if there were a lot of accounts in here that would add up to some considerable amount of time saved.
The above script is not perfect, maybe you could make it even more dynamic.
Summary
To summarize this post:
- Identify all the fields that a user can interact with
- Take a look at all the input fields
- Consider all the parameters being passed to the backend
- Consider HTTP headers like User-Agent and Cookies, when application looks like it’s tracking a user
- Test each point individually with different characters and conditions
- Use functions like
substring
when UNION is not possible - When dealing with repetitive tasks, or a lot of data/ queries, use automation
Testing Checklist:
Name | Character | Function |
---|---|---|
Single quote | ' |
String terminator |
Semi colon | ; |
Query terminator |
Comment | -- - |
Removing rest of the query |
Single quote with a comment | '-- - |
End a string and remove rest of the query |
Single quote, semi colon and a comment | ';-- - |
End a string, end query, and remove rest of the query |
OR operator | OR 1=1-- - |
For integers, true test |
OR operator | OR 1=2-- - |
For integers, false test |
OR operator | ' OR '1'='1'-- - |
For strings, test test |
AND operator | AND 1=1-- - |
For integers, true test |
AND operator | AND 1=2-- - |
For integers, false test |
AND operator | ' AND '1'='1'-- - |
For strings, true test |
Sleep function | OR sleep(5)-- - |
Blind test |
Blind boolean hack steps:
- Identify “right” and “wrong” errors.
- Test if
substring
is working with the username column - Run a test round for the first position of the password column, which would be hash
- Write a script to perform the same
- Update the script to cycle through each character (a-f0-9) for 32 positions and print it out.
Fin
Both the scripts are available in this git repo. If some part of it feels unexplained or you did not understand, feel free to contact me :)
Take care, have a great day, and keep hackin'!