Research / 시스템 / AFL++는 어떻게 동작하는가: 퍼징 루프와 구성 파일 정리
AFL++는 어떻게 동작하는가: 퍼징 루프와 구성 파일 정리
- 유형
- 연구 기록
- 분류
- 시스템
- 발행
- 2025. 12. 28.
- 분량
- 1분
- 관련 프로젝트
- sudo 취약점 탐색과 N-day 분석
- 종류
- 연구 기록
퍼징이 무엇인지, AFL++가 fork 서버와 공유 메모리로 커버리지를 모아 입력을 바꿔 가는 과정, 그리고 설치 폴더의 도구들이 각각 무슨 일을 하는지 정리했습니다.
sudo 프로젝트에서 퍼저를 고르기 전에 AFL++를 공부하면서 정리했습니다.
퍼징이란
퍼징은 프로그램에 무작위나 변형된 입력을 대량으로 넣어, 비정상 종료나 오류를 일으키는 입력을 찾는 자동화 테스트 기법입니다. 성능, 안정성, 보안 취약점을 찾는 데 씁니다.
AFL의 퍼징 루프
afl-fuzz가 대상 프로그램을 실행합니다.- 대상이 처음 실행될 때 파이프로
afl-fuzz와 통신하며 fork 서버를 띄웁니다. 이후로는 매번 처음부터 실행하지 않고fork()로 새 인스턴스를 만듭니다. - 표준 입력이나 파일로 입력이 전달됩니다.
- 실행 경로(커버리지)가 공유 메모리에 기록되고 프로그램이 끝납니다.
afl-fuzz가 공유 메모리의 기록을 보고, 새로운 경로를 연 입력을 바탕으로 다음 입력을 만듭니다.- 다시 2~5를 반복합니다.
fork 서버 덕분에 프로그램 초기화 비용을 매번 치르지 않아도 됩니다. 커버리지를 보기 때문에 새로운 코드 경로에 닿은 입력을 우선해서 변형합니다.
설치 폴더의 구성
직접 쓰는 도구
| 도구 | 하는 일 |
|---|---|
afl-fuzz |
퍼저 본체 |
afl-showmap |
대상의 커버리지 맵 출력 (계측이 잘 됐는지 확인) |
afl-cmin |
커버리지를 유지하면서 시드 개수 줄이기 |
afl-tmin |
크래시를 재현하는 입력을 작게 줄이기 |
afl-whatsup |
진행 상태 요약 (큐, 크래시, 속도) |
afl-plot |
통계 그래프 생성 |
afl-analyze |
입력의 어느 부분이 어떻게 처리되는지 분석 |
afl-gotcpu |
CPU 코어 상태 점검 |
계측 빌드용 컴파일러 래퍼
대상 프로그램을 AFL이 커버리지를 볼 수 있게 계측(instrumentation) 해서 빌드할 때 씁니다.
| 래퍼 | 설명 |
|---|---|
afl-cc |
범용 래퍼, 내부에서 clang/gcc 선택 |
afl-clang-fast, afl-clang-fast++ |
LLVM 패스 기반 계측 (주로 권장) |
afl-gcc, afl-gcc-fast |
gcc 기반 계측 |
소스가 없을 때: 모드
| 모드 | 방식 |
|---|---|
qemu_mode |
바이너리만 있을 때 QEMU로 계측 |
frida_mode |
Frida 기반 동적 계측 |
unicorn_mode |
Unicorn 기반 에뮬레이션 |
nyx_mode, coresight_mode |
가상화·하드웨어 기반 특수 모드 |
그 밖의 파일
- LLVM 계측 패스(
afl-llvm-pass.so,cmplog-*.so등): 컴파일할 때 커버리지 수집, 비교문 분해 같은 변환을 끼워 넣음 - 런타임 오브젝트(
afl-compiler-rt*.o): 계측된 바이너리에 함께 링크됨 libdislocator.so,libtokencap.so: 메모리 오류 탐지, 비교 토큰 수집 보조dictionaries/: 퍼징 힌트로 쓰는 토큰 사전custom_mutators/: 사용자 정의 변이기
sudo 프로젝트에서
팀에서는 이 구조를 바탕으로 sudoers 파서(visudo)를 계측 빌드해 퍼징했습니다. 빠른 퍼징용(LTO), ASan 탐지용, ASan+디버그 심볼(크래시 분석용) 세 가지로 빌드를 나눴고, sudoers 키워드로 사전을 만들어 넣었습니다.
정리
- 퍼저의 핵심은 커버리지를 보고 입력을 고르는 것입니다.
- fork 서버로 실행 비용을 줄이고, 공유 메모리로 실행 경로를 주고받습니다.
- 소스가 있으면 컴파일러 래퍼로 계측하고, 없으면 QEMU·Frida 모드를 씁니다.