AFL++ em escala: por que o volume de falhas não é igual a vulnerabilidades
357 arquivos de falha. 2 bugs reais.
Isso não é uma falha de difusão. É uma falha de interpretação.
Em uma campanha recente de difusão do AFL++ visando o libarchive, executamos aproximadamente 8,5 bilhões de execuções em todas as fases de difusãogerou mais de mil arquivos de falhas e, por fim, reduziu-os a dois locais de falhas exclusivos por meio de triagem estruturada de falhas e desduplicação.
Este blog é um guia prático de engenharia para esse processo:
- Como projetar um fluxo de trabalho de difusão multifásico
- Como construir a matriz de instrumentação AFL++ correta
- Como otimizar a cobertura versus o rendimento
- Como implementar a triagem de falhas fuzzing em escala
- Como passar do volume de falhas para vulnerabilidades reais
Se o seu pipeline de difusão parar na contagem de falhas, você não estará medindo a segurança. Você está medindo o ruído.
Por que a difusão AFL++ produz altas contagens de falhas (e por que elas enganam)
Fuzzers modernos como AFL++ são extremamente bons na geração de resultados. Esse resultado, no entanto, não é diretamente equivalente a vulnerabilidades.
- Um único bug pode ser acionado por centenas de caminhos de entrada
- Cada caminho é registrado como uma falha separada
- Instâncias de difusão paralela amplificam a duplicação
É por isso:
- A contagem de falhas não é igual à contagem de vulnerabilidades
- Cobertura não é igual a profundidade de risco
- A velocidade de execução não é igual a descoberta significativa
Se você não implementar a desduplicação difusa e o clustering de causa raiz, seus resultados sempre serão inflacionados.
Seleção de alvo: Por que o libarchive funciona para difusão
libarchive é um alvo de difusão ideal porque:
- Ele analisa entradas de arquivo controladas pelo invasor
- Suporta vários formatos, como tar, zip, cpio, ISO e RAR
- Está escrito em C com lógica de análise complexa
- É amplamente implantado em sistemas de produção
Isso cria uma superfície de ataque realista onde entradas malformadas podem desencadear problemas de segurança de memória, desreferências nulas, inconsistências de analisador e condições de negação de serviço.
Matriz de construção AFL++: o erro de difusão mais comum
Antes de executar o AFL++, a etapa mais crítica é construir os binários corretos.
Configuração mínima viável do AFL++:
|
Binário |
Propósito |
|
Nativo (LTO) |
Taxa de transferência máxima |
|
ASAN |
Detecção de erro de memória |
|
CmpLog |
Desbloqueia caminhos baseados em comparação |
Por que o LTO é importante
A instrumentação LTO fornece visibilidade completa do programa, cobertura de borda livre de colisões e extração automática de dicionário.
Por que não executar apenas ASAN
ASAN introduz aproximadamente duas vezes a sobrecarga de tempo de execução. A execução de todas as instâncias com ASAN reduz o total de execuções e limita a descoberta.
Padrão correto
afl-fuzz -M main ... -w ./target_asan -- ./target_native
O binário nativo oferece velocidade. O binário ASAN valida a segurança da memória.
Por que o CmpLog é crítico
Muitos formatos dependem de comparações estritas e bytes mágicos. CmpLog permite que o AFL++ observe comparações de tempo de execução, extraia valores de operandos e os injete em mutações. Isso melhora significativamente a descoberta de caminhos em formatos estruturados.
Fase 1: validação CLI
Comece simples.
Em vez de construir chicotes imediatamente, difunda o alvo CLI:
afl-fuzz -i afl_inp -o afl_out/ -t 1000 -M FUZZ01_LTO \
-- ./lto_build/bin/bsdtar -xf @@ -C /tmp/out_bsdtar
Meta
- Validar o conjunto de ferramentas
- Garanta que as sementes atinjam caminhos de código reais
- Construa o corpus inicial
Resultado
- Sementes: 29 a 64, minimizadas para 42
- Duração: aproximadamente 7 horas
Conclusão importante
Não pule a validação. Configurações quebradas são mal dimensionadas.
Fase 2: Modo persistente em AFL++ (eliminando sobrecarga de fork)
A melhoria de desempenho mais importante no AFL++ é o modo persistente.
Por que isso importa
- Elimina a sobrecarga de fork e execução
- Usa entrada de memória compartilhada
- Melhora o rendimento em cinco a vinte vezes
Loop persistente mínimo
while (__AFL_LOOP(10000)) {
archive_read_open_memory(a, buf, len);
archive_read_next_header(...);
}
Regras de engenharia
- Estado sempre livre por iteração
- Limite as iterações do loop para evitar desvios de estado
- Coloque a inicialização após uma configuração cara
Resultados
- Aproximadamente 394 execuções por segundo por instância
- Corpus: 42 a 1.059 entradas
- Falhas: 0
Estratégia de paralelização: evitando trabalho de mutação redundante
Depois que o modo persistente remove a sobrecarga de execução, o próximo gargalo é quão efetivamente múltiplas instâncias de difusão exploram o espaço de entrada.
A principal escolha do lado secundário é o cronograma de energia (-p), e a regra é simples: não dê a cada secundário o mesmo.
Se todas as instâncias executarem programações idênticas, elas convergirão rapidamente para mutações semelhantes, levando a trabalho redundante e baixa utilização da CPU. Programações mistas garantem que cada instância explore uma região diferente do espaço de busca.
Distribuição de cronograma recomendada
- -p explorar → avança em direção a caminhos de cobertura novos e inexplorados
- -p explorar → concentra-se em insumos já próximos a estados interessantes
- -p raro → prioriza bordas raramente atingidas, eficaz para descoberta de casos extremos
Esta diversidade garante que os fuzzers paralelos sejam complementares em vez de duplicados.
Integração MOpt (direcionada, não universal)
Uma instância secundária deve ser executada com -L 0 para habilitar MOpt (Otimização do Operador de Mutação).
MOpt usa um modelo de otimização de enxame de partículas para:
- Acompanhe quais operadores de mutação produzem nova cobertura
- Ajustar dinamicamente as probabilidades de mutação para estratégias eficazes
Ele tem melhor desempenho como única instância adaptativa dentro de uma configuração heterogênea, não como um substituto para todos os fuzzers.
Conclusão importante
O modo persistente desbloqueia o rendimento.
A estratégia paralela determina se esse rendimento se traduz em crescimento significativo da cobertura ou ciclos desperdiçados.
Esta fase cria cobertura, não bugs.
Fase 3: Otimização de rendimento em AFL++ (maximizando exec/seg)
Assim que a cobertura se estabilizar, mude o objetivo.
Mudança de estratégia
- Reduza o trabalho por iteração
- Concentre-se em caminhos de falha de alta probabilidade
- Reutilize o corpus da Fase 2
Resultados
- 5,2 bilhões de execuções
- Aproximadamente 7.400 execuções por segundo
- 270 acidentes
Entendimento
A cobertura estabilizou enquanto o volume de acidentes aumentou. Isso indica duplicação em vez de descoberta de novos bugs.
Conclusão importante
Mais velocidade aumenta o volume de falhas, não necessariamente o número de novas vulnerabilidades.
Fase 4: Expandindo a cobertura de difusão para superfícies mais profundas do analisador
Para encontrar novos bugs, você deve alterar a superfície que está sendo testada.
Novas superfícies exploradas
- Iteradores ACL
- Travessia de região esparsa
- Estruturas de metadados
- Gráficos de objetos aninhados
Por que isso é importante
Essas áreas introduzem estruturas vinculadas a ponteiros, incompatibilidades de contagem e caminhos de iteração profundos.
Entradas malformadas aqui acionam:
- Desreferências nulas
- Iteração ilimitada
- Inconsistências aritméticas em regiões vinculadas.
Estratégia de execução: Diversificação do cronograma de energia
Expandir a superfície por si só é insuficiente. Sem um estratégia de mutação diversificadafuzzers paralelos convergem em caminhos e ciclos de desperdício semelhantes.
A Fase 4 apresenta separação explícita do cronograma de energia entre os secundários, garantindo que cada instância explora uma região distinta do espaço de entrada.
Estratégia de instância
- -p explorar → impulsiona a descoberta de novos caminhos de cobertura
- -p explorar → intensifica mutações perto de entradas interessantes conhecidas
- -L 0 (MOpt) → otimiza dinamicamente os operadores de mutação com base na eficácia observada
- -eu 2 (laf-intel) → reescreve comparações multibyte em verificações de bytes, melhorando a capacidade de resolução de restrições
- -c (CmpLog) → captura comparações de tempo de execução para orientar a mutação de entrada
Por que isso é importante
Mesmo equipamento + mesmo cronograma = trabalho redundante
Mesmo equipamento + horários diferentes = exploração paralela
Cada instância:
- Muda as entradas de maneira diferente
- Prioriza diferentes caminhos de execução
- Contribui com cobertura não sobreposta
Isto é o que permite que superfícies mais profundas na verdade introduzir novos bugsem vez de duplicando classes de falha anteriores.
Resultados
- A cobertura aumentou de aproximadamente 8,6 mil para 9,7 mil bordas
- Falhas: 896
- Trava: 2.414
Interpretação
Novas classes de travamento surgiram de uma lógica de analisador mais profunda, e não do aumento do volume de iteração.
O aumento acentuado nos travamentos reflete:
- Travessia de caminhos de iteradores aninhados
- Comportamento quadrático em estruturas malformadas
Criticamente, estes foram novas regiões de execuçãonão extensões de bugs de caminho de gravação anteriores.
Conclusão importante
Novos bugs vêm de:
Novas superfícies × Diversas estratégias de mutação
Não há mais iterações.
A expansão superficial sem diversidade de cronograma produz redundância, não descoberta.
Evolução do Corpus em todo o fluxo de trabalho difuso
|
Estágio |
Arquivos |
|
Sementes iniciais |
29 |
|
Pós CLI |
42 |
|
Pós Fase 2 |
1.059 |
|
Pós Fase 4 |
6.779 |
|
Mesclagem final |
36.310 |
O crescimento do corpus bruto é exponencial. A cobertura exclusiva não é.
Funil de colisão: do ruído ao sinal

