Hi, my fellow hackers. This is Rayofhope. I have over 5 years of experience and am currently working as a consultant with a Big 4 firm.
It’s Day 13 of posting all the PortSwigger labs, not just the solutions. I’ll break down why we take each step, because once the ‘why’ is clear, the ‘how’ becomes easy.
Let’s Start:
Before you go for this blog, make sure to read the Previous one
Link to Previous Blog: https://medium.com/@arayofhope7/day-12-blind-sql-injection-with-conditional-errors-zero-to-hero-blind-injection-portswigger-e94f9e3977a5
Video Walkthrough — You can watch the video or read the blog, totally up to you. But if you ask me, start with the video, then read the blog to connect all the dots.
This is how the lab looks.
“This lab contains a SQL injection vulnerability. The application uses a tracking cookie for analytics and performs a SQL query containing the value of the submitted cookie. The results of the SQL query are not returned.
The database contains a different table called users
, with columns called username
and password
. To solve the lab, find a way to leak the password for the administrator
user, then log in to their account.”
Vulnerable Parameter:
Tracking Cookie
End Goals:
1. Exploit SQLi to output the password of the administrator user
2. Log in as the administrator user.
Request was intercepted and sent to the intruder.
This is what the normal response looks like.
As we know, the cookie parameter is vulnerable. I used a single quote ('
), and it returned a 500 Internal Server Error. This indicates that the single quote might be breaking the SQL query, suggesting that the query could be part of custom developer-written code.
In the response, it returned an error, and the error message included the SQL query, which could be really sensitive. Let’s see how we can use it to craft a new payload.
This is what we had in the response body: Unterminated string literal started at position 52 in SQL SELECT * FROM tracking WHERE id = ‘WPq6tHyy2UpzpneU’’. Expected char
The server is not properly configured to handle errors. Instead of showing a generic error message, it leaks sensitive SQL query details, exposing the backend and increasing the risk of exploitation.
- If you look at the SQL query revealed by the server, you’ll notice that at the end it uses double single quotes (
''
). In the above scenario:SQL SELECT * FROM tracking WHERE id = 'WPq6tHyy2UpzpneU''.
.This suggests that the single quote is not properly closed, and the application is trying to handle it internally by escaping the quote, possibly to prevent SQL Injection.
Now, I used --
and it commented out the rest of the code after the first single quote. Quick tip: you can also use #
or --
(with a space and another dash) to comment out the rest of the code.
But it does create a verbose error, which could be the same way we may retrieve the administrator password.
Note: —
The CAST()
function is used to convert a syntax from one data type to another
Like: ‘ AND CAST((SELECT 1) AS INT) — —
But wait, we got an error even after using ‘AND CAST((SELECT 1) AS INT)’.
But in the response, we got the solution as well. It says that ‘the argument of AND must be of type boolean, not type integer,’ which clearly explains why we are getting the error.
And there we go, we balanced the syntax: (WPq6tHyy2UpzpneU’ and 1=cast ((select 1) as int) — — ), and it returned a 200 OK.
It forces the query to return a true
condition by ensuring that 1=1
is evaluated. Here, we used the result of select 1
to an integer, making the comparison work as expected.
Used ' AND 1=CAST((SELECT username FROM users) AS INT)--
. Let's see if the error message returned the username.
There we have a verbose error, but not the username. However, we know that the syntax we are using is correct.
Let’s see if the response reveals the username.
It’s a generic error, likely because the query is attempting to retrieve more columns than expected, which is why the username isn’t being returned. Let’s try using LIMIT
to restrict the results and see if that helps.
Executed the payload ‘ AND 1=cast((select username FROM users LIMIT 1) as INT) — — , and it successfully returned the username in the response.
Used the payload ' AND 1=CAST((SELECT password FROM users LIMIT 1) AS INT)--
, and it successfully returned the password qpqb5nyimb0azddj2ahz
in the response.