Tutorial de teste Fuzz (Fuzzing)

⚡ Resumo Inteligente

O teste de fuzzing alimenta um programa com dados inválidos, inesperados ou aleatórios e monitora falhas, travamentos e erros de memória, expondo defeitos de segurança que testes funcionais automatizados quase nunca detectam por conta própria.

  • 🔘 Origem: Barton Miller cunhou o termo na Universidade de Wisconsin-Madison, e as primeiras execuções de fuzzing em 1989 causaram a falha de aproximadamente um terço dos utilitários UNIX testados.
  • ☑️ Loop de seis etapas: Identifique o alvo, identifique as entradas, gere dados fuzzificados, execute, monitore o comportamento e, em seguida, registre cada defeito que aparecer.
  • Três estratégias de geração: Os fuzzers de mutação alteram amostras válidas, os fuzzers de geração constroem entradas a partir de um modelo e os fuzzers de protocolo funcionam a partir de uma especificação.
  • 🧪 O feedback da cobertura alterou o cenário: Os mecanismos de busca modernos armazenam qualquer entrada que chegue ao novo código, o que permite encontrar erros muito mais complexos do que dados puramente aleatórios.
  • 🛠️ As ferramentas evoluíram: Peach Fuzzer e WebScarab estão arquivados, enquanto AFL++, libFuzzer, OSS-Fuzz, boofuzz ​​e OWASP ZAP são as opções que recebem manutenção.
  • ⚙️ Limites conhecidos: O fuzzing encontra falhas, não problemas de lógica, portanto complementa, em vez de substituir, a revisão de código e os testes de penetração.

Tutorial de teste Fuzz (Fuzzing)

O que é teste Fuzz?

Teste de Fuzzing ou Fuzzing Fuzzing é uma técnica de teste de software que consiste em inserir dados inválidos ou aleatórios, chamados de FUZZ, em um sistema de software para descobrir erros de codificação e falhas de segurança. O objetivo do fuzzing é inserir dados usando técnicas automatizadas ou semiautomatizadas e testar o sistema em busca de várias exceções, como travamentos do sistema ou falhas no código interno.

O teste de fuzzing foi originalmente desenvolvido por Barton Miller na Universidade de Wisconsin-Madison, que cunhou o termo depois que o ruído na linha de um modem causou a falha dos programas que ele estava usando. Seus alunos executaram os primeiros fuzzers em 1989 e descobriram que aproximadamente um terço dos utilitários UNIX que eles testaram travavam ou congelavam. O teste de fuzzing é uma ferramenta para testar a compatibilidade entre software e tecnologia. teste de software técnica, e é um tipo de Teste de Segurança.

O diagrama abaixo mostra o ciclo básico de fuzzing, onde os dados gerados são enviados para a aplicação em teste e a resposta é observada.

Fluxo de trabalho de teste de fuzzing: um fuzzer gera entradas malformadas e as envia para a aplicação em teste.

Por que fazer testes Fuzz?

O fuzzing conquista seu espaço em um plano de testes porque explora entradas para as quais ninguém pensou em escrever um caso de teste. Os principais motivos pelos quais as equipes o adotam estão listados abaixo.

  • Os testes de fuzzing geralmente encontram as falhas e defeitos de segurança mais graves, pois uma falha é uma evidência direta de um caminho de entrada não tratado.
  • O teste de fuzzing oferece um resultado mais eficaz quando usado com Preto Box Testes, testes beta e outros métodos de depuração.
  • O teste de fuzzing é usado para verificar a vulnerabilidade de softwares e é uma técnica de teste muito econômica, pois as entradas são geradas em vez de escritas manualmente.
  • O teste de fuzzing é uma das técnicas de teste de caixa preta. O fuzzing também é um dos métodos mais comuns usados ​​por hackers para encontrar vulnerabilidades em um sistema, portanto, executá-lo primeiro elimina a rota de acesso mais fácil para um atacante.

Tipos de Testes de Fuzzing

Os fuzzers geralmente são agrupados de acordo com o quanto eles sabem sobre o programa que estão atacando. Quanto mais o fuzzer souber, mais profundamente ele poderá penetrar no código.

Formato O que o fuzzer sabe Uso típico
desfoque de caixa preta Nada sobre os componentes internos; ele apenas vê entradas e saídas. Execuções rápidas de testes de fumaça em um endpoint binário ou em um endpoint ativo.
desfoque de caixa branca Código-fonte completo, frequentemente combinado com execução simbólica para resolver ramificações de difícil acesso. Análise aprofundada de um componente cuja fonte está disponível.
desfoque de caixa cinza Sem revisão do código-fonte, mas com feedback em tempo de execução, como por exemplo, quais ramificações de código uma entrada alcançou. O padrão para mecanismos modernos como AFL++ e libFuzzer.

