Advanced Techniques

Stacked Queries · Second-Order · Filter Bypass
05.06 · SQL Injection

Beyond the Basics

Once an application blocks simple injections, attackers escalate to advanced techniques that circumvent filters, exploit stored data, and abuse query structure in unexpected ways.

Stacked Queries

Using semicolons to execute multiple independent SQL statements in a single request.

Second-Order Injection

Payloads stored safely but triggered when the stored data is used in a later query without sanitization.

ORDER BY Injection

Manipulating sort clauses with CASE expressions to extract data bit by bit through sort-order differences.

Filter Bypass

Evading keyword blocklists through case alternation, inline comments, encoding, and creative syntax.

LAB 01

Stack Attack

Acme Corp's product search page runs a SELECT query. But what if the database supports stacked queries—multiple statements separated by semicolons? You could piggyback an entirely different statement onto the search.

SELECT name, price FROM products WHERE name LIKE '%[INPUT]%'
CHALLENGE

Use a stacked query (semicolon) to create a new admin user in the users table while searching for products.

Search Results
NamePrice
users table (check for new entries)
IDUsernamePasswordRole

Close the LIKE string, end the first statement with a semicolon, then write an INSERT:

'; INSERT INTO users (username, password, role) VALUES ('hacker', 'pwned', 'admin') --

The semicolon separates the SELECT from your malicious INSERT. The -- comments out the trailing %'.

Stacked queries are devastating because they allow any SQL operation, not just data extraction. The attacker is no longer limited to the original statement type. MySQL with PHP (mysql_query) historically blocked stacked queries, but PostgreSQL, SQL Server, and modern MySQL drivers often allow them. Always use parameterized queries regardless.

LAB 02

The Sleeper Agent

This is a two-step attack. Step 1: Register with a carefully crafted username. The registration form properly escapes input (parameterized query). Step 2: View "My Profile," which uses the stored username directly in a new query without sanitization. The payload sleeps in the database until triggered.

STEP 1 — REGISTRATION

Register (Safe Insert)

The username is stored safely via parameterized query. No injection occurs here.

INSERT INTO users (username) VALUES ($1) -- parameterized!
STEP 2 — PROFILE VIEW

View Profile (Vulnerable)

The stored username is concatenated directly into a new query. The injection triggers here.

SELECT * FROM users WHERE username='[stored_value]'
CHALLENGE

Register with a username that will cause injection when your profile is viewed. The goal: make the profile query return the admin's data instead of yours.

users table
IDUsernameEmailRole

Register with username: admin' --

Step 1 safely stores it as the literal string admin' --.

Step 2 builds: SELECT * FROM users WHERE username='admin' --'

The stored quote closes the string, -- comments out the rest, and the query returns the admin's row.

Second-order injection is particularly hard to detect because the input point and the trigger point are in different parts of the application. The registration form did everything right—it used parameterized queries. But the profile page trusted stored data as if it were safe. Every query must use parameters, even when the data source is your own database.

LAB 03

Sort Order Secrets

The employee directory lets users sort results by column. The sort parameter goes directly into an ORDER BY clause. You cannot use UNION here (ORDER BY comes after SELECT), but you can use a CASE expression to change the sort order based on a boolean condition—effectively asking the database yes/no questions.

SELECT name, department, salary FROM employees ORDER BY [INPUT]
CHALLENGE

Use an ORDER BY CASE injection to determine: does the admin (employee ID 1) earn more than $90,000? The sort order will change based on the answer.

QUICK SORT:
employees table
NameDepartmentSalary

Inject a CASE expression that sorts by different columns depending on a condition:

(CASE WHEN (SELECT salary FROM employees WHERE id=1) > 90000 THEN name ELSE department END)

If the condition is TRUE, results sort by name. If FALSE, they sort by department. Compare the two orderings to infer the answer.

ORDER BY injection is a form of inferential (boolean-based) extraction. You cannot see data directly, but you can ask true/false questions and observe which sort order appears. By refining the condition (> 90000, > 95000, > 92500...), an attacker can binary-search for exact values. This is slow but unstoppable if the ORDER BY parameter is not parameterized.

LAB 04

Filter Evasion

The developers got smart. They added a keyword filter that blocks common SQL injection terms. Your input is checked against a blocklist before it reaches the query. But blocklists are notoriously easy to bypass...

SELECT name FROM products WHERE name LIKE '%[INPUT]%'
ACTIVE FILTERS (CASE-SENSITIVE EXACT MATCH)
UNION ' SELECT DROP
CHALLENGE

Bypass the keyword filter and extract usernames from the users table. The filter is case-sensitive and checks for exact keyword matches.

BLOCKED BY FILTER
Query Results
Results

The filter is case-sensitive. It blocks UNION and SELECT, but what about mixed case?

Try: 0) UniOn SeLeCt username FROM users--

Or use inline comments to break up keywords: 0) UN/**/ION SE/**/LECT username FROM users--

The SQL engine ignores case and strips comments, but the naive filter does not.

Blocklist-based filtering (also called "blacklisting") is fundamentally flawed for SQL injection prevention. Attackers can use mixed case, inline comments (/**/), URL encoding, Unicode normalization, and countless other tricks. The only reliable defense is parameterized queries (prepared statements), which separate code from data at the protocol level. No amount of filtering can match the security of proper parameterization.