Skip to main content

What is Blind SQL Injection?

Blind SQLi is when the application is vulnerable but HTTP responses don’t return query results or error details — making UNION attacks ineffective since there’s nothing to read back.

Exploiting via Conditional Responses

Consider a tracking cookie processed like this: Blind SQL injection tracking cookie
The app shows a “Welcome back” message if the query returns results — nothing otherwise. That single boolean difference is enough to extract data. Injecting two conditions back to back:
This lets you ask yes/no questions against the database and infer data one bit at a time.

Extracting Data Character by Character

To brute-force the Administrator password, test one character at a time using SUBSTRING:
  • “Welcome back” shown → first char is after m in the alphabet
  • No “Welcome back” → first char is not after t
  • “Welcome back” shown → first char is s
Repeat for each character position until the full password is recovered.
SUBSTRING is called SUBSTR on some databases — check the SQL injection cheat sheet.

Lab: Conditional Response (Administrator Password Extraction)

In this lab, I exploited a blind SQL injection vulnerability in the TrackingId cookie to extract the administrator password character by character. The application returned a different response when the injected condition evaluated to true, which allowed boolean-based extraction.

Working Payload

The injection works because the backend query likely looks like:
By injecting:
I:
  • Closed the original string
  • Appended a boolean condition
  • Commented out the rest of the original query

Mistake I Kept Making

I got stuck multiple times because I forgot to append -- at the end of the payload. Without the comment:
  • The original query was not properly terminated
  • The trailing ' from the application broke the syntax
  • The condition never evaluated correctly
Important detail: -- must usually be followed by a space. Correct pattern:

Extraction Method

I used Burp Intruder to brute-force the password using prefix matching. Strategy:
  1. Start with length = 1
  2. Bruteforce the first character
  3. Increase substring length gradually
  4. Continue expanding the known prefix
Example:
If the response indicated true, the prefix was correct. I increased the substring length from 1 up to 21.

Where It Stopped Working

When I tested length 21, I stopped getting matches. This indicated:
  • The actual password length was likely 20 characters
  • Comparing beyond the real length caused the condition to return false
This confirmed that the full password had been extracted.

Cleaner Alternative Approach

Instead of matching growing prefixes, a more controlled method is testing one character at a time:
This avoids long prefix comparisons and reduces potential mistakes.

Key Takeaways

  • Always terminate injections with -- .
  • A missing SQL comment can completely break blind SQLi.
  • If increasing substring length stops producing matches, you likely exceeded the real string length.
  • Boolean-based blind SQLi works reliably when response differences exist.
  • Burp Intruder is effective for structured character-by-character extraction.

Error-Based Blind SQL Injection

Sometimes a query runs, but the page looks the same whether your condition is true or false. In that case, normal boolean response checks do not help. The workaround is to make the database throw an error only when a condition is true. Then you infer truth from a response difference (500 error, different page length, missing content, etc.).

Two Common Outcomes

  • Conditional error channel: trigger an error only when your condition is true.
  • Verbose DB errors: in misconfigured apps, actual query data may leak directly in error messages.

Conditional Error Pattern

  • 1=2 path returns 'a' and usually no DB error.
  • 1=1 path hits 1/0 (divide-by-zero), causing a DB error.
If the HTTP response changes between these two payloads, you can use this as a true/false oracle.

Extracting Data With Conditional Errors

One-liner (Burp-friendly):
If the request now errors, the condition is true. If it does not, the condition is false. Repeat this per character and position to recover the full value.

Lab: Conditional Errors (Oracle, Administrator Password)

Vulnerable input: TrackingId cookie Goal:
  • Extract administrator password
  • Log in as administrator

Step 1: Confirm Injection (Oracle)

If this is accepted, test a forced failure:
If behavior changes, the parameter is injectable.

Step 2: Confirm users Table

If this works, users exists. Why ROWNUM = 1 matters:
  • A scalar subquery inside || (...) || must return exactly one row.
  • (SELECT '' FROM users) can return many rows -> Oracle throws ORA-01427: single-row subquery returns more than one row.
  • (SELECT '' FROM users WHERE ROWNUM = 1) is capped to one row -> valid.

Step 3: Confirm administrator Row

