[Fuzzing 101] 퍼징으로 1-day 취약점 분석하기(LibXML)

들어가며

LibXML2라는 XML파싱 라이브러리를 퍼징한다. LibXML2 2.9.4에서 발생하는 CVE-2017-9048을 분석한다. 그리고 발견한 크래시에서 코드 커버리지를 분석한다.

CVE-2017-9048은 스택버퍼오버플로우 취약점이다. 실행중인 스택영역의 값을 덮어씌울 수 있으며, DTD 유효성 검사 함수에서 발생한다.

공격자는 바이너리의 컨텍스트 내에서 임의 코드를 실행할 수 있다.

준비

1-day 실습이므로 취약점이 발생했던 같은 환경을 준비한다. 또는 다음의 내용을 학습할 수 있다.

다음의 내용을 학습한다.

  • 새로운 실행 경로 탐색을 위한 커스텀 딕셔너리 사용 방법
  • 멀티 코어를 활용한 병렬 퍼징

Dictionaries

복잡한 텍스트 기반 파일 형식(ex: XML)을 퍼징하려는 경우 기본 구문 토큰 목록을 포함하는 사전(Dictionary)을 미리 제공하는 것이 유용하다.

AFL의 경우 이러한 사전은 단순히 단어 또는 값(value)의 집합이며, AFL이 현재 메모리 내 파일에 변경 사항을 적용하기 위해 사용한다. 아래와 같은 기법을 사용하여 AFL은 사전에 제공된 값을 사용한다.

재정의(Override) : 특정 위치를 n개의 바이트 수로 바꾼다. 여기서 n은 사전 항목의 길이

삽입(Insert) : 현재 파일 위치에 사전 항목을 삽입하여 모든 문자를 아래로 이동하고 파일의 크기를 늘린다.

https://github.com/AFLplusplus/AFLplusplus/tree/stable/dictionaries

Paralellization(병렬화)

멀티 코어 시스템을 사용하는 경우 CPU 리소스를 최대한 활용하기 위해 퍼징 작업을 병렬화한다.

독립 인스턴스(Independent instances)

가장 간단한 병렬화 방법이다. 완전히 별개의 afl-fuzz 인스턴스를 실행한다.

AFL은 비결정론적 테스트 알고리즘을 사용한다. 따라서 여러 AFL 인스턴스를 실행하면 크래시 발견 확률이 높아진다.

이를 위해 여러 터미널 창에서 “afl-fuzz” 인스턴스만 실행하면 되며, 각각의 터미널 창에 대해 서로 “output” 폴더를 설정할 수 있다. 간단한 방법은 코어의 수만큼 퍼징 작업을 실행한다.

  • 이때 -s 플래그를 사용하는 경우 각 인스턴스에 대해 다른 시드를 사용해야 한다.

공유 인스턴스(Shared instances)

병렬 퍼징에 대한 좀 더 개선된 접근법이다. 각 퍼저 인스턴스는 다른 퍼저에 의해 발견된 테스트 케이스를 수집한다.

일반적인 퍼징은 한 번의 실행에 하나의 마스터 인스턴스만 존재한다.

./afl-fuzz -i afl_in -o afl_out -M Master -- ./program @@

그리고, N-1개의 슬레이브를 구성할 수 있다.

./afl-fuzz -i afl_in -o afl_out -S Slave1 -- ./program @@
./afl-fuzz -i afl_in -o afl_out -S Slave2 -- ./program @@
...
./afl-fuzz -i afl_in -o afl_out -S SlaveN -- ./program @@

Download and Build

1. 대상 프로그램 다운 및 빌드

cd $HOME
mkdir Fuzzing_libxml2 && cd Fuzzing_libxml2
wget http://xmlsoft.org/download/libxml2-2.9.4.tar.gz
tar xvf libxml2-2.9.4.tar.gz && cd libxml2-2.9.4/
sudo apt-get install python-dev
CC=afl-clang-lto CXX=afl-clang-lto++ CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" ./configure --prefix="$HOME/Fuzzing_libxml2/libxml2-2.9.4/install" --disable-shared --without-debug --without-ftp --without-http --without-legacy --without-python LIBS='-ldl'
make -j$(nproc)
make install

2. 실행확인

./xmllint --memory ./test/wml.xml

Seed Corpus creation

github 저장소에서 제공하는 SampleInput.xml 파일을 이용한다.

mkdir dictionaries && cd dictionaries
wget https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/dictionaries/xml.dict
cd ..

Fuzzing

크래시를 탐지하기 위해 —valid 옵션을 활성해야한다. dictionary 경로를 -x 플래그로 지정하고 -D 플래그로 결정론적 변이를 활성화한다.(병렬 퍼징을 위한 마스터 퍼저)

afl-fuzz -m none -i ./afl_in -o afl_out -s 123 -x ./dictionaries/xml.dict -D -M master -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@

슬레이브 인스턴스 실행은 다음과 같다.

afl-fuzz -m none -i ./afl_in -o afl_out -s 234 -S slave1 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@

좌 : master 우 : slave

Triage

이전 실습에서와 같이 ASAN을 이용해 빌드했기 때문에 디버그하기에는 더 쉽다. 크래시가 발생한 파일을 인자로 하여 바이너리를 실행한다.

./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude './afl_out/default/crashes/id:000000,sig:06,src:003963,time:12456489,op:havoc,rep:4'
반응형