Purpose: I wanted to do a write up highlighting an SQL injection lab provided by TryHackMe. Although it is listed as ‘Easy’, I do find the lab to be quite beneficial. In this write up I will discuss how I went about retrieving the flag using a python script I created for this purpose. I do understand this may not be the best way to go about it, but I am still learning. I would like to explore other methods but with just previous knowledge and some searching in the python documentation this is what I came up with.
What is Blind SQL Injection?:
Before jumping into the script, lets first discuss what is Blind SQL Injection. In some scenarios, an attacker may be able to dump information from the database directly to the website. An example of this would be a web application that is intended to display user information for a particular user based on a specific query. If the attacker can inject some code to say SELECT * FROM users; instead of the original query, now the attacker may be able to see all rows/columns stored on the server directly on the web page.
Blind SQL injection is exactly the opposite. Instead, this form of injection will find an answer based on true or false questions. How do we know if its true or false? Well it can vary from one use case to the next, but this example I am discussing below will base it on the username input field. If we were to inject a query asking for the first character of a password and it matches, the return would be true and in other words the username would pass authentication.
Example:
admin' AND SUBSTR((SELECT password FROM users LIMIT 0,1),1,1) = CAST(X'54' as Text)-- -
The above code is an SQL query that we will inject into the username. I will explain this in further detail below but quickly, the SUBSTR function of SQLite will select a specified character from the string we provide. In this case the string we provide is password from users table. If the (1,1) first character matches T (hex x54) then the username will return true. Because we also used — – at the very end, commenting out the rest of the query we will be authenticated. If the first character of the password did not match a T, the username would return false and a different response would generate.
As you can see we are now logged in as ‘Unknown’. If we did not know a valid username we can also use OR statement with no username, in hopes that there is not a blank username in the database giving false results.
Automate (BruteForce)
From the brief explanation above we can now see how we can brute force the password based on the response of the page. We will go character by character and match it against each letter in the alphabet and number 0-9 in hopes for a match and continue until no letters remain.
The Script
Note: I hope you can read the text, if not please head over to my GitHub to get a better view. I will be breaking it down piece by piece but if you want the whole snippet, GitHub is the answer.
First off we will want to import three Python Libraries, requests string and time.
After importing the libraries we will start with Figure 1.2. First line we notice is
start = time.time()
This is setting the start variable to the current time of execution, this variable will later be used in order to determine time to process.
Next we can see the function hexify():, This function will be used in order to convert ascii letters and numbers to hex. From the string library imported at the very beginning, we will use string.ascii_letters function. We will then append the numbers to the end of the letters variable to ensure the entire alphabet and 0-9 digits are involved.
We will then define hex_letters as an empty list.
Inside of the for loop we will encode each character from letters variable and then append it to the hex_letters.
Lastly we will return hex_letters list to the function
In figure 1.3, we will discuss the injectstuff function. This function is where most of the action will take place.
First we want to create a session from the requests library
Next we will want to define the URL we are going to attack
Before the while loop we will define {i} (used as the counter) and empty string variable to store the flag
Now we do not know the length of the password, but a guess was made in saying that it is less then 40. We will run the while loop until counter (i) is less then or equal to 40
We will then want to create a nested for loop. This loop will iterate through our letters variable defined early for each character inside of password
In fig 1.4, we will inject our query using the post method from request library. Inside of this post method we must define the URL first. If we do not pass the URL variable first then you must define by using url=’http://example/.com’. Next we will pass the data parameter the injection query. The data parameter is a dictionary, using key:value. First we define the ‘username’ key and then inject the query discussed earlier for the value.
Two things to look out for. One being the use of the f string, and the second being the values passed inside of those {}. An f string allows us to pass variables directly inside of the line of text. So in this case the first {i} (counter) is used to define which position in the password are we comparing. so for a password of ‘password’ the first iteration of this loop will compare ‘p’ to each letter in our Letters variable. The {h} in this case is the letter or number from our Letters variable (h comes from when we defined the for loop.
So from left to right of the query. The admin’ is being passed as the username in conjunction with the SUBTR function. The built-in SUBSTR function will be passed the password from users table limiting to first column and only one column (0,1). For each iteration of the loop, depending on {i} will determine which position of the password we are currently comparing. Next, we will use the CAST function to pass the hex variable {h} in the format expected by the database as text. Lastly, the password will be commented out using — -.
After we post the query to the website we will check the HTML response for ‘Invalid’. If Invalid is not found in response we will print to screen ‘Found a match!’. We will then convert the hex value back to ascii and add it to our flag variable defined earlier. This will then break out of the current cycle and move on to the next position by increasing i+=1. Finally, once each iteration is performed we will print the flag variable to screen.
Although, it probably goes without saying. In order for this to happen, we must call the hexify function first and then pass the return of hexify to injectstuff. After the loops inside of inject stuff have finished, we will define end variable to current time. Last but not least we will subtract the difference between the two to get time of processing.
Conclusion
I really enjoyed this lab on TryHackMe. I have yet to finish the all of the tasks due to many projects going on, but I do plan so. I did not need to create a python script nor was it in the write up of the TryHackMe lab. I did so in order to continue my practice with scripting and also to document it not only for myself but for others to gain a better understanding of this method. A tool such as SQLmap could of easily been used to exploit this injection point. I hope someone was able to take something away. I will continue my security write-ups as the weeks go on and as always, Never Stop Learning!