Uma segunda divisão, mais antiga, separa distorção burra da fuzzing inteligenteUm fuzzer burro inverte bits sem qualquer noção do formato de entrada, portanto, a maior parte dos seus dados é rejeitada pelo primeiro analisador sintático que encontra. Um fuzzer inteligente entende checksums, campos de comprimento e estrutura da mensagem, de modo que suas entradas sobrevivem à validação e chegam à lógica subjacente. Fuzzing guiado por cobertura É o refinamento da caixa cinza que tornou o fuzzing popular: o mecanismo instrumenta o binário, mantém qualquer entrada que alcance um novo ramo e modifica os sobreviventes, de modo que o conjunto de dados evolua constantemente em direção a um código inexplorado, em vez de recomeçar a partir de ruído aleatório.

Como fazer testes Fuzz

As etapas para o teste de fuzzing incluem as etapas básicas de teste:

Etapa 1) Identifique o sistema de destino — escolha o endpoint binário, biblioteca, serviço ou protocolo que será atacado e confirme que você tem permissão para testá-lo.

Etapa 2) Identificar entradas — listar todos os pontos de entrada dos quais o alvo lê: arquivos, argumentos de linha de comando, variáveis ​​de ambiente, pacotes de rede, campos de formulário e payloads de API.

Etapa 3) Gerar dados difusos — produzir entradas malformadas através da mutação de amostras válidas, da geração delas a partir de um modelo do formato ou da combinação de ambos.

Etapa 4) Execute o teste usando dados fuzzy — execute o alvo em relação às entradas geradas, idealmente em um loop que reinicie o processo automaticamente após cada falha.

Etapa 5) Monitore o comportamento do sistema — fique atento a travamentos, congelamentos, falhas de asserção, uso descontrolado de memória e relatórios de sanitização, em vez de verificar apenas a saída impressa.

Etapa 6) Registrar defeitos — Salve a entrada exata que desencadeou cada falha, reduza-a ao menor caso reproduzível e arquive-a junto com a pilha de chamadas. trace anexado.

Exemplos de Fuzzers

Os fuzzers também são classificados pela forma como constroem sua entrada, e as três abordagens abaixo são as que você encontrará com mais frequência.

  • Fuzzers baseados em mutação Alterar amostras de dados existentes para criar novos dados de teste. Esta é uma abordagem muito simples e direta: começa com amostras válidas de um protocolo e continua a manipular cada byte ou arquivo.
  • Fuzzers baseados em geração Eles definem novos dados com base na entrada do modelo. Em seguida, começam a gerar entradas do zero, com base na especificação.
  • Fuzzers baseados em protocolo Dependem de um conhecimento detalhado do formato do protocolo que está sendo testado, e esse conhecimento vem da especificação. Envolve escrever uma matriz da especificação na ferramenta e, em seguida, usar uma técnica de geração de testes baseada em modelo para percorrer a especificação e adicionar irregularidades no conteúdo dos dados, na sequência e assim por diante. Isso também é conhecido como teste de sintaxe, teste de gramática ou teste de robustez. Um fuzzer pode gerar casos de teste a partir de um existente ou pode usar entradas válidas ou inválidas.

Existem duas limitações da difusão baseada em protocolo:

  1. O teste não pode prosseguir até que a especificação esteja madura.
  2. Muitos protocolos úteis são uma extensão de protocolos publicados. Se o teste fuzz for baseado em especificações publicadas, Cobertura de teste para novos protocolos será limitado.

A forma mais simples de técnica de fuzzing consiste em enviar entradas aleatórias para o software, seja como pacotes de protocolo ou como um evento. Essa técnica de envio de entradas aleatórias é muito poderosa para encontrar bugs em diversos aplicativos e serviços. Outras técnicas também estão disponíveis e são muito fáceis de implementar. Para implementá-las, basta alterar as entradas existentes, o que pode ser feito simplesmente trocando seus bits.

Tipos de bugs detectados pelo Fuzz Testing

Como o fuzzing avalia uma execução com base em como o programa se comporta, em vez de um valor esperado, os defeitos que ele revela se agrupam em três famílias.

  • Falhas de asserção e vazamentos de memória: Essa metodologia é amplamente utilizada em grandes aplicações onde os bugs afetam a segurança da memória, o que representa uma vulnerabilidade grave. Buffer Overflows, uso após liberação e leituras fora dos limites aparecem aqui.
  • Entrada inválida: Em testes de fuzzing, os fuzzers são usados ​​para gerar entradas inválidas, que são utilizadas para testar rotinas de tratamento de erros. Isso é importante para softwares que não controlam suas entradas. O fuzzing simples pode ser visto como uma forma de automatizar... teste negativo.
  • Erros de correção: O fuzzing também pode ser usado para detectar alguns tipos de bugs de "correção", como um banco de dados corrompido ou resultados de pesquisa ruins. O fuzzing diferencial, que alimenta a mesma entrada para duas implementações e compara as respostas, é a maneira usual de detectar esses bugs.

