multi-byte comparison을 여러 개의 single-byte comparison으로 split
LAF-Intel 특징
=, <=연산자를 >, <, == comparison으로 단순화
signed integer comparison ⇒ sign-only comparison, unsigned comparison 변환
64,32,16bit의 모든 unsigned integer comparison을 8bit의 multiple comparison으로 분리
예) if(strcmp(input, “abcdefg”))는 입력 전달하기 어려우니까 if(input[0] == ‘a’ && input[1] == ‘b’ … )와 같이 재작성 → 복잡한 조건을 fuzzer가 해결할 수 있게 함
2) RedQueen
KAFL 기반
어려운 comparison과 checksum check를 우회하기 위한 가능성 탐색
Input-To-State (I2S) comparison
대부분의 roadblock이 Input-To-State comparison 타입
입력 내 operand 중 최소 하나에 direct dependency가 있는 comparison
예) if(input[15964] == 35) crash(); → 커버하기 힘든 branch edge에 입력이 바로 쓰일 때
colorization 단계에서 입력에서의 entropy를 증가시키고, 테스트 케이스의 coverage를 유지하면서 byte를 랜덤한 값으로 대체
Input-To-State comparison의 operand를 관찰하면 입력에서 guess해야 할 수 줄일 수 있음
comparison에서 추출한 I2S token을 대체해가며 입력을 mutation
4. Mutate Structured Inputs
fuzzer에서 유효하지 않는 입력을 생성하는 문제
→ input model 사용해서 해결. 생성된 입력의 공간 감소시키고 더 깊은 경로 탐색 가능하게 함
AFLSmart
PEACH를 black-box fuzzing 시 input model로 사용
input structure 고려해서 structured input 생성하도록 해서 invalid input 생성 피하고 deep path 탐색 가능하도록 함
A New Baseline for Fuzzing
1. Seed Scheduling
AFLFAST을 기존 모든 schedule 기법(fast, coe, explore(default), quad, lin, exploit)에 power schedule(mmopt, rare) 추가해서 확장함
AFLFast에 추가로 고려한 부분
queue에서 seed가 선택되는 시간
동일한 coverage 갖는 입력 수
동일한 coverage 갖는 테스트 케이스 수
mmopt schedule
새롭게 발견된 경로를 찾을 수 있도록 가장 새로운 seed에 score 더 줌
rare schedule
seed의 런타임 무시하고, 다른 seed에서 드물게 처리된 edge를 가진 seed에 집중
2. Mutators
AFL++은 기존 AFL의 deterministic하고 havoc pipeline보다 더 많은 mutator를 포함한다. mutator들을 조합해서 사용할 수 있다.
1) Custom Mutator API
AFL fork, patch 없이 AFL++ 내에서 API 사용해서 새로운 연구에 확장해서 쓰거나 특정 타겟 대상으로 취약점 찾는데 적용 가능
afl_custom_(de)init
initialize, deinitialize module 시 사용
afl_custom_queue_get
fuzzer가 현재 queue entry를 fuzzing 해야할지 결정하는 callback
afl_custom_fuzz
주어진 입력에 대해 custom mutation 수행. 추가 테스트 케이스 accept
afl_custom_havoc_mutation
입력에 대해 custom mutation 수행
havoc 단계에서 다른 mutation와 함께 쌓인다
afl_custom_havoc_mutation_probability
havoc에서 호출된 custom mutation 확률 리턴
디폴트는 6%
afl_custom_post_process
custom mutator에서 리턴한 데이터 포맷이 타겟 입력에 적합하지 않는 경우, checksum이나 크기 조정할 수 있도록 함
afl_custom_queue_new_entry
queue에 새로운 테스트 케이스가 추가될 때 호출
disk에 metadata 저장 시 유용
afl_custom_trim
AFL++에서 trimming routine 시 복잡한 포맷 구조를 파괴해서 입력 일부는 처리할 수 있고 입력 나머지 부분은 에러 발생할 수 있다. 이때 custom trimming routine 가능한 API 사용
각 trimming 연산 시 호출되어 현재 상태 기억하고 초기 입력 데이터 길이 초과하지 않는 trimmed input buffer 리턴
2) Input-To-State Mutator
AFL++은 REDQUEEN의 Input-To-State replacement 기반으로 mutator 구현하여 기존 구현에서 몇 가지 최적화 진행
최적화 부분
colorization
입력에서 각 바이트의 entropy 높이는데 효과적인 반면 fuzzer 속도 저하시킴
mutated 지역에서 coverage bitmap의 hash가 동일하게 유지되면서 기존보다 2배 slowdown하는 bound를 실행 속도가 유지하도록
comparison 시 probabilistic fuzzing
comparison bypass 충족할 입력을 생성하지 못할 수 있는데, 다음 시간에 이 comparison이 lower 확률로 fuzz 되도록 해서 unsolvable comparison에 너무 많은 시간 소요하지 않도록 함
3) MOpt Mutator
AFL++은 MOPT의 Core와 Pilot mode 구현했음
MOPT mutator는 Input-To-State mutator와 결합하여 사용 가능
3. Instrumentations
AFL++이 instrumentation 위해 지원하는 backends
NeverZero
AFL에 hitcount 최적화 위해 개발한 instrumentation에 사용되는 backend
bitmap entry 내 바이트 사용 시 edge execution 수가 overflow될 수 있는데 이게 발생하면 fuzzer가 일관성 없는 상태에 있을 수 있다.
overflow 해결 방법
NeverZero
bitmap entry에 carry flag 추가해서 overflow 피하는 방법
매우 효과적, coverage와 속도 관련해서 AFL 개선함
Saturated Counters
255 값에 counter 도달 시 counter를 freeze하는 방법
AFL 성능 저하함
1) LLVM
Context-sensitive Edge Coverage
edge coverage는 각 block의 ID와 callee의 ID를 XOR하는 것
code coverage에는 효과적인데 더 많은 collision 갖고 속도 더 느림
Ngram
N이 2부터 16 사이 숫자일 때, fuzzer는 destination block과 N-1 previous block 고려해서 edge를 logging
LLVM은 coverage feedback 위해 instrument를 패스하는데, AFL++에는 여기에 추가로 더 패스한다. 모든 LAF-INTEL pass가 포함되고, CmpLog pass 가능함
LLVM mode에서 instrument에 특정 source module 지정 가능
INSTRIM(LLVM에서 instrument 시 basic block 선택하는데 효율적)을 patch해서 구현
2) GCC
afl-gcc에 GCC 플러그인 추가 → deferred initialization, persistent mode
3) QEMU
AFL++은 AFL QEMU에 QEMU 3.1.1 사용해서 patch
→ emulation time에 instrumentation 추가 가능
→ helper 사용해서 AFL에서 불가능하게 한 block linking 가능하게 함
→ Thread Local Storage 사용
→ AddressSanitizer 기반 sanitization 지원
→ CompareCoverage: source-level fuzzing과 binary-level fuzzing 사이 gap 감소 위해 AFL++의 QEMU mode는 CompareCoverage 사용해서 compariso을 LAF-INTEL로 분리 가능
→ persistent mode: AFL++의 QEMU mode는 persistent mode 지원
⇒ 10x speedup 가능하고 이 모드 가능하면 추천함
4) Unicornafl
unicornafl: AFL++에서는 펌웨어와 같은 blob 바이너리 퍼징 위해 AFL에 unicorn engine 추가한 afl-unicorn을 확장한 것
original은 forkserver을 initial basic block에서 시작하고 garbage-collected python으로만 가능했는데, AFL++은 unicornafl에 C API, Rust, Python binding 추가해서 AFL++와 직접적으로 통신 가능하게 함
5) QBDI
LLVM 사용해서 compiler instrumentation으로 Android 라이브러리나 closed-source 라이브러리를 fuzzing
4. Platform Support
GNU/Linux, Android, iOS, macOS, FreeBSD, OpenBSD, NetBSD, Debian, Ubuntu, NixOS, Arch Linux, FreeBSD, Kali Linux 지원
AFL++의 QEMU mode의 Wine mode는 Win32 바이너리를 GNU/Linux에서 fuzzing 가능
5. Snapshot LKM
fork() → AFL의 state-restore 메커니즘, 타겟 수 많으면 성능 bottleneck 있음
AFL++은 Linux Kernel Module 통합해서 snapshot과 restore 처리하는 메커니즘 구현