When the application returns no useful data - no error, no UNION result, no visible difference between queries that work and queries that don't - the attacker switches to a binary oracle. One yes-or-no per request. Yes, you read that right: one bit per request, and the database does the work.
- L8Blind injection overview
- Distinguish a boolean oracle (page present vs. page missing) from a length oracle and a content oracle.
- Recover a string one character at a time with
SUBSTRINGandASCII. - Use binary search to halve the alphabet on every request.
- Explain why boolean extraction is the slowest blind technique - and why it is still the most common.
- Boolean oracle
- A side channel where the attacker observes a true/false answer. The two states are typically "page present" vs. "404 Not Found" or "Welcome" vs. "Invalid".
- SUBSTRING
- A SQL function that returns a slice of a string. SUBSTRING(s, n, 1) returns the n-th character - the building block of every boolean extraction.
- Binary search oracle
- A request that asks "is the ASCII code of the next character greater than N?" and halves the range on every request, recovering the character in log2(alphabet) requests instead of alphabet.
- Throughput
- Bits per second the attacker can extract. Boolean oracles are the slowest of the three blind channels; the next lesson covers the time-based alternative that works when even the page response is identical.
One yes-or-no per request
The simplest boolean oracle is a 200 vs. a 404. The attacker crafts a WHERE clause that, when true, returns the page; when false, returns a generic 404. The two responses are byte-for-byte identical except for the status code and the body length.
-- TRUE → page returns 200
?id=1 AND 1=1
-- FALSE → page returns 404
?id=1 AND 1=2
-- Now the attacker can wrap any subquery in the same shape:
?id=1 AND (SELECT SUBSTRING(user(),1,1))='a'
?id=1 AND ASCII(SUBSTRING((SELECT user()),1,1))>100
?id=1 AND ASCII(SUBSTRING((SELECT user()),1,1))>115The first payload narrows the first character down to 'a'. The second payload asks if the ASCII code of the first character is greater than 100. The third is greater than 115. The attacker keeps halving until only one value is possible. Each character of the secret takes about 7 requests with a binary search.
Recover a hidden string
The sandbox is faking a database that holds an 8-character string. Cycle the alphabet at each position. The page returns "Welcome back, user." on a true match and 404 on a false one. Recover the full string and see how many requests you used.
SELECT id FROM users WHERE id = 1 AND (substring(secret,1,1) = 'a')-- Slow, but it always works
Boolean extraction is the workhorse of bug bounty hunters. The HBGary Federal breach of 2011 used a boolean blind SQLi to dump the entire CEO inbox. The attacker (Anonymous' LulzSec) recovered one character at a time over the course of a single afternoon, and the breach was only discovered after the data was posted publicly. In a typical modern bug bounty report, the response is just a status code, but the technique is the same.
Throughput is the bottleneck. 7 requests per character, 50 characters per second with a slow scanner, 350 characters per second - enough to dump a 8-character password hash in under a second. A full 100,000-row table with a 32-character email column is a multi-day job; attackers use UNION or time-based extraction first when they can.
The fix is still the same
Parameterised queries prevent the attack entirely - the predicate becomes a typed boolean, the subquery never executes, and the only signal an attacker can observe is the value they submitted.
// SAFE - the predicate is data, not code
const query = 'SELECT id FROM users WHERE id = $1 AND ($2::boolean)';
await db.query(query, [id, isAdmin]);What to remember
- A boolean oracle gives you one bit per request. Two requests tell you which half of the alphabet the next character is in.
- The classic primitives are
SUBSTRINGandASCII. Binary search keeps the request count near log2(alphabet). - The oracle can be a status code, a length difference, a redirect, or a single character in the body. Any deterministic difference is enough.
- Slow but reliable. Most real-world blind extractions still use boolean - it is the only oracle available in most apps.
Knowledge check
0/3 answered · 0 correct1.Why is "binary search" faster than cycling the alphabet for boolean extraction?
2.The application returns a 200 on a true predicate and a 200 on a false predicate - but the body is different by exactly one word ("Welcome" vs. "Invalid"). Is that a usable oracle?
3.A boolean SQLi takes ~7 requests per character. How long to recover a 32-character password hash at 50 req/sec?