Вы находитесь на странице: 1из 9

Artigos

Velhas falhas, novas técnicas:


Error Based SQL Injection
Por Ulisses Castro, Conviso Security Labs

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 1
Velhas falhas, novas técnicas: Error Based SQL
Injection
Por Ulisses Castro | Conviso Security Labs

Todos que já buscaram saber como funcionam os ataques de SQL Injection


sabem que é uma técnica antiga, porém sempre é bom ver novas ideias surgindo
que acabam servindo para ampliar as possibilidades. Quero compartilhar aqui
algumas técnicas não convencionais de extração de dados que normalmente são
utilizadas por "seres humanos", mas porque dizer assim?

Por mais acostumado a automatizar tudo utilizando as melhores e mais variadas


ferramentas para pentest, você não pode esquecer que em algumas das muitas
situações encontradas durante o processo, é manualmente e com aquele
pensamento "fora da caixa" que vai conseguir um resultado inesperado e
diferenciado de qualquer outra abordagem similar no mercado.

Por isso venho aqui compartilhar uma técnica semelhante a utilizada no MS-SQL
Server que é bem conhecida para quem tem experiência em ataques neste tipo
de banco de dados, entretanto pouco conhecida e utilizada em outros, em
específico MySQL, Oracle, PostgreSQL e SyBase que é o Error Based Blind SQL
Injection, nada mais do que a extração de dados baseada em erro.

Ferramentas fechadas de renome e também aquelas que tem seu código fonte
aberto não abordam tal técnica, perdendo uma grande gama de possibilidades e
acabam não suprindo um gap que nós pentesters encontramos. Apesar de não
abordar neste artigo especificamente como utilizá-la em conjunto com outras
técnicas para bypass em Web Application Firewalls (WAFs), deixo aqui algumas
ideias que passam batido em muitos WAFs como HPP em conjunto com SQL
Injection e existem até aqueles que deixam passar SQL Injection com erros
boleanos pois na assinatura só conseguem se defender quando encontrarm o
famoso UNION na URL, fica aqui um gancho para uma próxima pesquisa.

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 2
Introdução
Partindo do princípio que o leitor sabe do que se trata um SQL Injection, vamos
deixar claro as seguintes técnicas de maneira resumida:

Union Inband SQL Injection: Técnica onde ao injetar códigos SQL através de
uma aplicação, buscamos utilizar a função UNION em conjunto com
operadores boleanos para imprimir os dados solicitados na própria página da
aplicação.

Blind SQL Injection: Diversas formas diferentes, basicamente utilizando


comparações boleanas em conjunto com funções de string do banco de
dados, forçamos um verdadeiro ou falso na aplicação montando corretamente
ou não a página.

Error Based Blind SQL Injection: Este tipo ataque, o qual vou abordar neste
artigo, é exatamente o tipo que é pouco abordado em ferramentas, onde
muitas vezes não conseguimos trazer os dados utilizando o UNION, mas sim
forçar que algum erro seja impresso na página,.

Sendo assim existem algumas técnicas para extrair os dados destas mensagens.
Importante frisar que as demonstradas utilizando este tipo de ataque podem
facilmente ser adaptadas para o Blind SQL Injection e também o conhecido Time
Based SQL Injection, que se baseia no tempo em que é retornada determinada
consulta sem enxergar absolutamente nada na interface da aplicação.

Error Based Blind SQL Injection, como funciona?

MySQL
Vamos ver como poderíamos forçar um erro boleano no MySQL e extrair dados do
mesmo? O pesquisador Dmitry Evteev, apresentou uma técnica muito interessante
utilizando a função ExtractValue() do mysql versão 5.1 ou superior para demonstrar
como extrair dados. No console do MySQL podemos ver o que ocorre ao forçar
um erro utilizando a função apontada, repare que não é utilizado UNION, conforme
apresentado na imagem abaixo.

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 3
Esta abordagem é excelente, porém só conseguimos extrair os dados em
versões 5.1 ou mais recentes, a quantidade de dados extraída é limitada a 31
bytes e ainda alguns casos dependemos de um UNION o que é péssimo
frente a defesas como WAFs. Abaixo, um exemplo demonstrando a limitação
dos 31 bytes:

Para as limitações citadas existem diversas formas de amenizar estas


dificuldades, primeiro é preciso eliminar a dependência de versão. Abaixo um
insight feito por Qwazar que demonstra como é possível realizar esta técnica
aplicada para versões do MySQL 4, 5.0, 5.1 e superiores. Tal abordagem além
de mitigar a limitação da versão, amplia a quantidade de bytes extraídos
passando de 31 para 64 bytes e mantém a mesma lógica demonstrada
acima:

