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)
After going through application,
/login.php was the only endpoint with which a user can interact, and with a database.
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.
noobsec : hasd
We can definitely do user enumeration.
We’ll start with testing now.
Testing with a single-quote (
admin' : hasd
We get an error - Try again... Looks like we broke the internal query.
Next we will test with a comment (
admin--+- : hasd
We get an error - Try again... Looks like we broke the internal query.
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.
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.
We will now test with an operand -
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.
OR with a non-existent user.
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.
Now let’s test the field with
admin'+AND+'1'='1'--+- : hasd
We get the wrong password error - Wrong identification : admin, great.
Let’s test by sending a
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.
Let’s conclude our testing with the
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 :)
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.
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).
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.
# Importing necessary library
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)|
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.
To summarize this post:
substringwhen UNION is not possible
||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|
Blind boolean hack steps:
substringis working with the username column
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’!