
Fuzzer baseado em cobertura para aplicações Python
pip3 install frelatage
O Fuzzer Python que o mundo merece
Instalação | Como funciona | Recursos | Usar o Frelatage | Configuração
Frelatage é uma biblioteca de fuzzing Python baseada em cobertura que pode ser usada para fuzzar código Python. O desenvolvimento do Frelatage foi inspirado por vários outros fuzzers, incluindo AFL/AFL++, Atheris e PythonFuzz. O principal objetivo do projeto é aproveitar as melhores características desses fuzzers e reuni-las em uma nova ferramenta para fuzzar aplicações Python de forma eficiente.
AVISO: Este projeto está em estágio alfa e ainda pode causar muitos comportamentos inesperados. O Frelatage não deve ser usado em ambiente de produção neste momento.
pip3 install frelatage
Recomendado para desenvolvedores. Ele clona automaticamente o branch principal do repositório frelatage e instala a partir do código-fonte.
# Clona automaticamente o repositório Frelatage e instala o Frelatage a partir do código-fonte
bash <(wget -q https://raw.githubusercontent.com/Rog3rSm1th/Frelatage/main/scripts/autoinstall.sh -O -)
A ideia por trás do design do Frelatage é o uso de um algoritmo genético para gerar mutações que cubram o máximo de código possível. O funcionamento de um ciclo de fuzzing pode ser resumido aproximadamente com este diagrama:
graph TB
m1(Mutação 1) --> |input| function(Função fuzzada)
m2(Mutação 2) --> |input| function(Função fuzzada)
mplus(Mutação ...) --> |input| function(Função fuzzada)
mn(Mutação n) --> |input| function(Função fuzzada)
function --> generate_reports(Gerar relatórios)
generate_reports --> rank_reports(Classificar relatórios)
rank_reports --> select(Selecionar n melhores relatórios)
select --> |mutate| nm1(Mutação 1) & nm2(Mutação 2) & nmplus(Mutação ...) & nmn(Mutação n)
subgraph Mutações do ciclo
direction LR
m1
m2
mplus
mn
end
subgraph Mutações do próximo ciclo
direction LR
nm1
nm2
nmplus
nmn
end
style function fill:#5388e8,stroke:white,stroke-width:4px
O Frelatage permite fuzzar uma função passando um arquivo como entrada.
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.parse(data)
input = frelatage.Input(value="initial_value")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
O Frelatage oferece a possibilidade de fuzzar parâmetros de entrada do tipo arquivo. Para inicializar o valor desses arquivos, você deve criar arquivos na pasta de entrada (./in por padrão).
Se quisermos inicializar o valor de um arquivo usado para fuzzar, podemos fazer assim:
echo "initial value" > ./in/input.txt
E então executar o fuzzer:
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
input = frelatage.Input(file=True, value="input.txt")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
import frelatage
import my_vulnerable_library
input = frelatage.Input(file=True, value="input.txt")
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file(data)
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file_but_different(data)
# E assim por diante
# Isso fuzzará os métodos instrumentados um após o outro
frelatage.Fuzzer.fuzz_all()
Se você precisar carregar vários arquivos em um corpus de uma só vez (útil se você usar um corpus grande), você pode usar a função embutida do Frelatage load_corpus. Esta função retorna uma lista de inputs.
load_corpus(directory: str, file_extensions: list) -> list[Input]
./, ./images["jpeg", "gif"], ["pdf"]import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
my_vulnerable_library.load_file(data2)
# Carrega todos os arquivos no diretório ./in
corpus_1 = frelatage.load_corpus(directory="./")
# Carrega todos os arquivos .gif/.jpeg no subdiretório ./in/images
corpus_2 = frelatage.load_corpus(directory="./images", file_extension=["gif", "jpeg"])
f = frelatage.Fuzzer(MyFunctionFuzz, [corpus_1, corpus_2])
f.fuzz()
Você pode copiar um ou mais dicionários localizados aqui no diretório dedicado a dicionários (./dict por padrão).
Fuzzing diferencial é uma técnica popular de teste de software que tenta detectar bugs fornecendo a mesma entrada para múltiplas bibliotecas/programas e observando diferenças em seus comportamentos. Você encontrará um exemplo aqui de uso de fuzzing diferencial com o Frelatage usando as bibliotecas json e ujson.
Você pode encontrar mais exemplos de fuzzers e corpus no diretório de exemplos.
Cada relatório de crash é salvo na pasta de saída (./out por padrão), em uma pasta chamada: id:<crash ID>,err:<tipo de erro>,err_pos:<erro>,err_file:<arquivo de erro>.
O diretório do relatório está no seguinte formato:
├── out
│ ├── id:<crash ID>,err:<tipo de erro>,err_file:<arquivo de erro>,err_pos:<err_pos>
│ ├── message
│ ├── input
│ ├── 0
│ ├── <inputfile1>
│ ├── ...
│ ├── ...
Cada relatório de crash contém um arquivo message que contém a mensagem de erro.
Cada relatório de aumento de cobertura é salvo na pasta de cobertura (./cov por padrão), em uma pasta chamada: coverage_<ID>, e o diretório do relatório está no mesmo formato dos diretórios de relatórios de crash.
As entradas passadas para uma função são serializadas usando o módulo pickle antes de serem salvas no arquivo <pasta_relatorio>/input. Portanto, é necessário desserializá-lo para poder ler o conteúdo do arquivo. Esta ação pode ser realizada com o comando frelatage-report.
$ frelatage-report input
Existem duas maneiras de configurar o Frelatage:
Um exemplo de configuração:
export FRELATAGE_DICTIONARY_ENABLE=1 &&
export FRELATAGE_SAVE_NEW_COVERAGE=1 &&
export FRELATAGE_TIMEOUT_DELAY=2 &&
export FRELATAGE_INPUT_FILE_TMP_DIR="/tmp/frelatage" &&
export FRELATAGE_INPUT_MAX_LEN=4096 &&
export FRELATAGE_MAX_THREADS=8 &&
export FRELATAGE_MAX_STAGES=1000000 &&
export FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS=5000 &&
export FRELATAGE_INPUT_DIR="./in" &&
export FRELATAGE_DICTIONARY_DIR="./dict" &&
python3 fuzzer.py
import frelatage
def myfunction(input1_string, input2_int):
pass
input1 = frelatage.Input(value="initial_value")
input2 = frelatage.Input(value=2)
f = frelatage.Fuzzer(
# O método que você deseja fuzzar
method=myfunction,
# Corpus
corpus=[[input1], [input2]],
# Número de threads
threads_count=8,
# Exceções que serão consideradas
exceptions_whitelist=(OSError),
# Exceções que não serão consideradas
exceptions_blacklist=(),
# Diretório onde os relatórios de erro serão armazenados
output_directory="./out",
# Diretório onde os relatórios de aumento de cobertura serão armazenados
coverage_directory="./cov",
# Ativar ou desativar o modo silencioso
silent=False,
# Ativar ou desativar fuzzing infinito
infinite_fuzz=False
)
f.fuzz()
Lembre-se de que, assim como muitas outras tarefas computacionalmente intensivas, o fuzzing pode sobrecarregar seu hardware e o sistema operacional. Em particular:
Sua CPU ficará quente e precisará de resfriamento adequado. Na maioria dos casos, se o resfriamento for insuficiente ou parar de funcionar corretamente, as velocidades da CPU serão automaticamente reduzidas. No entanto, especialmente ao fuzzar em hardware menos adequado (laptops, smartphones, etc.), não é totalmente impossível que algo queime.
Os programas-alvo podem acabar consumindo erraticamente gigabytes de memória ou preenchendo o espaço em disco com arquivos inúteis. O Frelatage tenta impor limites básicos de memória, mas não pode evitar todos os possíveis acidentes. O resultado final é que você não deve fuzzar em sistemas onde a perspectiva de perda de dados não seja um risco aceitável.
O fuzzing envolve bilhões de leituras e gravações no sistema de arquivos. Em sistemas modernos, isso geralmente será fortemente armazenado em cache, resultando em E/S "física" bastante modesta - mas há muitos fatores que podem alterar essa equação. É sua responsabilidade monitorar possíveis problemas; com E/S muito pesada, a vida útil de muitos HDDs e SSDs pode ser reduzida.
Uma boa maneira de monitorar a E/S de disco no Linux é o comando 'iostat':
$ iostat -d 3 -x -k [...optional disk ID...]
Eu sou Rog3rSm1th, tenho 21 anos e sou um entusiasta francês de computação e cibersegurança. Gosto de desenvolver ferramentas (OSINT, Fuzzing...) e jogar CTFs/Wargames. Para saber mais sobre mim e meus projetos, basta clicar aqui.
➜ Se você quiser me contratar para um de seus projetos (Programação, cibersegurança...), entre em contato comigo em [email protected] e avaliaremos suas necessidades juntos.
Para qualquer comentário, sugestão, relatório de bug, ou se você encontrou um bug usando o Frelatage, pode entrar em contato comigo em [email protected] ou no twitter @Rog3rSm1th
| Variável de Ambiente | Descrição | Valores Possíveis | Valor Padrão |
|---|
| FRELATAGE_DICTIONARY_ENABLE | Ativar o uso de mutações baseadas em elementos do dicionário | 1 para ativar, 0 caso contrário | 1 |
| FRELATAGE_SAVE_NEW_COVERAGE | Salvar novas entradas de cobertura para reutilizá-las posteriormente | 1 para ativar, 0 caso contrário | 1 |
| FRELATAGE_TIMEOUT_DELAY | Atraso em segundos após o qual uma função retornará um TimeoutError | 1 - infinito | 2 |
| FRELATAGE_INPUT_FILE_TMP_DIR | Pasta temporária onde os arquivos de entrada são armazenados | caminho absoluto para uma pasta, por exemplo /tmp/custom_dir | /tmp/frelatage |
| FRELATAGE_INPUT_MAX_LEN | Tamanho máximo de uma variável de entrada em bytes | 4 - infinito | 4094 |
| FRELATAGE_MAX_THREADS | Número máximo de threads simultâneas | 8 - infinito | 8 |
| FRELATAGE_MAX_STAGES | Número máximo de estágios para uma função fuzzada | 1 - infinito | 1000000 |
| FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS | Número de ciclos sem novos caminhos encontrados após o qual passamos para o próximo estágio | 10 - infinito | 5000 |
| FRELATAGE_INPUT_DIR | Diretório contendo os arquivos de entrada iniciais. Deve ser um caminho relativo (até o caminho do arquivo de fuzzing) | caminho relativo para uma pasta, por exemplo ./in | ./in |
| FRELATAGE_DICTIONARY_DIR | Diretório padrão para dicionários. Deve ser um caminho relativo (até o caminho do arquivo de fuzzing) | caminho relativo para uma pasta, por exemplo ./dict | ./dict |
| FRELATAGE_DEBUG_MODE | Ativar o modo de depuração (mostrar o erro quando o Frelatage travar) | 1 para ativar, 0 caso contrário | 1 |