Porém nos traz mais uma desvantagem, neste caso devido a função rand(),
não existe 100% de chance de retorno, ou seja, as vezes o resultado pode vir
e as vezes não. Isso é péssimo, principalmente em casos onde escrevemos
um script para automatizar mas como sempre existe uma solução, abaixo
montei uma versão sem utilizar UNION e mantendo 99% de retorno do
resultado. Não sou DBA e nem mesmo ultra expert em SQL, assim acredito
que alguém com mais experiência possa ajudar a montar um consulta um
pouco menor, mas a syntax ficou assim (seguindo o mesmo exemplo acima):

select '1 ' and if(row(0,0)> (select + count(*), concat((select concat_ws


(0x3a,user,password) from mysql.user limit 1), 0x3a, floor(rand()*2)) x from
mysql.user group by x limit 1),1,if(row (0,0)> (select + count(*),concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1),0x3a,floor(rand()*2)) x from
mysql.user group by x limit 1),1,if(row(0,0)>(select + count(*),concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1),0x3a,floor(rand()*2)) x from
mysql.user group by x limit 1),1,if(row(0,0)>(select + count(*),concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1),0x3a,floor(rand()*2)) x from
mysql.user group by x limit 1),1,if(row(0,0)>(select + count(*),concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1),0x3a,floor(rand()*2)) x from
mysql.user group by x limit 1),1,if(row(0,0)>(select + count(*),concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1),0x3a,floor(rand()*2)) x from
mysql.user group by x limit 1),1,if(row(0,0)>(select + count(*), concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1), 0x3a, floor(rand()*2)) x
from mysql.user group by x limit 1),1,if(row(0,0)>(select + count(*),concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1),0x3a,floor(rand()*2)) x from
mysql.user group by x limit 1),1,if(row(0,0)>(select + count(*),concat((select
concat_ws(0x3a,user,password) from mysql.user limit 1), 0x3a,floor(rand()*2)) x
from mysql.user group by x limit 1),1,(select 1 and 1=1))))))))));

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 4
Veja que o resultado apresentado na imagem abaixo não vem truncado em 31
bytes e conseguimos ver a hash completamente, pois conseguimos trazer 64
bytes. Essa talvez não seja nem de perto a melhor solução e seria preciso
estudar um pouco mais algumas formas de melhorar os resultados.

Com a utilização desta técnica, em conjunto com outras funções específicas


do MySQL, é possível ampliar ainda mais extração de dados. Utilizando a
função compress() com a biblioteca zlib podemos compactar o resultado,
conforme apresentado abaixo.

Com isso, é possível diminuir o tamanho do resultado, o que nos testes que
realizei chegaram a 50% de taxa na compressão. Porém, a desvantagem é
que não seria possível utilizar técnica de Verdadeiro/Falso para extrair
caractere por caractere, pois o tipo de dado de retorno impede que isso
aconteça.

Porém a solução é simples, com a função hex() conseguimos transformar todo


o resultado em hexadecimal, e com esta saída não só podemos transportar
qualquer tipo de dados como ainda facilita a comparação Verdadeiro/Falso em
casos de Blind SQL Injection, e pode preparar a consulta para trazer um "tira"
única em hexadecimal de todos os dados solicitados independente do
tamanho.

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 5
A única desvantagem perceptível é que o processamento do banco de dados
será elevado em caso de automatização, mas isso pode ser ajustado através
de delays e timers. Na prática para extrair os dados, seria necessário
"caminhar" pela tira em hexadecimal, extraindo o resultado de 64 em 64 bytes
ou 32 em 32 bytes dependento da técnica, utilizando funções específicas para
lidar com strings. Na prática ficaria como na imagem abaixo:

E para potencializar tudo isto seria possível ainda utilizar "sub-querys" e trazer
muitos dados com pouquíssimos "hits" no Servidor Web, técnica que
demonstrei no OWASP AppSec Brasil do ano passado. Porém nem todas as
empresas utilizam MySQL e é preciso estar preparado para encontrar os mais
diversos tipos pela frente. Acima demonstrei como é feito com MySQL, porém
cada um possui sua limitação e também vantagens, o que nos traz novas
possibilidades a cada abordagem.

Aplicação em outras bases


Abaixo algumas idéias menos elaboradas do que esta feita no MySQL, porém
mostrando a pontinha do iceberg e um caminho para aplicar a mesma técnica
em diferentes cenários.

MSSQL Server / Sybase


Não é nada novo, a maioria das técnicas de SQL Injection demonstradas em
diversos forums, how-tos, listas de discussão, demonstram esta técnica que
força a conversão no tipo de dado para demonstrar o erro, no caso do
MSSQL é possível trazer aproximadamente 1024 bytes e no Sybase a técnica
é a mesma.

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 6
PostreSQL
Semelhante ao MSSQL/Sybase, porém utilizando a função CAST():

