American Fuzzy lop 가지고 놀기

American Fuzzy lop 가지고 놀기

1. 개요

American Fuzzy lop 이하 AFL은 Michal Zalewski 가 개발한 퍼저 입니다.

AFL은 오픈소스 SW의 취약점을 찾는데 아주 훌륭한 선택이라고 생각합니다.

그 이유는 아래와 같습니다.

먼저 AFL은 단순히 파일을 뮤테이션 해서 소프트웨어로 전달하는 퍼저와는 조금 다릅니다.

일반적인 파일 퍼저는 아래와 같은 동작을 수행합니다.

위 파일은 일반적인 PNG 파일 입니다. 해당 파일을 랜덤 뮤테이션한 결과는 아래와 같습니다.

몇몇 바이트들이 원래의 값에서 임의의 값으로 변경되었습니다.

파일 퍼저는 위처럼 소스파일을 변조해서 대상 SW의 입력으로 전달 합니다.

그 과정에서 소프트웨어의 크래시를 찾는것이 일반적인 파일 퍼저의 역할입니다.

AFL은 위처럼 소스파일을 변경하는 기능도 수행하지만, 퍼징 대상 SW의 소스코드를 변경합니다.

AFL 퍼저는 대상 SW를 컴파일 할때 AFL의 기능을 사용 할 수 있도록 코드를 추가합니다.

간단한 예제를 통해서 살펴 보도록 하겠습니다.

#include <stdio.h>

int main(int argc, char* argv[]){

  if( argc !=2 ) exit(-1);

  printf("Hello\n");

  return 0;
}

위 소스코드를 컴파일한 결과는 아래와 같습니다.

AFL을 이용해서 컴파일한 결과는 아래와 같습니다.

원본 바이너리와 비교하면 총 3곳에 afl 코드가 추가된것을 확인 할 수 있습니다.

이렇게 컴파일 시 원하는 코드를 추가 했을 때 얻는 장점은 뭘까요?

위처럼 소스코드를 컴파일 할 떄 원하는 코드를 추가해서 이점을 얻는 방식을

compile-time instrumentation 이라 합니다.

이러한 방식은 기존의 DBI(Dynamic Binary Instrumentation) 보다 퍼징 작업에 이점이 있습니다.

예를 들어 Pin은 위의 경우와 다르게 소스코드가 없어도 비슷한 작업을 수행할 수 있지만,

pin이 동작하기 위한 pinVM을 추가로 실행해야 합니다.

마치 호스트기반 가상화와 Docker의 차이점 처럼 과정을 생략해서 퍼포먼스의 이득을 얻게됩니다.

위 바이너리 코드에서 추가된 어셈블리 명령어들은 퍼징을 수행하는 동안

프로그램 전체를 살펴보는데 큰 도움을 주게 됩니다.

프로그램의 실행 흐름이 변경 될 수 있는 각종 분기점에 위처럼 로깅을 하게되면

퍼저가 커버할 수 있는 범위가 얼마나 되는지 알 수 있습니다.

아래 이미지는 AFL로 gzip 바이너리를 5시간 퍼징한 결과입니다.

크고 아름답네요. level6의 depth까지 도달한것이 확인됩니다.

AFL은 이처럼 컴파일 시점에 코드를 삽입해서 퍼저의 커버 가능 범위를 넓히고,

새로운 분기에 도달 했을 때 사용된 인풋을 위주로 사용해서 입력값이 프로그램에

미치는 영향을 높힐 수 있습니다.

이렇게 효율적으로 퍼징을 하자 라는 개념은 이전부터 SmartFuzzing 이라는 이름으로 불렸습니다.

관련자료는 beist 님의 2012 CodeEngn Everyone has his or her own fuzzer 를 살펴보면 좋을듯 합니다.

해당 슬라이드에는 퍼징과 관련된 전반적인 내용들이 모두 포함되어 있습니다.

2. Do it

AFL 퍼저는 linux와 osx 등 unix 기반 OS에서 사용 가능합니다.

개인적으로는 다양한 오픈소스를 빌드하기 용이한 우분투 환경을 추천합니다.

우분투 15.04 64bit를 기준으로 작성 하겠습니다.

2.1 AFl 설치

먼저 afl을 아래처럼 설치 할 수 있습니다.

wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar zxvf afl-latest.tgz
cd afl-1.85b
make
sudo make install
2.2 퍼징 대상 빌드

퍼징 대상 소프트웨어는 giflib으로 선정 했습니다.

다음으로 giflib 다운로드 합니다.

wget "http://cznic.dl.sourceforge.net/project/giflib/giflib-5.1.1.tar.gz" -O "./giflib.tar.gz"
tar zxvf giflib
cd giflib-5.1.1

