SQL InjectionStacked Queries

Stacked Queries: Running Multiple Statements Through One Injection

Stacked-query injection terminates the intended statement and appends another one. Learn why driver protocol and privileges matter, how a harmless delay proves batching, and how to prevent it.

SelfSec Team4 min read
Part 6 of 8from theSQL Injectionseries
1; WAITFOR DELAY '0:0:2' --

At a glance

CWE-89
Interpreter
SQL database engine
Required condition
The driver and database accept multiple statements in the vulnerable execution path.
Potential impact
Independent read, write, administration, or destructive statements may run.
Primary defense
Parameterized queries, single-statement execution, and minimal database privileges.

Safe practice: use benign proof only, in a disposable local lab or on a system you are explicitly authorized to test.

On this page

Most SQL injection bends an existing query — a UNION bolted onto a SELECT, a boolean appended to a WHERE. Stacked queries do something blunter. By terminating the original statement with a semicolon and writing a second one after it, an attacker stops borrowing the application's query and runs an arbitrary statement of their own. A bug that looked read-only suddenly becomes a way to write, delete, or — on the right engine — execute operating-system commands.

This post covers how statement stacking works, the driver and engine combinations that actually allow it, and why it turns the severity of an injection up sharply.

When this technique applies

The SQL text must be injectable, the database and client protocol must accept more than one statement in the same execution, and the appended statement must be valid under the application's database privileges. A semicolon that merely causes a syntax error does not prove stacking.

Support is not a property of the database name alone. Driver mode, prepared versus simple protocol, connection settings and API method all affect whether multiple statements are accepted. Test the deployed combination, not a generic matrix copied from another stack.

How it happens

The injection point is the usual concatenation. What stacking adds is a database driver that will execute more than one statement from a single string. Take a status-update endpoint that splices an id straight into the query:

$id = $_GET['id'];
$db->exec("UPDATE orders SET seen = 1 WHERE id = $id");

The application intends exactly one UPDATE. But the id is unbound, so an attacker can close that statement and start another:

1; WAITFOR DELAY '0:0:2' --

On SQL Server, if the driver hands both statements to the database, the order is marked seen and the batch pauses for two seconds. A matched no-delay control distinguishes the appended statement from ordinary latency. Nothing is created, changed beyond the intended update, or deleted by the proof.

Why stacking changes impact

Other SQL injection techniques usually operate inside the original statement. Stacking can introduce an independent statement type: a read-only-looking endpoint may reach an INSERT, UPDATE, data-definition operation or administrative procedure if the database account has those rights. Some engines expose optional operating-system integration to highly privileged roles, making least privilege especially important.

Stacking does not automatically grant write or administrative access. It lets the attacker attempt another statement with exactly the privileges already granted to the application account.

When stacking actually works

Statement stacking is not universal; it depends on what the driver sends in one call.

Platform Typical behavior to verify
SQL Server T-SQL batches are commonly accepted by direct command APIs
PostgreSQL Simple-query protocol can carry multiple statements; extended prepared execution is stricter
MySQL Multi-statements are commonly disabled unless explicitly enabled by the client
SQLite Typical APIs prepare or step one statement at a time

These are review hints, not guarantees. A wrapper can split strings itself, a connection flag can enable batching, and a framework upgrade can change protocol behavior.

Safe verification and false signals

  • Use a fixed, reversible or timing-only statement in an isolated lab.
  • Pair it with a no-delay control and repeat at low request volume.
  • Confirm the first statement still behaves as expected; a syntax error alone is insufficient.
  • Do not create accounts, alter permissions, enable administrative features or touch real data.
  • Record the exact driver, version, protocol and connection setting with the result.

The fix: parameterize and remove the privilege

Binding the input closes stacking the same way it closes every other variant — there is no statement boundary to write past when the value is a parameter:

using var cmd = new SqlCommand(
    "UPDATE orders SET seen = 1 WHERE id = @id", connection);
cmd.Parameters.Add("@id", SqlDbType.Int).Value = id;

With a parameter, the entire supplied value is treated as one invalid id, not a second statement. Reinforce it by choosing data-access APIs that forbid multi-statements and by running under a least-privilege database account: a read path should not hold schema-changing rights, broad cross-table writes or access to operating-system integration.

How SelfSec finds it

SelfSec's SQL injection engine covers 19 database families with error-based, boolean, time-based, UNION, stacked and out-of-band techniques. For stacked queries it tests the deployed engine, driver, protocol and connection behavior as one combination rather than assuming support from the database name alone. It reports the issue only when the second statement produces repeatable evidence and includes a reproduction request. Core scan processing runs locally on 127.0.0.1; managed OOB confirmation uses the documented remote callback flow when enabled.

References

SelfSec is intended strictly for authorized security testing of systems you own or are explicitly permitted to assess.

Do both things about SQL Injection

SelfSec covers this class from both sides — the scanner confirms it in your own app, the firewall blocks it in front of your origin while the fix ships.

The SQL Injection series

  1. 01OverviewSQL Injection, Explained: From Login Bypass to Full Data Extraction6 min
  2. 02Boolean-Based BlindBoolean-Based Blind SQL Injection: Extracting Data One Bit at a Time4 min
  3. 03Error-BasedError-Based SQL Injection: Turning Database Errors Into Data4 min
  4. 04Out-of-BandOut-of-Band SQL Injection: Confirming a Blind Flaw Through DNS5 min
  5. 05Second-OrderSecond-Order SQL Injection: When Stored Input Detonates Later4 min
  6. 06Stacked QueriesStacked Queries: Running Multiple Statements Through One InjectionReading
  7. 07Time-Based BlindTime-Based Blind SQL Injection: Reading a Database Through the Clock5 min
  8. 08UNION-BasedUNION-Based SQL Injection: Appending Your Own Result Set4 min

Related reading