1. 퍼징 (Fuzzing) 이란

- 비정상적인 데이터를 계속 던져서 프로그램이 언제 무너지는지 관찰하는 자동화 테스트
- 소프트웨어에 무작위이거나 의도적으로 Invalid/Unexpected data를 지속적으로 주입하는 테스팅 기법
- 이렇게 해서 약간 예외 사항이나 알고리즘으로 치면 반례 같은 걸 찾아내는 과정이라고 보면 됨!
- 반례 찾아내는 걸 사람 머리도 물론 쓰지만 기계를 써서 (엣지 케이스라는 용어가 있다) 더 빨리 찾아낼 수 있는 게 장점...
- 엣지 케이스 :: 개발자 측면에선 최소/최대 경계값을 넣어서 테스트할 때 정상 동작하는지 확인하는 거!
+) 코너 케이스는 뭐냐 -> 엣지 케이스는 만족하는데 외부 변수나 특정 조건에서 문제가 발생하는 경우 ... 약간 집에선 잘 되던 스마트폰이 등산하면 잘 안 터지는 그런 쪽 문제라고 이해했다!
2. 퍼징 과정
1) Seed(초기값) 준비 :: 정상적인 파일을 초기 입력값으로 설정
2) Mutation(변이) :: Seed 파일 비트나 바이트를 무작위로 뒤집거나 쪼개서 이상한 데이터들을 막 생성해보는 거 (방법론으로는 비트 플립, 바이트 스왑 등이 있다고 함.)
3) Execution(실행) :: 변이된 데이터를 타겟 프로그램에 입력해 실행
4) Monitoring(감시) :: 어떻게 돌아가는지 실시간으로 모니터링하기
5) Logging(기록) :: 만약 크래시가 발생했다면 그 크래시를 유발한 정확한 입력 데이터를 별도로 저장 (추후 분석 및 디버깅에 사용)
- 방법 자체는 간단하다!
3. 퍼징의 종류
1) 블랙박스 퍼징 :: 내부 코드 모르는 상태로 입출력만 조작 (속도 빠름, 얕은 버그 탐지 유리)
2) 화이트박스 퍼징 :: 소스코드 완벽 분석 + Symbolic Execution 등을 사용 (속도 느림, 깊숙한 코드 라인까지 도달, 분석에 연산량 多)
3) 그레이박스 퍼징 :: 블 + 화 합친 것. 코드를 완벽 분석까지는 아니어도 프로그램이 실행되는 경로는 추적함.
ㄴ 이게 요즘 가장 많이 쓰이며, AFL (American Fuzzy Lop) 이 대표적.
4. 커버리지 기반 퍼징 (Coverage-Guided Fuzzing)
- 코드 커버리지 (Code Coverage) :: 프로그램 실행 시 전체 소스코드 중 어느 부분이 실행되었는지 나타내는 지표
- 어떻게 쓰는가 :: A 데이터 넣었을 때 이전에 안 가봤던 새로운 코드 경로가 나오면 그 A 데이터를 '좋은 시드'로 승격
- '좋은 시드'를 바탕으로 다시 변이가 진행 -> 프로그램의 로직을 점점 더 깊게 파고들게 됨!
5. Sanitizer
- 퍼징할 때 꼭 같이 세팅해야 하는 컴파일러 옵션 (GCC, Clang)
- 필요한 이유? : 메모리 꼬였는데 프로그램이 우연히 안 죽고 버티는 경우 (Silent Bug라고 한다네요) 퍼저가 이걸 취약점으로 인지 못하고 넘어갈 수 있기 때문!
- 하는 역할 : 프로그램 코드 사이사이에 들어가 있는데, 미세한 메모리 오류가 발생하면 그 즉시 강제로 크래시를 유발해 퍼저가 버그를 인식하게 만들어준다!
- 대표적으론 ASan (AddressSanitizer, BOF와 UAF 등의 메모리 침범 버그 특화)와 MSan(MemorySanitizer, 초기화되지 않은 메모리 사용 탐지)가 있다.
6. 퍼징에 대한 추가 팁?
1) Harness를 작성하자
- 하네스(Harness)는 무엇인가 :: 테스트하고 싶은 특정 함수만 따로 떼서 퍼저가 주는 입력값을 바로 먹일 수 있게 연결해주는 약간.. 바로가기 창 같은 걸 만들어주는 껍데기 코드 같은 것.
- 하네스를 어떻게 짜느냐에 따라 퍼징의 속도와 질이 달라진다고 함!
2) 시드를 최소화하자
- Corpus :: 퍼저에게 줄 시드 파일의 묶음
- 데이터를 최소화해서 작게 여러번 변이시키는 게 버그 확률도 낮고 속도도 빠르다!
- AFL 기준으론 두 가지 최소화 기법이 있는데,
a) cmin (Corpus Minimization) :: 중복되는 코드 경로를 실행하는 코드 파일을 걸러냄 (중복 제거)
b) tmin (TestCase Minimization) :: 시드 파일 자체의 크기만 줄이기 (경로는 그대로!)
3) 모든 크래시가 보안 취약점은 아님!
- 크래시가 엄청나게 쌓였을 때도 단순 버그와 치명적 보안 취약점을 구분해낼 줄 알아야 한다.
- Triage :: 크래시를 유발한 입력값을 디버거에 넣고 돌려보면서 메모리의 어느 주소에서 프로그램이 죽었는지, 분석자가 메모리 값(레지스터 같은)을 조작할 수 있는지 분석하는 것! (매우 중요)
'Security' 카테고리의 다른 글
| [최신 보안 사례 조사] (0) | 2026.03.23 |
|---|