Ferramentas de teste Fuzz

Ferramentas utilizadas em segurança web podem ser amplamente usadas em testes de fuzzing, tais como: Burp Suite e Peach Fuzzer. Vários dos nomes clássicos abaixo agora estão arquivados, portanto, seu status atual é indicado ao lado de cada entrada.

  • Fuzzer de pêssegoO Peach Fuzzer oferece uma cobertura de segurança mais robusta do que um scanner. Outras ferramentas de teste só conseguem procurar ameaças conhecidas, enquanto o Peach Fuzzer permite que os usuários encontrem ameaças conhecidas e desconhecidas. A Peach Tech foi adquirida pela GitLab e a Community Edition v3 não recebe mais manutenção; a versão sucessora com manutenção é a [nome da versão com manutenção]. GitLab Protocol Fuzzer Community Edition.
  • Proxy de picoSPIKE Proxy é uma ferramenta de nível profissional que busca vulnerabilidades em aplicações web. Ele abrange os recursos básicos, como: SQL Injeção e cross-site scripting em um ambiente completamente aberto. Python infraestrutura, e estava disponível para Linux e WindowsNão recebe manutenção há muitos anos e está incluído aqui para fins de contexto histórico.
  • Escaravelho WebO WebScarab foi escrito em Java e, portanto, é portátil para diversas plataformas. O framework WebScarab se comunica usando os protocolos HTTP e HTTPS e funciona como um proxy interceptador: ele permite que o operador revise e modifique as requisições criadas pelo navegador antes que o servidor as receba, e revise e atualize as respostas geradas pelo servidor antes que o navegador as receba. Qualquer vulnerabilidade encontrada pelo WebScarab é adicionada à sua lista de problemas relatados. O repositório foi arquivado em abril de 2024 e agora é somente leitura.
  • OWASP WSFuzerWSFuzzer é um programa licenciado sob a GPL, escrito em Python que tinha como alvo serviços web e, em sua última versão, era baseado em HTTP. Serviços SOAP eram o principal alvo. Foi lançado como parte do WebScarab e descontinuado juntamente com ele; OWASP ZAP e seu complemento Fuzzer são os substitutos recomendados.
  • Alternativas mantidas: AFL++ e libFuzzer são os mecanismos padrão guiados por cobertura para código nativo, OSS Fuzz executa-os continuamente e gratuitamente para projetos de código aberto, e boofuzz aborda a análise de fuzzing de protocolos de rede em PythonUma lista mais completa está compilada no guia para ferramentas de teste de segurança.

Melhores práticas para testes de fuzzing

Um fuzzer apontado para um alvo e deixado em execução raramente encontra muita coisa. As práticas abaixo diferenciam uma campanha que produz defeitos registrados de uma que apenas consome tempo de CPU.

  • Comece com um bom conjunto de sementes. Colete entradas reais e válidas que o aplicativo já aceite. Modificar um arquivo genuíno permite que o código de análise seja executado muito mais rapidamente do que modificar bytes aleatórios.
  • Crie um chicote de fios pequeno e rápido. O ponto de entrada deve executar uma única ação por vez, evitar chamadas de rede e gravações em disco, e retornar rapidamente, pois a taxa de transferência é medida em execuções por segundo.
  • Ligue os desinfetantes. A corrupção silenciosa de memória geralmente não causa travamentos. AddressSanitizer e UndefinedBehaviorSanitizer a convertem em uma falha imediata e diagnosticável.
  • Corra por muito tempo e corra continuamente. Uma execução de uma ou duas horas detecta bugs superficiais; caminhos mais complexos geralmente exigem muitas horas, e é por isso que o fuzzing deve ser realizado em uma tarefa de CI noturna, em vez de uma sessão manual.
  • Minimize e elimine duplicatas de cada falha. Reduza a entrada com falha à sua menor forma e agrupe as falhas por pilha. tracCaso contrário, um único bug pode se transformar em centenas de chamados.
  • Mantenha um corpus de regressão. Adicione cada entrada que reproduz o problema a um conjunto permanente que é executado em todas as compilações, para que um defeito corrigido não possa retornar silenciosamente.
  • Defina o alvo legalmente. Realizar testes de compatibilidade (fuzzing) em um serviço de terceiros em produção sem autorização por escrito é indistinguível de um ataque.

Vantagens do teste Fuzz