E seguindo a mesma linha de raciocínio aplicada no MySQL:

Oracle
Demonstrando esta técnica utilizando Oracle 9.0 ou superior, é possível extrair
até 214 bytes de uma mensagem de erro no Oracle usando a função XMLType
() em conjunto com outras para tratamento de string, seguindo lógica
semelhante a demonstrada com o MySQL, transforma a consulta em
hexadecimal, usa substr() para cortar a "tira" e trazer os resultados em partes.
E ainda podemos utilizar um truque para trazer versão em uma única linha,
isso porque o Oracle não possui offset ou limit:

SELECT banner FROM(SELECT banner,rownum rnum FROM v$version a)


WHERE rnum=1;

Usar XMLType() para mostrar a versão na saída de erro, para isto foi
necessário utilizar a função REPLACE() e substituir os espaços por
"_"(underline). Repare que a todo momento utilizo a função CHR() visando
evitar qualquer problema com caracteres de "'"(aspas simples).

SELECT XMLTYPE(CHR(60)||CHR(58)||(SELECT REPLACE((SELECT banner


FROM(SELECT banner,rownum rnum FROM v$version a)WHERE
rnum=1),CHR(32),CHR(95))FROM dual)||CHR(62))FROM dual;

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 7
Abaixo uma versão mais apurada para burlar os problemas de tipos de dados,
assim como replicar técnica semelhante à aplicada ao MySQL, utilizando
conversão para hexa e removendo a função REPLACE() não precisamos dela
já que estamos convertendo para hexadecimal.

SELECT UPPER(XMLTYPE(CHR(60)||CHR(58)||(SELECT RAWTOHEX((SELECT


banner FROM(SELECT banner,rownum rnum FROM v$version a)WHERE
rnum=1))FROM dual)||CHR(62)))FROM dual;

E finalmente a versão para ser utilizada em um Error Based Blind SQL Injection
utilizando operador lógico simulando um ataque.

SELECT 1 FROM dual WHERE 1=1 AND(1)=(SELECT UPPER(XMLTYPE(CHR


(60)||CHR(58)||(SELECT RAWTOHEX((SELECT banner FROM(SELECT
banner,rownum rnum FROM v$version a)WHERE rnum=1))FROM dual)||CHR
(62)))FROM dual);

Existem diferentes maneiras de se chegar ao mesmo objetivo, com um


conhecimento maior em determinado banco de dados e mais pesquisas, com
certeza é possível chegar a um resultado mais apurado, lembrando que em
todos os casos sempre pensei em não utilizar o UNION para tentar evitar um
possível match de assinatura com WAFs, assim como a utilização de função
para representar caracteres ascii na consulta e evitar enviá-los através de URL.

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 8
Conclusão
SQL Injection é uma técnica que por mais antiga que seja a cada dia evolui,
seja com lançamento de novas versões com novas funções, ou fazendo uma
releitura de técnicas já conhecidas e aprimorando sempre é possível
demonstrar algo novo.

E como sou um viciado convicto em Python nada melhor do que escrever


scripts em python usando threads somado a técnicas novas, é sempre
diversão garantida. Abaixo um screen shot do video demonstrando na prática
um script que aplica a técnica acima, atacando um banco de dados MySQL,
disponível na íntegra no canal da Conviso IT Security no YouTube, em http://
www.youtube.com/ConvisoITSecurity.

Sobre o Autor
Em um futuro próximo a Conviso IT Security estará disponibilizando em
formato Open Source algumas ferramentas criadas em laboratório, fique de
Ulisses Castro
olho! ;)

Atuando há muitos anos com IT Security,


Referências
iniciou suas atividades no Conviso
http://www.owasp.org/index.php/SQL_Injection
Security Labs em 2008, contribuindo no
http://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet desenvolvimento de novas ferramentas,
análise de malware, criação de exploits e
http://www.owasp.org/index.php/AppSec_Brasil_2009_%28pt-br
payloads para testes de segurança.
%29#tab=Arquivos_das_Apresenta.C3.A7.C3.B5es
Especializado em testes de intrusão e
https://forum.antichat.ru/printthread.php?t=119047&page=2&pp=10 análise de vulnerabilidades, trabalhou
como consultor e administrador de
http://ptresearch.blogspot.com/2010/01/methods-of-quick-exploitation-of-
blind_25.html sistemas operacionais Linux/Unix. É
palestrante em eventos internacionais
http://qwazar.ru/?p=7 como FISL e OWASP AppSec Brasil,
contribui e participa ativamente em
projetos com ênfase em software livre
como Debian, Metasploit e OWASP.

Conviso IT Security | Artigos | Velhas falhas, novas técnicas: Error Based SQL Injection 9

Вам также может понравиться