# Fuzzing

Fuzzing é uma técnica de teste de software, frequentemente automatizada ou semi automatizada, que envolve fornecer dados aleatórios, inválidos ou inesperados como entradas para programas de computador. O programa é então monitorado, analisando exceções, como erros em tempo de execução. Fuzzing é uma técnica comumente utilizada para testar problemas de segurança em softwares ou sistemas computacionais.
Existem duas formas de programas fuzzing: baseados em mutação e baseados em geração, que podem ser empregadas como testes de caixa branca, cinza e preta. Os maiores alvos para este tipo de teste são os formatos de arquivos e protocolos de rede, mas qualquer tipo de entrada de programa pode ser criada. Entradas interessantes incluem, variáveis de ambiente, eventos de teclado e mouse, e chamadas de API. Até itens normalmente não considerados "entrada" podem ser gerados, ou fuzificados, como o conteúdo de banco de dados, memória compartilhada, ou trocas de contextos entre threads.
Com o objetivo de avaliar o nível de segurança, entradas que ultrapassam as barreiras de segurança são consideradas as mais interessantes. Por exemplo, é mais importante fuzificar código que realiza o upload de um arquivo por qualquer usuário que fuzificar código que analisa arquivo de configuração que é acessível a apenas um usuário privilegiado.

## História

O termo "fuzzing" ou "fuzz" tem origem num projeto de classe da Universidade de Wisconsin-Madison, ensinado pelo Professor Barton Miller, em 1988. O projeto foi nomeado "Programa de Confiabilidade Utilitário em Sistemas Operacionais - O Gerador Fuzzing". O projeto desenvolveu um fuzzer baseado em linha de comando para testar a confiabilidade de programas Unix, bombardeando-os com dados aleatórios até eles apresentarem um erro em tempo de execução. O teste foi repetido em 1995, expandido para incluir teste em ferramentas baseadas em GUI, protocolos de rede e APIs de bibliotecas do sistema. Trabalhos seguintes incluíram testes em sistemas baseados em linha de comando e em GUI, tanto em Windows como em Mac OS X.
Um dos primeiros exemplos de fuzzing surgiu antes de 1983. "The Monkey" ("O Macaco") foi uma aplicação Macintosh desenvolvida por Steve Capps. Ele alimentava programas Mac com eventos aleatórios e foi usado para detetar bugs no MacPaint.

## Usos

Fuzzing é frequentemente empregado utilizando a metodologia de testes de caixa preta em grandes projetos de software onde existe orçamento que permite a criação para ferramentas de teste. Fuzzing é uma das técnicas que oferece uma boa relação custo-benefício.
A técnica pode apenas disponibilizar uma amostra aleatória do comportamento do sistema, e em muitos casos passar por um teste de fuzz pode apenas demonstrar que uma parte do software pode tratar exceções sem apresentar erro durante execução, do que se comportar devidamente. Isto significa que fuzzing indica apenas uma garantia da qualidade geral do sistema, não se trata de uma técnica para detetar bugs, e não substitui teste exaustivos nem métodos formais.
Como uma medida grosseira de confiabilidade, fuzzing pode sugerir que partes do programa podem precisar de atenção especial, na forma de auditoria de código, aplicação de análise estática, e reescrita parcial.

### Tipos de Bugs

Fuzzing também pode ser utilizado para detetar bugs e leaks de memória (quando acoplado a um debugger de memória). A metodologia é útil em grandes aplicações, onde qualquer bug que afete a segurança do uso da memória possa gerar um erro durante a execução do programa.
Já que fuzzing frequentemente gera entradas inválida, estas são usadas para testar rotinas que tratam de possíveis erros, que são importantes para o software que não estam controlando estas entradas. Fuzzing poder ser pensado como uma forma de automatizar testes negativos.
Fuzzing também pode encontrar alguns tipos de bugs relacionados a corretude do programa. Por exemplo, a técnica pode ser utilizada para detetar bugs relacionados a formas de serialização incorretas gerando mensagens sempre que um serializador do programa emita algo que o mesmo parser do programa rejeita. A técnica também pode encontrar diferenças não intencionais entre duas versões de um programa ou entre duas implementações da mesma especificação.

## Técnicas

