Login
ChallengesLearn
Scoreboard
Teams
SPNZ

LearnSQL InjectionSQL Injection: Time-based blind
SQL Injection·Lesson 10 of 20

SQL Injection: Time-based blind

When the page gives you nothing - no error, no length delta, no status change - the database clock does. SLEEP(5) and WAITFOR DELAY as the only oracle.

Advanced16 min
SQLiTimeBlind
Loading lesson…
PreviousSQL Injection: Boolean-based blindNextSQL Injection: Database enumeration

© 2026 SPNZ.

Terms of ServicePrivacy PolicyCookie Policy

When the application returns nothing - no error, no length delta, no status change - the only oracle left is the database clock. The attacker crafts a query that sleeps for a long time when a predicate is true, returns immediately when it is false. The response time is the only signal.

Prerequisites
Read these lessons first:
  • L8Blind injection overview
What you'll be able to do
  • Use SLEEP() and pg_sleep() to convert a boolean predicate into a delay.
  • Choose a delay long enough to clear network jitter (typically 5 seconds in MySQL, 2 in PostgreSQL).
  • Recognise when time-based extraction is the only oracle left.
  • Explain why time-based extraction is the slowest of the three blind channels.
Key terms
Time oracle
A side channel where the attacker observes how long the server takes to respond. The only oracle available when the application is fully opaque (same status, same body, no length delta).
SLEEP(n)
MySQL function that pauses execution for n seconds. The PostgreSQL equivalent is pg_sleep(n). SQL Server uses WAITFOR DELAY '00:00:05'.
IF()
MySQL and PostgreSQL conditional expression. The pattern IF(condition, SLEEP(5), 0) is the canonical time-oracle primitive.
Jitter
Network and application noise that smears response times. A 5-second delay is the typical lower bound so that jitter (usually <500ms) does not pollute the signal.
What is it?

When the only signal is the clock

Time-based extraction is the technique of last resort. It works when every other channel is closed - same status code, same body length, no errors, no reflected output. The attacker uses the database scheduler itself as the oracle.

sqlvulnerable
-- MySQL
?id=1 AND IF((SELECT SUBSTRING(user(),1,1))='a', SLEEP(5), 0)

-- PostgreSQL
?id=1 AND (SELECT CASE WHEN SUBSTRING(current_user,1,1)='a' THEN pg_sleep(5) ELSE pg_sleep(0) END)

-- SQL Server
?id=1; IF SUBSTRING(system_user,1,1)='a' WAITFOR DELAY '00:00:05'

-- Oracle
?id=1 AND DECODE(SUBSTR(user,1,1),'a',DBMS_PIPE.RECEIVE_MESSAGE('a',5),0)=0

The delay must be long enough to clear network jitter. Most scanners default to 5 seconds for MySQL/PostgreSQL and 2 seconds for SQL Server, where the implementation is more accurate. Anything below the jitter ceiling produces false positives and is the #1 reason time-based scanners report incorrect results on production networks.

Boolean predicate → wall-clock delay
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 string with the clock

The sandbox simulates a server that sleeps for ~1 second when the predicate is true and returns immediately when it is false. The binary search half-interval updates after every probe. Watch the range and the wall-clock delay evolve.

devexfil.local/panel?target=acme-portal
blind-extractor
Recovered
________
0 / 8 chars
Current range
31–126
Probe: L1,1
Last delay
-
Requests: 0 · threshold 800ms
Current predicate
SELECT id FROM products WHERE id = 1 AND IF((ASCII(SUBSTRING(secret,1,1)) > 78), SLEEP(1), 0)-- 
Press Send & measure. The simulated server will sleep for ~1 second if the predicate is true, return immediately otherwise.
Real-world relevance

The slow lane that every scanner uses

Most bug bounty payouts for time-based SQLi are 50–80% of the value of an equivalent UNION or error-based finding. The reason is throughput. A 5-second delay per request at 1 bit each is 0.2 bits per second per thread. Recovering a 32-character hash takes 7 × 32 × 5 = 1,120 seconds (almost 19 minutes) on a single connection. Real-world attackers run dozens of parallel connections and target the slowest findings only when the data justifies it.

Time-based extraction is also noisy at the network layer. A volumetric SQLi burst that holds database connections open for minutes is visible to any WAF or rate-limiter; defenders are usually tipped off before the dump completes.

Mitigation

The fix is unchanged

Parameterised queries make time-based extraction impossible - the conditional expression becomes a typed boolean, the sleep function is never reached, and every request returns in the same number of milliseconds regardless of the input.

javascriptparameterised
// SAFE - the predicate is data, not code
const query = 'SELECT id FROM products WHERE id = $1 AND $2::boolean';
await db.query(query, [id, predicate]);
Further reading
  • Time-based blind SQL injection (PortSwigger)(PortSwigger)
  • MySQL SLEEP() reference(MySQL)
  • PostgreSQL pg_sleep() reference(PostgreSQL)
Key takeaways

What to remember

  • Time-based extraction is the oracle of last resort - used only when status code, body, and length are all identical.
  • The canonical primitives are SLEEP() in MySQL, pg_sleep() in PostgreSQL, WAITFOR DELAY in SQL Server.
  • The delay must clear network jitter - typically 5 seconds in MySQL/PostgreSQL, 2 seconds in SQL Server.
  • Time-based extraction is the slowest blind technique. Parallelise and prefer UNION or error-based extraction when the target allows.

Knowledge check

0/3 answered · 0 correct
  1. 1.When is time-based blind SQLi the right technique to use?

  2. 2.Why is the typical delay set to 5 seconds in MySQL and PostgreSQL?

  3. 3.Why is time-based extraction noisier than boolean at the network layer?