Login
ChallengesLearn
Scoreboard
Teams
SPNZ

LearnSQL InjectionSQL Injection: Boolean-based blind
SQL Injection·Lesson 9 of 20

SQL Injection: Boolean-based blind

The slow, methodical cousin of UNION. One yes-or-no per request, characters extracted one bit at a time. The technique you fall back to when nothing else works.

Intermediate14 min
SQLiBooleanBlind
Loading lesson…
PreviousSQL Injection: Blind injection overviewNextSQL Injection: Time-based blind

© 2026 SPNZ.

Terms of ServicePrivacy PolicyCookie Policy

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.

Prerequisites
Read these lessons first:
  • L8Blind injection overview
What you'll be able to do
  • 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 SUBSTRING and ASCII.
  • 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.
Key terms
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.
What is it?

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.

sqlvulnerable
-- 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))>115

The 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.

Bits per request, requests per character
Mini Map
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Try it

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.

devexfil.local/panel?target=acme-portal
blind-extractor
Recovered so far
________
0 / 8 characters
Position
Requests used
0
Worst case: 26 × 8 = 208
Query (cycle the alphabet)
SELECT id FROM users WHERE id = 1 AND (substring(secret,1,1) = 'a')-- 
Try a character
Welcome back, user. is the only signal. If the WHERE is true, the page renders; if false, the page returns 404. Pick a character and press Send.
Real-world relevance

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.

Mitigation

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.

javascriptparameterised
// 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]);
Further reading
  • Blind SQL injection (PortSwigger)(PortSwigger)
  • HBGary Federal breach write-up(Ars Technica)
  • Binary search with ASCII comparisons(sqlmap)
Key takeaways

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 SUBSTRING and ASCII. 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 correct
  1. 1.Why is "binary search" faster than cycling the alphabet for boolean extraction?

  2. 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. 3.A boolean SQLi takes ~7 requests per character. How long to recover a 32-character password hash at 50 req/sec?