Pular para o conteúdo

Snapchange - Cheatsheet de Fuzzing Baseado em Snapshot com KVM

Snapchange - Cheatsheet de Fuzzing Baseado em Snapshot com KVM

Snapchange (by AWS) é um framework Rust para fuzzing baseado em snapshot. Fuzzers tradicionais reiniciam o alvo para cada input, que é fatalmente lento quando o código interessante fica atrás de setup caro — um aperto de rede, um login, uma config parseada, um banco de dados carregado. Snapchange em vez disso pega um snapshot de memória do alvo no momento exato de interesse, então restaura aquele estado e muta a partir daí usando KVM, alcançando taxas de iteração muito altas contra código que fuzzing convencional dificilmente consegue alcançar.

Fuzzing baseado em snapshot requer KVM e root. Execute em um ambiente isolado.

Requisitos

RequisitoNota
Linux com KVM/dev/kvm acessível
Toolchain RustSnapchange é uma biblioteca Rust
QEMUUsado para tirar o snapshot
RootPara KVM e acesso de memória

O Modelo

FaseO que acontece
1. SnapshotExecute o alvo em QEMU até o ponto de interesse, dump memória + registros
2. HarnessEscreva um fuzzer Rust descrevendo onde input vai e quando parar
3. FuzzSnapchange restaura o snapshot por iteração e muta o input
4. TriagemCrashes são registrados com o estado exato de registrador/memória

O insight crítico: todo o custo de setup é pago uma vez, no snapshot. Cada iteração subsequente começa a partir daquele estado em microsegundos.

Tirando um Snapshot

# Conceitual: execute o alvo sob QEMU, quebre na função de interesse,
# então dump memória física e estado de registrador
./snapchange/qemu_snapshot/take_snapshot.sh --target ./my-server
ArtefatoContém
fuzzvm.physmemMemória física do guest completa
fuzzvm.qemuregsEstado do registrador de CPU
*.symbolsTabela de símbolo para cobertura/breakpoints
vmlinux / binaryPara simbolização

Escrevendo um Fuzzer

// Forma conceitual — veja exemplos Snapchange para código completo
impl Fuzzer for MyFuzzer {
    type Input = Vec<u8>;
    const START_ADDRESS: u64 = 0x555555555000;
    const MAX_INPUT_LENGTH: usize = 1024;

    fn set_input(&mut self, input: &Self::Input, fuzzvm: &mut FuzzVm<Self>) -> Result<()> {
        // Escreve o input mutado na memória do guest onde o alvo o lê
        fuzzvm.write_bytes_dirty(VirtAddr(BUFFER_ADDR), CR3, input)?;
        Ok(())
    }

    fn reset_breakpoints(&self) -> Option<&[AddressLookup]> { /* condições de parada */ }
    fn crash_breakpoints(&self) -> Option<&[AddressLookup]> { /* sites de crash */ }
}
ElementoPropósito
START_ADDRESSOnde execução retoma cada iteração
set_inputInjeta o fuzz input na memória do guest
reset_breakpointsOnde uma iteração termina normalmente
crash_breakpointsEndereços que indicam um crash (ex. panic, abort)

Executando

ComandoDescrição
cargo run -r -- fuzz -c 8Fuzz com 8 cores
cargo run -r -- project translateSimboliza/inspeciona o snapshot
cargo run -r -- coverageGera cobertura do corpus
cargo run -r -- trace <input>Single-step rastreia um input
cargo run -r -- minimize <input>Encolhe um input travando

Por que Fuzzing de Snapshot Vence Aqui

Característica do alvoFuzzing convencionalSnapchange
Startup caroPaga a cada iteraçãoPaga uma vez
Requer auth/handshakeDifícil de alcançarSnapshot passou disso
Protocolo statefulDifícilSnapshot mid-session
Código de kernel/hypervisorMuito difícilNatural fit

Cobertura & Triagem

CapacidadeNota
Cobertura de breakpointCobertura via breakpoints em blocos básicos
Dedup de crashAgrupado por endereço/estado de falha
Rastreamentos single-stepRastreamento de instrução completo para um dado input
Inspeção de memóriaLeia memória do guest no tempo de crash

Snapchange vs Outros Fuzzers

AspectoSnapchangeAFL++LibAFL (Nyx)syzkaller
ExecuçãoRestauração de snapshot KVMFork de processoSnapshot (Nyx)VM + syscalls
Custo de setupPago uma vezA cada iteraçãoUma vezPor VM
AlvoQualquer coisa em um snapshot de VMBinários userspaceConfigurávelKernels SO
EsforçoAlto (escreva um fuzzer Rust)BaixoAltoMédio

Compare com LibAFL (que também oferece backends de snapshot) e syzkaller para fuzzing de syscall de kernel.

Recursos