Utilizado com expectativas realistas, o fuzzing agrega valor que outras técnicas têm dificuldade em igualar.

  • O teste de fuzzing melhora os testes de segurança de software.
  • Os bugs encontrados em testes de fuzzing às vezes são graves e frequentemente são os mesmos usados ​​por atacantes, incluindo travamentos, vazamentos de memória e exceções não tratadas.
  • Se algum bug não for detectado pelos testadores devido a limitações de tempo e recursos, esse bug também será encontrado nos testes de fuzzing.
  • Os dados de entrada são gerados por uma máquina, portanto a cobertura continua a crescer durante a noite sem esforço manual adicional.

Desvantagens do teste Fuzz

As mesmas propriedades que tornam o fuzzing barato também limitam o que ele pode provar.

  • Os testes de fuzzing, por si só, não conseguem fornecer uma visão completa de uma ameaça de segurança geral ou de um conjunto de bugs.
  • Os testes de fuzzing são menos eficazes no combate a ameaças de segurança que não causam falhas no programa, como alguns vírus, worms e cavalos de Troia.
  • O teste de fuzzing só consegue detectar falhas ou ameaças relativamente simples e não leva em consideração a lógica de negócios.
  • Para funcionar eficazmente, requer um tempo de máquina significativo.
  • Definir uma condição de valor limite com entradas aleatórias é muito problemático, embora os testadores agora resolvam grande parte disso com algoritmos determinísticos orientados por entradas do usuário.

Teste de Fuzzing vs. Teste de Penetração

Ambas as atividades procuram falhas de segurança, mas respondem a perguntas diferentes e raramente são intercambiáveis.

Critérios Teste Fuzz Teste de Penetração
Conduzido por Um mecanismo automatizado que gera entrada malformada Um testador habilidoso raciocinando sobre o sistema.
Procura por Travamentos, congelamentos e falhas de segurança de memória. Vulnerabilidades exploráveis, incluindo falhas de lógica e configuração.
Profundidade Ampla cobertura de entrada, raciocínio superficial. Cobertura restrita, raciocínio profundo
saída Reproduzindo entradas e pilha traces Um relatório de descobertas com caminhos de exploração e classificações de risco.
Melhor momento Continuamente, no pipeline de construção Periodicamente, contra uma versão candidata a lançamento

Na prática, os dois se retroalimentam: o fuzzing elimina as falhas baratas e automatizáveis, permitindo que as horas limitadas de um testador sejam dedicadas às falhas que apenas um humano detectará.

Perguntas Frequentes

Um corpus inicial é o conjunto inicial de entradas válidas que o fuzzer modifica. Arquivos pequenos, variados e reais funcionam melhor, porque cada um deles já passa pelo analisador sintático, permitindo que o mecanismo gaste seu orçamento em código mais complexo em vez da primeira verificação de validade.

Um "fusz harness" é uma pequena função que entrega um buffer de bytes fuzzificados ao código em teste. Ele deve evitar alterações no estado global, escritas em arquivos e chamadas de rede, para que o mecanismo possa executá-lo milhares de vezes por segundo.

Uma ou duas horas expõem falhas superficiais. Campanhas sérias duram muitas horas ou dias, porque a cobertura aumenta gradualmente. Um platô na curva de cobertura, e não a leitura do relógio, é o sinal honesto de que uma campanha parou de dar resultados.

O AddressSanitizer detecta estouros de buffer e uso após liberação (use-after-free), o UndefinedBehaviorSanitizer detecta uso indevido de inteiros e ponteiros, e o MemorySanitizer detecta leituras de memória não inicializada. Sem eles, muitas corrupções passam silenciosamente e o fuzzer não reporta nenhuma falha.

Reproduza o problema, minimize a entrada para o menor caso de falha possível e agrupe-o com falhas que compartilham a mesma pilha de erros. trace, em seguida, abra um chamado pelo método usual. processo de gerenciamento de defeitos e adicione a entrada a um corpus de regressão.

Compartilham a aleatoriedade, mas não a intenção. Teste com macacos O fuzzing lança ações arbitrárias do usuário em uma interface em execução, enquanto o fuzzing tem como alvo um analisador de entrada específico e mede a cobertura de código, podendo assim se direcionar para trechos de código que as entradas anteriores nunca alcançaram.

Os modelos de linguagem estão sendo usados ​​para elaborar estruturas para APIs não fuzzificadas, sintetizar entradas iniciais para formatos exóticos e agrupar e resumir relatórios de falhas. O mecanismo ainda fornece o feedback de cobertura; o modelo elimina principalmente o trabalho de configuração manual.

Copiloto do GitHub É possível criar um ponto de entrada para o libFuzzer, um arquivo de compilação e um gerador de sementes a partir de uma assinatura de API existente. RevAnalise o resultado com atenção, pois um sistema que ignora erros silenciosamente não reportará nenhuma falha.

Resuma esta postagem com: