Solution for PortSwigger Academy Lab: Blind SQL injection with time delays and information retrieval
The following is my documentation on PortSwigger’s Academy labs.
End Goal: Time-delayed Blind SQL injection to steal the password of the administrator
In this lab, we are tasked to log in as the administrator of the website below by exploiting SQLi.
There is some legwork we have to do first before we get there, however. The website will not be outputting any data to the page like it has in previous labs, so what do we do? Delay, delay, delay!
Time delay
We’ll be using “true” or “false” statements in our payload to confirm the data we are looking for, and we’ll accomplish that with time delays. What do I mean by true or false statements? Math! 1=1 is a true statement, so we’ll ask the server to delay its response to us if what we are asking it is “true”. If what we are looking for is not there, the server will load the page normally. So even though we cannot have the site output the data to the page, we can ask it “yes” or “no” questions and then see how long it loads.
So let us first confirm that this type of vulnerability exists, and to do that we need to know which SQL database type we are working with. To do that, let us first open Burp Suite and intercept our cookie. Turn the intercept on and reload the page.
Next, right-click and send what we intercepted over to Reapeater, making sure that we turn the Interceptor off when we are done.
Now we have our means to deliver our payload, let’s construct it. First, have a look at our SQL cheat sheet:
We can see here that each database requires a different method for delay. If we send it a method the database type is not familiar with it will not delay, letting us know that it is not that type of database. We’ll try them one at a time, asking to delay for ten seconds, until we see the delay that we are looking for. We’ll replace the string after “TrackingId=” and send it away.
After testing a couple of payloads we get a ten-second delay after sending our PostgreSQL payload, confirming that it’s that type of database.
Confirming that the “users” table exists
Now that we know what database type we are dealing with (and consequently confirming that this type of blind SQLi works) we’ll move on and confirm that there is indeed a table with passwords that we can poke at.
When constructing our new payload, we need to make sure that the server does not delay if what we are asking is false. This is something we did not need to consider in our previous payload because the server would just ignore what it could not read. So we’ll ask it to delay for 10 seconds with a true statement, like “1=1”, and to load normally with a false statement like “-1” or “0”. We are basically telling the database: “If our input is good then 1=1, which is of course true, and if something is true then you have to sleep for 10 seconds. If our input is false, then you have to sleep for -1 seconds, which is impossible so stay awake!” In PostgreSQL speech that looks like this:
We get a valid “200 OK” response, and what’s more, it took 10 seconds to return. Nice. Next, we’ll check to see if “users” is a valid table and check if “administrator” is a good username. Technically we already know this because the lab tells us that it exists, but let’s do it anyway so that we can get into the habit of doing our due diligence. We’ll accomplish this by replacing (1=1) with (username=’administrator’) (making that our new true statement) and then appending end from users)– to our payload:
…and we are in luck! The response took ten seconds to get back.
Figuring out the length of the password
Now that we have confirmed that “administrator” is a valid username, we have to figure out the password. We are going to ask the database if the first letter of the password is “a”, and if it isn’t, we are going to ask if it is “b”, and we are going to do this for every character until we have them all. Wouldn’t it be nice to know how many characters there are in the password that we have to guess? So before we start going through the whole alphabet, let’s figure out how many times we are going to have to do that. To do so, we’ll ask if the length of the password is larger than a number until the answer is no. We’ll insert and LENGTH(password)>1) into our payload.
Now, we could send the payload, see that the length is indeed larger than one character after a time delay, and then substitute the “1” for another number and send it again— but that would be horrible and time-consuming. Instead, let’s automate that task like a sane person. That’s what Burp Suite’s Intruder is for. Right-click and send to Intruder.
Next, highlight the 1 and press the “Add §” button to make a “Payload set”, then navigate over to the “Payloads” tab.
Make sure you have the “Payload type” set to “Numbers”. For our range, we’ll go from “1” to something like “30”, with “1” step. Now we just have to navigate over to the “Resource pool” tab and “Create a new resource pool” with the “Maximum concurrent requests” set to “1”. We do this because we need to do a single request at a time.
Now we start our attack and watch for our results. Keep an eye out on the “Response” column (if you do not see that go to the “Columns” drop-down menu and select “Response received”), when you see that the response time is less than ten seconds then we are at the end of the line. Remember to make note of how many requests had a response time of ten seconds or more, that will be our password length.
With our password length confirmed (in my case it was 20 characters), we can move on to guessing the password itself!
Getting our password with blind time-delayed SQLi
We are close to the finish line, but there is still work to be done. Our final payload will ask the database to delay if it guesses a correct character in the correct character position. We will do so by replacing and LENGTH(password)>1 with and substring(password, 1, 1) in our current payload. According to ProtSwigger we do this so that we can
“…extract part of a string, from a specified offset with a specified length.”
The first “1” refers to the offset, and the second “1” refers to the length of characters returned from the string. Thus, if we have substring(‘foobar’, 4, 2) we return the string ba. Our “a” in the payload is the character we will be checking against. So right now if the first character of the administrator’s password is “a” we’ll get a ten-second delay in returning the page from the server. If we were to do this without automating we would substitute our second “1” with a number between it and 31 and substitute our “a” with any alphanumeric character until we get twenty separate ten-second delays. We would presumably have to do this seven hundred a twenty times.
So let’s not do that.
To automate it we will follow similar steps we did before, but first hit the “Clear §” button.
Highlight the first “1” and press the “Add §” button to make a new payload set.
Then do the same for our “a”, making sure to select “Cluster bomb” from our “Attack type” drop-down menu.
Our final payload should look like this:
Our first payload set for for the first “1” will be a “Numbers” payload type, with a sequential range from “1” to “20” in “1” step.
Switching to the second payload set for “a”, select “Brute forcer” as a payload type. This will fill out the character set with alphanumeric characters. Your “Min length” and “Max length” should be set to “1”.
Be sure to double-check that our custom resource pool is still selected.
Measure twice, cut once I always say! Better to double-check these things than to run our attack and find out later that nothing works. Hit that “Start attack” button and wait for the magic.
Oh no. Well, this could take a very, very long time as I have the “Community Edition” of Burp Suite. No matter, I hit “okay” and start to see results, making sure to select “Response received” and… wait a minute…
I immediately get errors.
Always Make Sure Your Lab has not Timed Out, or: How I Learned to Stop Worrying and Love the Community Edition
This happens to me every time. I am mostly done with the lab, take too much time doing it, and the lab times out. Every. Time.
So, I go back, start the lab over, recapture our session cookie, rebuild our payload, and hit “Start attack”.
That’s alright. No matter. Everything is working perfectly this time. We just have to fill some time. If you find yourself in the same shoes why not fill out that time by prepping for a different lab? I wrote a recipe for the “DOM XSS in AngularJS expression with angle brackets and double quotes HTML encoded” lab that you can check out.
Assembling our stolen password from our blind SQLi attack
After the attack is over and we have received all of our responses, it is time to assemble the password. Again, we are looking for responses of 10 seconds or more, like this:
The Community Edition will not allow you to filter the results easily, so in my case, I filtered by response time and then wrote the characters down next to their proper position number inside of a table.
All that is left is to try and log in.
Success!
Potential Impact of a SQLi attack using this method
We got into the administrator’s account. We have the keys to the castle! Everything is up for grabs at this point. The website, any PI or PII it contains… real ultimate power. If this were successful on an organization’s site the damage to both reputation and finances could be devastating.
Prevention
Whitelisting permitted input values and using different logic to deliver the required behavior is a good start. According to PortSwigger:
“For a parameterized query to be effective in preventing SQL injection, the string that is used in the query must always be a hard-coded constant. It must never contain any variable data from any origin.”
Notes
Rana Kalil’s tutorial video on this subject was extremely helpful when writing and thinking about this write-up. She does an excellent job educating and helping others. I completed this lab back in November of 2023, but I wanted to return to it to both cement my understanding of it and to balance out all of the XSS write-ups I have been doing with something else. SQLi is my first love. I will occasionally return to those labs for further write-ups.
Share this:
Filed under: Cybersecurity,Pentesting,PortSwigger Academy,SQLi - @ December 27, 2023 3:35 am