Programas fuzzing são subdivididos entre duas categorias. Fuzificadores baseados em mutação modificam amostram de dados existentes para criar outros testes de dados enquanto que fuzificadores baseados em geração criam novos testes de dados baseados em modelos da entrada.
A forma mais simples de fuzzing é enviar um fluxo de bits aleatórios para o software, seja como opções em linhas de comando, pacotes de protocolos aleatoriamente modificados, ou como eventos. Esta técnica de entradas aleatórias ainda é considerada uma ferramenta poderosa para encontrar bugs em aplicações baseadas em linhas de comando, protocolos de rede, e aplicações baseadas em GUI e em serviços. Outra técnica comum que é fácil de implementar é modificar entradas existentes modificando bits movendo blocos aleatoriamente pelo arquivo. No entanto, os melhores fuzificadores possuem entendimento detalhado do formato ou protocolo sendo testado.
O entendimento pode ser baseado em uma especificação. Um fuzzer baseado em especificação envolve escrever toda a especificação na ferramenta, e então usando técnicas de geração de testes baseadas em modelo seguem a especificação adicionando dados, mensagens, e sequências. Este "fuzzing inteligente" é também conhecido como um teste de robustez, teste de sintaxe, teste de gramática, e injeção de falhas. Testes para protocolos podem ser criados heuristicamente de exemplos usando uma ferramenta como Sequitur. Estes fuzificadores podem gerar casos de teste do zero, ou eles podem modificar exemplos de conjuntos de testes ou testes reais. Eles podem se concentrar em entradas válidas ou inválidas.
Existem duas limitações de fuzzing baseados em protocolo baseados em implementações de protocolo de especificações publicadas: 1) O teste não pode proceder até que a especificação esteja relativamente madura, já que a especificação é um pre-requisito para escrever o fuzzer, e 2) muitos protocolos úteis são proprietários, ou involvem extensões proprietárias para protocolos publicados. Se o fuzzing é baseado em apenas especificações publicadas, a cobertura dos casos para novos ou proprietários protocolos será limitada ou inexistente.
Fuzzing pode ser combinado com outras técnicas de teste. Fuzzing de caixa branca usa execução simbólica e solução restrita. Fuzzing evolucionário dá um feedback da cobertura do código, efetivamente automatizando a abordagem de testes exploratórios.

## Reprodução e Isolamento

Redução de casos de testes é o processo de selecionar um número de casos de teste a partir de um conjunto de casos de teste maior. A redução pode ser feita manualmente, ou usando ferramentas de software, e usualmente involve uma estratégia "dividir para conquistar" onde partes do testes são removidos um por um até que restem apenas os casos de testes essenciais para a cobertura do programa.
Os softwares de fuzzing frequentemente registram as entradas de dados que produz, usualmente antes de aplicar ao software. Se o computador apresenta um erro durante sua execução, o teste é preservado. Se o fluxo de dados gerados é pseudo-aleatório, o valor semente pode ser armazenado para reproduzir a tentativa de fuzzing. Se um bug é detetado, algum software de fuzzing irá auxiliar na construção do caso de teste, que pode ser utilizado para debugging.

## Diferenças entre o fuzzing caixa preta de aplicações Web e Desktop

O fuzzing black-box de aplicações Web tem uma grande desvantagem em relação ao de aplicações Desktop, que é a impossibilidade de acesso ao código server-side da aplicação. Esse código nunca é disponibilizado para os usuários, exceto quando alguma vulnerabilidade descoberta na aplicação permite o download de qualquer arquivo do servidor. O acesso ao código da aplicação favoreceria a utilização de várias técnicas de teste de software, que vão da análise da cobertura do código obtida com a execução de um caso de teste, até algoritmos para a geração de casos de teste com maior probabilidade de detetar vulnerabilidades.
Já no fuzzing black-box de aplicações Desktop, é sempre possível analisar o código nativo ou bytecode do executável, o que permite uma conversão para o fuzzing white-box dada a capacidade de se realizar análises sobre esse tipo de código. Com isso, consegue-se aplicar várias técnicas para obter uma melhor cobertura do código da aplicação, como a Execução Simbólica, que têm sido bastante utilizada em pesquisas acadêmicas [11-13] e em algumas ferramentas comerciais ou Open Source [2].

## Vantagens e Desvantagens

O principal problema com a técnica de fuzzing para encontrar falhas em programas é que ela normalmente encontra apenas falhas simples. A complexidade computacional do problema de teste de software é de ordem exponencial (O(c^n), c > 1) e todo fuzificador utiliza cortes na sua busca para encontrar algo interessante no tempo que seja razoável para uma pessoa. Um fuzificador pode ter uma cobertura de código ruim; por exemplo, se a entrada inclui um checksum que não é propriamente atualizado para combinar com outras mudanças aleatórias, apenas o código de validação do checksum será verificado. Ferramentas de cobertura de código são frequentemente usadas para estimar o quão "bem" um fuzificador funciona, mas estes não são meios precisos para avaliar a qualidade do fuzificador. Pode se esperar que cada fuzificador encontre um conjunto diferente de bugs.
Por outro lado, bugs encontrados utilizando fuzzing são normalmente severos, bugs que realmente podem ser explorados por um atacante real. A técnica de fuzzing tornou-se bastante conhecida para assegurar a segurança em softwares, mas também passou a ser explorada por atacantes para detetar falhas em programas. Nesse problema é que se encontra uma grande vantagem da auditoria de código binário ou fonte, a injeção de falhas, que pode ser difícil de se detetar por softwares que realizam fuzzing.
A aleatoriedade da entradas usadas é frequentemente vista como uma desvantagem, assim como seguir valores limites com entradas aleatórias é muito improvável.
Fuzzing é bastante importante na área de segurança de software e na criação de software seguro porque costuma encontrar defeitos que humanos não conseguiriam detetar com facilidade, e também não conseguiriam encontrar casos de testes manualmente que pudessem detetar a falha.