BrFuzzer: Um Fuzzer Distribuído para Navegadores Gabriel Quadros, Ramon Pires, Marlos Marques Departamento de Ciências Exatas Universidade Estadual do Sudoeste da Bahia (UESB) Estrada do Bem Querer, Km 4 – Vitória da Conquista – BA - Brasil {gquadrossilva, pires.ramon, marlos.uesb}@gmail.com Abstract. Fuzz testing has gained popularity in recent years as an efficient method for finding vulnerabilities. Currently there are fuzzers for various file formats that are used to test browsers, but they were created to run in isolation and can not be used to divide the tasks among multiple browsers. Even the new distributed solutions still require the installation of traditional software testing. This paper presents a distributed fuzzer for browsers, which in addition to harness the computational power of multiple machines requires no software installation on the client to be tested. Resumo. O teste de fuzzing tem ganhado popularidade nos últimos anos como um método eficiente para encontrar vulnerabilidades. Atualmente existem fuzzers para diversos formatos de arquivo que são usados para testar navegadores, mas eles foram criados para executar de forma isolada, não podendo ser usados para dividir as tarefas de teste entre vários navegadores. Mesmo as novas soluções distribuídas ainda exigem a instalação tradicional do software de teste. Este artigo apresenta um fuzzer distribuído para navegadores, que além de aproveitar o poder computacional de várias máquinas não necessita da instalação de software no cliente a ser testado. 1. Introdução Fuzzing, prática realizada por desenvolvedores de software e pesquisadores de vulnerabilidades, consiste na busca das mais variadas vulnerabilidades através da inserção de dados na entrada do software a ser testado. Com o fuzzing, dados, válidos ou não, são enviados para qualquer tipo de mecanismo de entrada, visando a verificação do comportamento da aplicação com os dados submetidos e a detecção das vulnerabilidades. Em março de 2010, a Microsoft divulgou a utilização de uma nova técnica de fuzzing distribuído (Distributed Fuzzing Framework), patenteada pela própria empresa [Conger et al. 2010]. Diferentemente da técnica simplificada de fuzzing, onde apenas uma máquina era encarregada de executar as milhões de iterações, o fuzzing distribuído pode contar com a utilização de um grande número de computadores aos quais são distribuídas as operações de teste a serem realizadas. Segundo Tom Gallagher, líder do time de Testes de Segurança do Microsoft Office, foram encontrados mais de 1800 bugs no Office 2010 através da execução de milhões de testes de fuzzing. Os testes contaram com a utilização não apenas das 3 4 Workshop de Trabalhos de Iniciação Científica e de Graduação (WTICG) máquinas dos laboratórios, mas também dos demais computadores pertencentes à empresa que estivessem ociosos. Softwares clientes, instalados em sistemas de toda a rede da Microsoft, automaticamente entravam em ação quando os computadores estavam ociosos, principalmente em finais de semana, para executar testes de fuzzing. As vulnerabilidades em navegadores estão entre as mais pesquisadas do mercado atualmente, pois com o crescimento das aplicações Web, o navegador se tornou o alvo de frequentes ataques nos últimos anos [TippingPoint 2005, Miller 2007, Amini 2009]. A técnica utilizada anteriormente pela Microsoft exigia que um único computador fosse empregado na execução dos testes. Com a utilização do DFF é possível o desenvolvimento de um fuzzer distribuído que deve ser instalado em diferentes máquinas e possibilita a divisão dos testes entre vários navegadores. Com base na ideia do fuzzing distribuído este trabalho propõe o desenvolvimento de um fuzzer capaz de dividir os casos de teste entre vários navegadores à medida que os clientes acessam o(s) servidor(es) de teste. Desta forma, não é necessária a instalação de nenhum software no cliente a ser testado. Quando analisado durante a execução de um caso de teste, o código vulnerável provoca a quebra do processo do navegador. O tipo de vulnerabilidade comumente encontrada com esse tipo de fuzzer é de corrupção de memória, como buffer overflows [Dowd et al. 2006] e use-after-free [Dowd et al. 2006]. Este artigo é dividido em seis seções. Na seção 2, é apresentado um breve histórico dos fuzzers para navegadores. A seção 3 descreve o BrFuzzer, um fuzzer distribuído para navegadores, e uma visão geral do seu funcionamento. Nela também são apresentados os algoritmos desenvolvidos para a divisão dos testes e a troca de mensagens, os detalhes da implementação com algumas análises para situações especiais e são discutidas algumas decisões do projeto. A seção 4 apresenta os resultados obtidos. Finalmente, as conclusões do trabalho são mostradas na seção 5 e a discussão dos trabalhos futuros é feita na seção 6. 2. Fuzzers para Navegadores O primeiro fuzzer para navegador lançado publicamente foi o MangleMe [Zalewski 2004], que consistia em um script CGI que gerava conteúdo HTML mal-formado e na época foi capaz de encontrar vulnerabilidades nos principais navegadores. Em 2005, os pesquisadores HD Moore e Aviv Raff lançaram o fuzzer Hamachi [Moore and Raff 2005], que testava páginas HTML dinâmicas. O CSSDIE [Moore 2005] foi desenvolvido com o objetivo de fazer fuzzing em CSS (Cascade Style Sheets). Em 2006, HD Moore e outros pesquisadores lançaram outro fuzzer chamado DOM-Hanoi [Moore et al. 2006], para testar elementos DOM (Document Object Model). Esta pesquisa evoluiu para um projeto conhecido como Month of Browser Bugs [Moore et al. 2006], que divulgava um bug dos principais navegadores por dia. Ainda nesse ano, iniciou-se outra tendência que foi a pesquisa de vulnerabilidades em componentes ActiveX para o navegador Internet Explorer. Foram lançados os fuzzers COMRaider [iDefense Labs 2006] e AxMan [Moore 2006]. A partir de 2007, vários outros fuzzers capazes de testar diferentes formatos de arquivo surgiram. Em geral, qualquer fuzzer de formatos de arquivos reconhecidos pelos X Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais 5 navegadores pode ser aplicado para testá-los. Embora alguns desses fuzzers explorem formatos de arquivos diferentes e consigam encontrar vulnerabilidades, a maioria funciona no mesmo modelo: o pesquisador abre a página no navegador e essa página vai sendo carregada com novos códigos até que o navegador quebre. Esta tarefa é executada em um navegador de cada vez. Além disso, o monitoramento do navegador é deixado por conta do pesquisador, que deve iniciá-lo dentro de um depurador para ver as exceções geradas. 3. BrFuzzer: Um Fuzzer Distribuído para Navegadores Até o presente momento, nenhum dos fuzzers divulgados faz uso de técnicas de sistemas distribuídos para dividir os testes entre vários navegadores. Neste trabalho, foi desenvolvido um fuzzer chamado BrFuzzer, capaz de tratar as requisições de vários navegadores e distribuir os casos de teste previamente gerados e armazenados por um ou vários servidores entre eles. Ao contrário do fuzzer desenvolvido pela Microsoft, que faz uso de um software instalado em cada cliente para monitorar o programa que está sendo testado e identificar quando ocorre uma quebra, o BrFuzzer utiliza código JavaScript para requisitar e executar os casos de teste baixados do servidor. Esse código é executado a partir do momento em que o cliente abre a página de teste no servidor usando um navegador. Por rodar dentro do navegador, esse código JavaScript não tem nenhum privilégio para monitorar a execução do navegador e detectar exceções. Para lidar com essa limitação, foram desenvolvidos alguns algoritmos para regular a troca de mensagens entre os navegadores e o(s) servidor(es). A premissa desses algoritmos é que quando ocorre uma quebra do navegador, o usuário ou o próprio sistema operacional irá eventualmente reiniciar o processo e assim é possível testar o mesmo caso de teste por um determinado número de vezes. Esse ciclo teste-quebra-reinício se repete até que um número máximo de tentativas de execução de um caso de teste seja alcançado e ele seja classificado como possível vulnerabilidade. 3.1. Algoritmos para a Distribuição dos Testes Para a representação dos algoritmos a seguir, um caso de teste é definido como um par <ID, caso>. Algoritmo do Cliente O Algoritmo 1 apresenta os passos que devem ser executados no cliente, isto é, no navegador. Para isto, é feito o uso de cookies para identificar quais foram os casos de teste executados que causaram a quebra do navegador e, posteriormente, executar novamente esses mesmos casos de teste. O navegador do cliente começa a executar esse algoritmo a partir do momento em que acessa a página principal do(s) servidor(es). 6 Workshop de Trabalhos de Iniciação Científica e de Graduação (WTICG) Enquanto verdadeiro 1. Solicita novo caso de teste do servidor 2. Se recebeu um caso de teste então i. Armazena o ID do caso de teste em um local que persista após uma possível quebra do navegador ii. Executa o caso de teste iii. Envia o ID do caso de teste testado com sucesso para o servidor iv. Apaga o ID armazenado anteriormente Algoritmo 1. Algoritmo do cliente Algoritmo do Servidor O Algoritmo 2 apresenta os passos que devem ser executados no(s) servidor(es) para servir os casos de teste aos clientes. Já o Algoritmo 3, apresenta os passos para marcar os casos de teste que foram executados com sucesso e, consequentemente, não detectaram uma vulnerabilidade. No intuito de organizar a execução dos testes, é sugerida a implementação desse algoritmo em uma interface de comunicação (página Web) diferente da usada no Algoritmo 2. Enquanto verdadeiro 1. Recebe uma requisição do cliente 2. Se for recebido o ID de um caso de teste então i. Incrementa o número de tentativas de teste desse caso no banco de dados ii. Se o número de tentativas de teste desse caso for igual ao número de tentativas máximo definido, então a) Marca esse caso de teste como “vulnerabilidade encontrada” b) Identifica qual é o navegador do cliente e sua versão c) Envia um novo caso de teste ainda não testado nesse tipo de navegador iii. Senão a) 3. Envia o mesmo caso de teste para o cliente Senão i. Identifica qual é o navegador do cliente e sua versão ii. Envia um novo caso de teste ainda não testado nesse tipo de navegador Algoritmo 2. Algoritmo para distribuição dos testes X Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais 7 Enquanto verdadeiro 1. Recebe do cliente o ID de um caso de teste 2. Marca o caso de teste como “testado” Algoritmo 3. Algoritmo para marcação dos casos de teste que não descobriram vulnerabilidades 3.2. Implementação O Algoritmo 1 foi implementado com JavaScript, que está presente na página principal do(s) servidor(es). As requisições ao(s) servidor(es) foram implementadas com XMLHttpRequest, para que a página não precise ser recarregada toda vez que for solicitar um novo caso. Para armazenar os IDs dos casos de testes, foi usada a funcionalidade de cookies implementada pelos navegadores. Dessa forma, após uma quebra do processo, o ID do último caso de teste executado fica armazenado no cookie. Os Algoritmos 2 e 3, junto com alguns utilitários como um visualizador do estado dos casos de teste e um gerador de casos de teste, foram implementados como páginas Web na linguagem PHP, rodando sobre um servidor Apache. O banco de dados usado foi o PostgreSQL. Análise de Situações Especiais Foram feitas algumas análises dos algoritmos para algumas situações especiais: Se o navegador do usuário for finalizado durante a execução de um caso de teste, o servidor não incrementa o número de tentativas desse caso. Se o usuário reiniciar o navegador, podem ocorrer as seguintes situações: 1. O navegador abre a página principal do servidor normalmente e reinicia os testes. ◦ Caso o cookie não tenha sido deletado intencionalmente, ele será enviado para o servidor quando o cliente requisitar um novo caso de teste. O servidor incrementará o número de tentativas de teste desse caso e enviará o mesmo caso de teste para esse navegador. ◦ Caso o cookie tenha sido deletado intencionalmente, o servidor envia um novo caso de teste. 2. O navegador não abre a página principal do servidor automaticamente. Nada acontece no servidor e o número de tentativas de teste do caso não é incrementado. O navegador que tem seus cookies apagados quando reiniciado, vai contribuir para identificar apenas os testes que não encontram vulnerabilidades. Isto ocorre porque 8 Workshop de Trabalhos de Iniciação Científica e de Graduação (WTICG) os testes que causaram algum problema vão continuar como "não testados" e eventualmente serão enviados para serem testados em outros navegadores. 3.3. Geração dos Casos de Teste O tipo dos casos de teste deve ser escolhido de acordo com a funcionalidade do navegador que se deseja testar. Por exemplo, pode ser testado JavaScript, HTML, CSS, JPEG, GIF, etc. No caso do BrFuzzer, foi implementado um script para gerar trechos de código JavaScript. Para gerar os casos de teste, pode ser usado qualquer um dos métodos tradicionais de geração de casos de teste para fuzzing, que são: random, mutation-based e generation-based [Sutton et al. 2007]. Tais casos podem ser gerados previamente ou por demanda. É possível implementar um método de geração feedback-based tomando como base o número de tentativas feitas pelo navegador para executar um caso de teste. Quando um caso de teste começar a provocar várias tentativas, ele é usado como fonte para gerar outros casos de teste. 3.4. Organização do Servidor Com os algoritmos desenvolvidos, o servidor pode ser replicado em várias máquinas para resolver um possível problema de gargalo caso o número de clientes aumente consideravelmente. Uma das técnicas que podem ser utilizadas para gerenciar a alocação de conexões dos clientes para os servidores é o DNS Round-Robin [RFC 1794], na qual um único domínio está associado com múltiplos endereços IP. Ao utilizar esta técnica para a resolução do nome de um domínio na Web, o navegador recebe uma lista de endereços e normalmente escolhe o primeiro endereço da lista. Usando um servidor DNS capaz de circular as entradas da lista de endereços, consegue-se uma distribuição das requisições entre os vários servidores [Albitz and Liu 2006]. Outras técnicas de distribuição baseadas no gerenciamento de servidores replicados também podem ser usadas. 4. Resultados Obtidos O BrFuzzer foi testado com sucesso nos navegadores Internet Explorer 8, Firefox nas versões 3.5 e 3.6, Opera 10 e Chrome nas versões 5.0 e 6.0. Foram gerados vários casos de teste para o fuzzing dos interpretadores de JavaScript presentes nesses navegadores. Percebeu-se que o uso de navegadores do mesmo tipo e versão aumentava proporcionalmente a velocidade com que os testes eram realizados, dado que os casos de teste estavam sendo divididos entre eles (Figura 1). Um navegador não receberia um caso de teste já testado por outro navegador do mesmo tipo e versão. Além disso, ao acessar a página principal do(s) servidor(es) com navegadores de tipos e versões diferentes, os testes também foram executados com sucesso. X Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais 9 Figura 1. Gráfico do número de casos testados por segundo versus número de navegadores 5. Conclusões Com base nos resultados obtidos, conclui-se que o fuzzing distribuído para navegadores é um método eficiente de teste, pois aproveita o poder computacional dos navegadores dos usuários na Internet e não necessita de instalação de software adicional. O código JavaScript que busca os casos de teste no(s) servidor(es) e os executam pode ser embutido em qualquer página Web que queira contribuir com o teste. Ao visitar essas páginas, os usuários podem utilizar seus serviços normalmente, ao mesmo tempo em que os seus navegadores executam casos de teste em segundo plano. Vale ressaltar que um código malicioso que deseja descobrir vulnerabilidades em navegadores, pode utilizar esse método de fuzzing e incluir o código JavaScript em um grande número de aplicações Web que estejam vulneráveis. Por exemplo, através de estratégias do tipo Cross-site Scripting que permitem que códigos sejam inseridos numa página dinâmica [OWASP 2010]. 6. Trabalhos Futuros O próximo passo na pesquisa será a utilização dos recursos de armazenamento local que está sendo introduzido nos navegadores com a adição do suporte ao HTML 5 [W3C 2010], que permite que seja armazenado até 5 MB de dados no cliente. Com esse recurso, passa a ser viável a geração independente de casos de teste no próprio cliente, adicionando ao BrFuzzer um modo de funcionamento no qual o cliente não busca mais os casos de teste no servidor, mas gera seus próprios casos de teste, testa-os e envia para o servidor apenas aqueles que atingirem um determinado número de tentativas de execução. 10 Workshop de Trabalhos de Iniciação Científica e de Graduação (WTICG) Referências Conger, D. J. and Srinivasamurthy, K. and Cooper, R. S. (2010) “Distributed File Fuzzing”, http://www.faqs.org/patents/app/20080256340, June. Miller, C. (2007) “The Legitimate http://weis2007.econinfosec.org/papers/29.pdf, June. Vulnerability Market”, Amini, P. (2009) “Mostrame la guita! Adventures in Buying Vulnerabilities”, www.ekoparty.org/archive/2009/Mostrame_la_guita_.pdf, June. TippingPoint (2005) “Zero Day Initiative”, http://www.zerodayinitiative.com, June. Zalewski, M. (2004) “MangleMe”, http://freshmeat.net/projects/mangleme/, June. Moore H. D. and Raff, Aviv. (2005) http://metasploit.com/users/hdm/tools/hamachi/hamachi.html, June. Moore H. D. (2005) “CSSDIE”, die/cssdie.html, June. “Hamachi”, http://metasploit.com/users/hdm/tools/see-ess-ess- Moore H. D. et al. (2006) “DOM-Hanoi”, http://metasploit.com/users/hdm/tools/domhanoi/domhanoi.html, June. Moore H. D. et al. (2006) “Month of Browser Bugs”, http://browserfun.blogspot.com/, June. iDefense Labs. (2006) “COMRaider”, http://labs.idefense.com/software/fuzzing.php, June. Moore, H. D. (2006) “AxMan ActiveX Fuzzer”, http://digitaloffense.net/tools/axman/, June. Sutton, M. and Greene A. and Amini, P. (2007) “Fuzzing: Brute Force Vulnerability Discovery”, Addison-Wesley Professional, 1º edition. RFC 1794 (1995) “DNS Support for Load Balancing”, http://tools.ietf.org/html/rfc1794, June. W3C (2010) “Web Storage”, http://dev.w3.org/html5/webstorage/, June. OWASP (2010), “Cross-site site_Scripting_(XSS), June. Scripting”, http://www.owasp.org/index.php/Cross- Dowd M. and McDonald, J. and Schuh, J. (2006) “The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities”, Addison-Wesley Professional, 1º edition. Albitz P. and Liu C. (2006) “DNS and BIND”, O'Reilly, 5º edition, p. 250-253.