Este funil representa o conceito mais importante na difusão em escala. Grandes volumes de falhas se transformam em um número muito pequeno de problemas reais.
Triagem de falhas: de 1.166 falhas a 2 bugs
Fuzzing produz entradas com falha. Não produz vulnerabilidades diretamente.
Pipeline de triagem
- Reproduza falhas usando ASAN
- Desduplicar saídas AFL++
- Gerar relatórios CASR
- Cluster por similaridade de rastreamento de pilha
Resultados
|
Estágio |
Contar |
|
Falhas brutas |
1.166 (aproximadamente) |
|
Reproduzível |
357 |
|
Locais de falhas exclusivos |
2 |
Causa raiz
Ambos os bugs estavam localizados em:
arquivo_entry_sparse.c
Visão do operador
Se uma campanha difusa produz centenas de falhas, mais de noventa por cento são normalmente duplicadas.
Conclusão importante
A triagem de falhas é onde a difusão se transforma em engenharia.
Configuração mínima de AFL++
Para replicar este fluxo de trabalho, comece com:
- Um binário nativo com LTO
- Um binário ASAN
- Chicote de modo persistente
- CmpLog ativado
- Uma instância mestre e duas instâncias secundárias
Expanda somente após a estabilidade ser confirmada.
Erros comuns em fuzzing pipelines
- Executando apenas compilações ASAN
- Ignorando o modo persistente
- Ignorando CmpLog
- Tratar a contagem de falhas como contagem de vulnerabilidades
- Não implementar um pipeline de triagem
O que isso significa para pipelines AppSec modernos
Este não é apenas um problema confuso. Reflete uma falha mais ampla nos pipelines de segurança de aplicativos:
- Muita produção
- Pouca interpretação
- Conexão fraca com risco real
As ferramentas de segurança identificam onde os sistemas falham. A engenharia determina o que realmente importa.
Esta é a mudança em direção à segurança com reconhecimento de execução:
- Concentre-se no comportamento do tempo de execução
- Recolher descobertas duplicadas
- Priorize as causas raízes
Do ruído ao sinal
A difusão produz ruído. A engenharia produz sinal.
A diferença é tudo.
Um fluxo de trabalho de difusão bem executado deve:
- Gere grandes volumes de dados
- Recolher esses dados de forma agressiva
- Produza um conjunto pequeno e acionável de bugs
Se o seu pipeline terminar com 357 falhas, ele estará incompleto.
Se terminar em 2 causas raízes, é útil.
Perguntas frequentes
O que é difusão AFL++?
AFL++ é uma estrutura de difusão guiada por cobertura usada para descobrir vulnerabilidades por meio da mutação de entradas e da observação do comportamento do programa.
Por que os fuzzers geram muitos travamentos?
Os Fuzzers geram muitos travamentos porque o mesmo bug pode ser acionado por vários caminhos de execução.
Como você desduplica falhas de difusão?
Você pode desduplicar falhas de difusão usando ferramentas de cluster, como CASR, que agrupam falhas com base em rastreamentos de pilha.
O que é o modo persistente no AFL++?
Um modo persistente no AFL++ é aquele em que o alvo é executado em loop, evitando reinicializações de processos e melhorando o desempenho.
Por que a contagem de falhas é enganosa?
A contagem de falhas é enganosa porque reflete a frequência de detecção em vez de vulnerabilidades únicas.




.png?w=330&resize=330,220&ssl=1)
