Blind SQL Injection
Union extraction (Chapter 4) needs the app to show query results. Often it doesn't — a login that just says "success" or "failure," a search that returns a generic page. The query is still injectable, but you can't see the answer. Blind SQLi recovers the data anyway, by turning the application into a yes/no oracle and reading one bit at a time. It's slower than union-based, but no less complete — and fully automatable. (Defensive framing: own systems / training labs only.)
The Core Idea: The App as a Boolean Oracle
You can't see data, but you can ask the database a true/false question and observe whether the app behaves differently. If you can reliably distinguish "the condition was true" from "the condition was false," you can ask questions about the data and reconstruct it bit by bit. The two ways to get that true/false signal define the two sub-types.
Boolean-Based Blind
Here the page content differs (even subtly) between a true and a false condition — different text, different length, present-or-absent results, a 200 vs a 500. First establish the tell:
Now swap the constant for a question about the actual data, and read the answer from which page you get back:
Binary Search: ~7 Requests per Character
Testing every possible character one by one is wasteful. Instead use a binary search on each character's ASCII value — each request halves the remaining range, so a full byte (0–127 printable, really ~95 candidates) is pinned in about 7 requests:
Then advance to position 2 (SUBSTRING(...,2,1)), repeat, and you've reconstructed the whole password. You typically first extract the length (' AND LENGTH(password)=N) so you know when to stop.
Time-Based Blind
Sometimes the page is identical for true and false — no content difference at all. Then you manufacture your own signal: make the database pause when the condition is true, and read the answer from the response time:
| Database | Conditional delay |
|---|---|
| MySQL | ' AND IF(condition, SLEEP(5), 0)-- |
| PostgreSQL | ' AND (CASE WHEN condition THEN pg_sleep(5) ELSE pg_sleep(0) END)-- |
| SQL Server | '; IF (condition) WAITFOR DELAY '0:0:5'-- |
Why Blind Is Slow but Not Safe
The contrast with union-based is stark and worth internalizing:
| Union-based | Blind | |
|---|---|---|
| Data per request | whole values / rows / tables | one bit (true/false) |
| Speed | fast (seconds) | slow (1 char ≈ 7 requests; +delay for time-based) |
| Needs visible output? | yes | no (content) / none at all (timing) |
| Recovers the same data? | yes | yes — eventually, completely |
sqlmap (Chapter 10) drives boolean and time-based blind extraction automatically, issuing the thousands of inference requests and reassembling the data with no manual effort. So the slowness of blind SQLi protects nobody in practice; the data comes out just the same. The lesson repeats: don't rely on hiding output — only parameterized queries (Chapter 7) remove the injectable oracle entirely, so there's no true/false to read and no delay to time.
Hands-On Exercises
Explain how a tester first establishes a boolean oracle (the true/false tell) on a page with no visible data, then walk a binary search extracting the first character of a secret, showing why it takes ~7 requests rather than ~95.
📄 View solutionExplain when you must use time-based instead of boolean-based blind, and write a MySQL time-based payload that tests whether the first character of the admin password is 'a'. Explain how the answer is read, and one reason time-based is noisier/less reliable.
A developer argues "our login only returns success/failure and shows no errors, so SQLi can't leak data." Rebut this using blind SQLi: explain how data still leaks with no output, why time-based works even on identical responses, and why only parameterization actually closes it.
📄 View solutionChapter 5 Quick Reference
- Blind SQLi — no visible output; turn the app into a true/false oracle and extract data one bit at a time
- Boolean-based — page content differs on true vs false (
' AND 1=1vs' AND 1=2); ask data questions and read the page - Binary search on each character's ASCII (
> 109?) → ~7 requests/char; grabLENGTH()first to know when to stop - Time-based — make the DB pause on true (
SLEEP/pg_sleep/WAITFOR DELAY); read the answer from response time - Time-based works even when responses are byte-for-byte identical — the universal fallback; only needs your SQL to execute
- Blind recovers the same data as union, just slower (one bit/request); “slow” ≠ “safe” — sqlmap automates it
- Hiding output/errors only forces boolean → time-based; it never removes the injectable oracle
- Only parameterized queries (Chapter 7) eliminate the oracle — no true/false to read, no delay to time
- Next chapter: beyond SELECT — INSERT/UPDATE/DELETE, stacked queries, and second-order SQLi