Authorization disclaimer. This walkthrough is limited to the intentionally vulnerable PortSwigger Web Security Academy lab linked below. Test only systems you own or are explicitly authorized to assess.

Overview

Here the database communicates through latency rather than content: cASE WHEN (…) THEN pgsleep(10) ELSE pgsleep(0) END establishes length 20; SUBSTRING(password,position,1)=candidate identifies characters with one concurrent request. A controlled sleep supplies the Boolean branch needed to recover the synthetic administrator password.

Lab objective/setup

Exercise Blind SQL injection with time delays and information retrieval through the endpoint and input recorded in the lab note. CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END establishes length 20; SUBSTRING(password,position,1)=candidate identifies characters with one concurrent request.

What I observed

CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END establishes length 20; SUBSTRING(password,position,1)=candidate identifies characters with one concurrent request.

`CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END` establishes length 20; `SUBSTRING(password,position,1)=candidate` identifies characters with one concurrent request.
`CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END` establishes length 20; `SUBSTRING(password,position,1)=candidate` identifies characters with one concurrent request.
CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END establishes length 20; SUBSTRING(password,position,1)=candidate identifies characters with one concurrent request. The visible pane contains the resulting application response.
CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END establishes length 20; SUBSTRING(password,position,1)=candidate identifies characters with one concurrent request. The visible pane contains the resulting application response.
`CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END` establishes length 20; `SUBSTRING(password,position,1)=candidate` identifies characters with one concurrent request.
`CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END` establishes length 20; `SUBSTRING(password,position,1)=candidate` identifies characters with one concurrent request.

Why it is vulnerable

CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END establishes length 20; SUBSTRING(password,position,1)=candidate identifies characters with one concurrent request. The application passes that value into the security-sensitive operation without the context-specific validation or encoding needed to prevent this result.

Walkthrough in the authorized PortSwigger lab

Calibrate an ordinary TrackingId request, then place pg_sleep(10) behind a CASE WHEN predicate. First identify the password length; next test one candidate character at one position per request, using a single concurrent request so queueing does not obscure the timing result.

Intruder varies N in a TrackingId CASE predicate that calls pg_sleep(10) when LENGTH(password)=N for the administrator account.
Intruder varies N in a TrackingId CASE predicate that calls pg_sleep(10) when LENGTH(password)=N for the administrator account.
The timing results single out password length 20 through the approximately ten-second PostgreSQL response delay.
The timing results single out password length 20 through the approximately ten-second PostgreSQL response delay.
A second single-threaded Intruder attack varies position and character; delayed pg_sleep(10) responses reveal each administrator password character.
A second single-threaded Intruder attack varies position and character; delayed pg_sleep(10) responses reveal each administrator password character.

Verification

The lab is confirmed when CASE WHEN (...) THEN pg_sleep(10) ELSE pg_sleep(0) END establishes length 20; SUBSTRING(password,position,1)=candidate identifies characters with one concurrent request. The captured result and the Academy solved state provide the verification; an ordinary status code alone does not.

Impact and defensive lesson

A blind timing channel can leak secrets despite identical pages and status codes. Use bound parameters, enforce database statement timeouts, and alert on repeated conditional sleep expressions and systematic cookie probes.

References