First, check the row directly:
Then validate your error channel baseline:
Now trigger an error only if administrator exists:
One-liner (Burp-friendly):
False-user check (should stay 200):
  • Error response (500) -> condition true
  • Normal response (200) -> condition false
Why both payloads can still return 200 in blind SQLi:
  • username='administrator' and username='fake_user' checks can both be syntactically valid and non-crashing.
  • 200 only means the query executed, not that your condition was true.
  • You need a conditional side effect (error or delay) to create a visible true/false signal.

Step 4: Find Password Length

One-liner (Burp-friendly):
Adjust the number until you identify the exact length.

Step 5: Extract Password Characters

One-liner (Burp-friendly):
Change position and candidate character until all characters are confirmed. Recovered password in this run:

Python Automation

1) My Version
2) My Version (AI Improved)
3) Tutorial Version

Extracting Sensitive Data via Verbose SQL Errors

Some applications expose raw database errors when input breaks a query. This is often a misconfiguration, and it can reveal exactly how your payload is being embedded. Example after injecting a single quote into an id parameter:
What this tells you:
  • You are inside a single-quoted string.
  • The injection point is in a WHERE clause.
  • You likely need to close the quote and comment out the tail to keep syntax valid.
This can turn an otherwise blind issue into a visible one if the app reflects database error details back to you.

Forcing Data into an Error with CAST()

You can intentionally trigger a type-conversion error that includes query data. CAST() is useful for this:
If example_column is text, many databases throw an error like:
Now the value (Example data) is leaked in the error message. Why this matters:
  • It gives direct data output when normal page content is blind.
  • It can still work when strict character limits make larger conditional payloads hard to use.
  • It is often faster than boolean extraction when verbose errors are available.

Lab: Visible Error-Based SQLi (Administrator Password)

Goal: leak the administrator password from DB errors, then log in.

Quick Approach

  1. Confirm quote-breaking behavior in TrackingId:
  1. Use a payload that forces string-to-int conversion on the password:
  1. Read the DB error. If verbose errors are enabled, the failing value is shown:
  1. Use the leaked password to authenticate as administrator.

Useful Mistakes I Made (and Fixes)

  • Mistake: I kept the original tracking ID prefix (GA9UUdl1nUvjSjgU) before the injection. Result: payload was too long and got cut mid-query. Seen as: ... FROM users WHE'. Expected char Fix: remove the original value and start directly with ' to save space.
  • Mistake: I used the longer filter payload first:
Result: truncation before WHERE finished. Fix: start with shorter extraction (LIMIT 1) to validate the technique, then refine if needed.
  • Mistake: forgetting to neutralize the trailing quote from the original query. Fix: close the string and end with -- so the rest of the server query is ignored.

Compact Payload Options

Primary (short, reliable for technique validation):
If multiple rows require iteration:
Then increase OFFSET until you leak the target account, or switch to a shorter user filter if the lab allows it.

Lab #18 Notes (My Run)

End goal: exploit SQLi in TrackingId, leak admin credentials from users, and log in as administrator. Observed backend pattern after quote testing:
Payload progression I used:
Recovered password in this run:
Quick validation logic:
  • If payload is malformed/truncated, you get unterminated string errors.
  • If CAST forces string -> int conversion, the DB error leaks the selected value.
  • Once password is leaked, authenticate as administrator to solve the lab.

Understanding Expected char

This error text is easy to misread. It usually means your payload was truncated, which left an unclosed string. What a string literal means here:
  • A string literal is plain text wrapped in single quotes in SQL, like 'admin'.
  • In this query, the cookie value is placed inside a string literal:
  • If your injection adds or breaks ' quotes incorrectly, SQL thinks the text string started but never finished.
Example error:
What is happening:
  • Input was cut around position 95 (cookie length/validation limit).
  • WHERE was truncated to WHE.
  • Truncation left a dangling '.
  • SQL parser sees a started string literal with no closing quote.
What Expected char actually means:
  • Not “I need a specific character”.
  • It means: “string literal started, but input ended before completion”.
Visual:
Fix: shorten the payload to fit the limit.
This removes unnecessary spaces and avoids wasting characters on the original tracking ID prefix.