giflib을 빌드합니다.

export AFL_HARDEN=1 //메모리 관련 에러를 좀 더 잘 찾을 수 있도록 하는 옵션
CC=afl-gcc CXX=afl-g++ ./configure --disable-shared --build=i686-pc-linux-gnu "CFLAGS=-m32" "CXXFLAGS=-m32" "LDFLAGS=-m32"
make

64비트 머신에서 32비트 바이너리를 정상적으로 빌드 가능합니다.

빌드가 마무리되면 바이너리를 테스트 해봅니다.

cd util
./giftext ../tests/wedge.gif
2.3 퍼징 테스트

giftext 바이너리를 대상으로 afl 퍼징을 진행 하겠습니다.

mkdir giffuzz
cd giffuzz
cp ../giflib-5.1.1/util/giftext .
mkdir in dict

기본 디렉토리 구성을 마무리 했습니다.

이제 gif 이미지를 in 디렉토리에 넣고 dict 폴더에는 gif의 사전파일을 복사합니다.

cp ~/afl-1.85b/testcases/images/gif/not_kitty.gif in
cp ~/afl-1.85b/testcases/_extras/gif.dict dict

afl-fuzz를 실행합니다.

afl-fuzz -i in -o out -x dict/gif.dict ./giftext

input 파일의 사이즈를 작게 만들수록 빠르게 실행 할 수 있습니다.

afl은 크래시가 발생한 경우 크래시 파일을 minimize 하는 기능을 제공합니다.


cdpython@linux64:~/giffuzz$ afl-tmin -i out/crashes/id\:000000* -o crash1 ./giftext
afl-tmin 1.85b by <lcamtuf@google.com>

[+] Read 198 bytes from 'out/crashes/id:000000,sig:11,src:000000,op:flip1,pos:39'.
[*] Performing dry run (mem limit = 50 MB, timeout = 1000 ms)...
[+] Program exits with a signal, minimizing in crash mode.
[*] Stage #0: One-time block normalization...
[+] Block normalization complete, 186 bytes replaced.
[*] --- Pass #1 ---
[*] Stage #1: Removing blocks of data...
    Block length = 16, remaining size = 198
    Block length = 8, remaining size = 48
    Block length = 4, remaining size = 40
    Block length = 2, remaining size = 40
    Block length = 1, remaining size = 40
[+] Block removal complete, 158 bytes deleted.
[*] Stage #2: Minimizing symbols (11 code points)...
[+] Symbol minimization finished, 3 symbols (3 bytes) replaced.
[*] Stage #3: Character minimization...
[+] Character minimization done, 2 bytes replaced.
[*] --- Pass #2 ---
[*] Stage #1: Removing blocks of data...
    Block length = 2, remaining size = 40
    Block length = 1, remaining size = 40
[+] Block removal complete, 0 bytes deleted.

     File size reduced by : 79.80% (to 40 bytes)
    Characters simplified : 477.50%
     Number of execs done : 131
          Fruitless execs : path=67 crash=0 hang=0

[*] Writing output to 'crash1'...
[+] We're done here. Have a nice day!

minimized된 테스트 케이스를 가지고 분석을 하면 되겠습니다.

3. 마무으리

지금까지 AFL을 이용해서 오픈소스 SW 퍼징에 대해 살펴 보았습니다.

위 내용엔 언급하지 않았지만 AFL에 추가적인 기능들이 있습니다.

  1. llvm 컴파일
    • 퍼징 속도를 2x 정도 빠르게!
  2. Address-Sanitizer 지원
    • 크래시의 유형을 좀 더 쉽게 살펴 볼 수 있습니다.
  3. qemu로 blackbox 퍼징 지원
    • 소스가 없는 바이너리에 대한 퍼징을 지원합니다.(Windows 바이너리 x)
  4. Parallelized 퍼징 지원
    • 병렬처리를 지원합니다. 단일 호스트 및 ssh를 통한 다수의 호스트 지원

위 기능들은 상황에 따라 사용하면 좋을듯 합니다.
AFL이 모든것을 다 해주진 않지만, 잘 활용하면 충분히 취약점을 찾는데 도움을 받을 수 있다고 생각합니다.

참고 :
http://lcamtuf.coredump.cx/afl/
http://www.slideshare.net/CodeEngn/2012-codeengn-conference-06-beist-everyone-has-his-or-her-own-fuzzer
http://files.meetup.com/17933012/2015-03-introduction-fuzzing-with-afl.pdf

Leave a Reply

Your email address will not be published. Required fields are marked *