1. AFL ++
AFL++는 AFL(American Fuzzy Lop) 프로젝트에서 파생된 오픈소스 퍼징 도구이다. AFL++는 AFL의 기능을 상속하면서도 성능 및 안정성을 개선하였다. 개선 사항으로 퍼징 알고리즘의 최적화, 사용자 인터페이스의 개선, 그리고 더 다양한 타겟 프로그램과 환경에 대한 지원 확대 등이 포함되었다.
1.1 특징
- 퍼징 대상
퍼징 대상은 실제로 입력을 받으면서 실행되는 모든 종류의 소프트웨어 프로그램이다. 이는 문서 리더, PDF 리더, 웹 브라우저, 파일 파서 등과 같이 다양한 입력 형태를 처리하는 프로그램을 포함한다.
- 소스 코드의 유무
소스코드가 있는 경우, AFL 또는 AFL++를 사용하여 소스 코드를 컴파일하고 특정 취약점을 탐지하기 위한 코드 커버리지 정보를 수집할 수 있다. -> 화이트박스 퍼징
소스코드가 없는 경우에도, AFL++는 블랙박스 퍼징 모드를 지원하여, 실행 가능한 바이너리에 대해 퍼징을 수행할 수 있다.
- AFL Compiler
AFL++는 효율적인 퍼징을 위해 기본 컴파일러(gcc, g++) 외에도 자체 컴파일러(afl-gcc, afl-g++)를 제공한다. 이 컴파일러들은 퍼징 대상 프로그램을 컴파일할 때 특별한 코드 커버리지 추적 기능을 삽입한다. 해당 기능은 실행 중인 프로그램의 어떤 코드 경로가 탐색되었는지를 AFL++에 알려주어, 퍼징 프로세스가 더 많은 코드 영역을 커버하도록 유도한다.
1. afl-cc (기본 컴파일러)
2. afl-clang
3. afl-clang-fast
4. afl-clang-lto (가장 빠른 컴파일러)
5. afl-gcc
6. afl-gcc-fast
7. afl-g++
8. afl-g++-fast
9. afl-lto
10. afl-lto++
"++"가 붙은 컴파일러들은 C++ 소스 코드를 위한 컴파일러이다. 이들을 모두 사용하기 위해서는 Clang/Clang++ 11 버전 이상을 가지고 있어야 한다.
- Clang에 대한 자세한 설명은 아래 링크 확인
컴파일러 동작과정과 종류 (GCC & LLVM)
1. 프로그램 생성 과정 프로그램 생성 과정 우리는 프로그램을 만들기 위해 프로그래밍 언어를 활용해 “소스코드”를 작성한다. 소스코드는 4가지 과정을 거쳐 실행가능한 프로그램을 만든다.
dhkstn.tistory.com
- Input Preparation
퍼징을 시작하기 전, 사용자는 테스트 대상 프로그램이 처리할 수 있는 다양한 유형의 입력 데이터를 준비해야 한다. 이는 텍스트 파일, 이미지 파일, 동영상 파일 등 프로그램이 받아들일 수 있는 모든 형태의 데이터를 포함할 수 있다. 이 입력 파일들을 하나의 디렉터리에 모아두면, AFL++와 같은 퍼징 도구가 이를 기반으로 퍼징 테스트를 수행할 수 있다. AFL++는 기본적으로 "./testcases" 디렉터리에 다양한 유형의 샘플 입력 파일들을 제공하여, 사용자가 이를 활용할 수 있도록 한다.
- AFL-CMIN
afl-cmin은 입력 데이터 세트를 최소화하는 유틸리티이다. 이 도구는 퍼징 과정에서 중복되거나 유사한 결과만 생성하는 불필요한 입력 파일들을 제거함으로써, 퍼징의 효율성을 높인다. 무엇보다 afl-cmin은 입력 데이터의 크기를 줄이기 때문에 더 빠르고 효과적인 퍼징이 가능하며, 퍼징 도중 발견할 수 있는 유니크한 버그의 수를 최대화하는 데 도움을 준다.
2. AFL 실행 명령어
afl-fuzz -i input -o output -- bin/target -someopt @@
- -i input
사용자가 준비한 입력 데이터가 존재하는 디렉터리를 지정한다.
- -o output
퍼징 과정에서 생성된 결과물(크래시 리포트, 통계 등)이 저장되는 디렉터리를 지정한다.
- bin/target
퍼징을 진행할 프로그램의 실행 파일 경로이다. '--'는 AFL의 명령어 옵션 끝을 나타내고, 'bin/target'은 퍼징 대상 프로그램을 가리킨다.
- -someopt
대상 프로그램에 전달할 추가 옵션들이다. 이는 선택적이며, 대상 프로그램에 따라 다를 수 있다.
- @@
AFL에서 입력 파일 경로를 대체할 위치를 나타내는 특수한 플레이스홀더이다. AFL은 이 위치에 input 디렉터리 내의 입력 파일 경로를 자동으로 삽입한다. 입력 파일이 여러 개인 경우, AFL이 자동으로 해당 파일들을 대상 프로그램에 순차적으로 제공한다.
플레이스홀더(Placeholder)는 컴퓨터 프로그래밍, 템플릿 생성, 디지털 콘텐츠 제작 등 다양한 분야에서 사용되는 용어로, 실제 값이나 데이터가 들어갈 자리를 임시로 표시하는 데 사용된다. 이는 나중에 실제 값이나 데이터로 대체될 예정인 임시 마커나 심볼을 의미한다.
2.1 ASAN (Address Sanitizer)
ASAN은 C/C++ 언어로 작성된 소프트웨어 메모리 오류를 검출하기 위한 도구이다. Stack/Heap Buffer Overflow, Memory leak, Use After Free 등의 오류를 검출할 수 있으며, 이를 통해 소프트웨어의 안정성과 보안성을 향상시킬 수 있다.
AFL_USE_ASAN=1 CC=afl-clang-lto ./configure --prefix="경로"
AFL_USE_ASAN=1 make
AFL_USE_ASAN=1 make install
configure와 make, make install 앞에 AFL_USE_ASAN=1을 붙여 활성화할 수 있으며, 활성화할 경우 Crash의 정보가 매우 자세히 나온다.
AFL에서 ASAN을 활성화하게 되면, Crash의 정보가 매우 자세하게 나오는 장점이 있지만, 가상 메모리 사용량이 크게 증가하게 되므로, afl-fuzz가 기본으로 설정한 메모리 제한을 초과할 가능성이 있다. 이러한 상황을 방지하기 위해, afl-fuzz 명령어에 -m none 옵션을 추가함으로써 메모리 제한을 해제할 수 있다.
alf-fuzz -m none -i input -o output -- bin/target -someopt @@
3. AFL ++를 이용한 Xpdf 퍼징 실습
- 실습 환경
- Vmware Workstation Pro 17
- Ubuntu 20.04.6 LTS
- Xpdf 3.02 Version
3.1 CVE-2019-13288
Xpdf 4.0.01에서 Parser.cc의 Parser::getObj() 함수는 공격자가 의도적으로 제작한 파일을 통해 무한 재귀를 일으킬 수 있으며, 공격자는 원격으로 DoS 공격을 수행할 수 있다. 해당 취약점은 PDF 파싱 과정에서 특정 함수에 무한 재귀를 유발하여 DoS를 일으킨다.
Xpdf는 PDF 파일을 보기, 변환, 수정할 수 있는 무료 오픈소스 도구 라이브러리 모음이다. C++로 작성되어 있으며, Unix, Linux, Window와 같은 다양한 플랫폼에서 작동한다. Xpdf는 PDF 문서의 텍스트와 이미지를 추출할 수 있는 기능을 포함하고 있으며, 사용자 친화적인 인터페이스와 명령줄 도구를 제공하여 다양한 용도로 활용될 수 있다.
- Xpdf 3.02 Download
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gz
- Build
cd xpdf-3.02
# 빌드를 위한 의존성 다운로드
sudo apt update && sudo apt install -y build-essential gcc
# 컴파일 결과 위치 설정
./configure --prefix="$HOME/fuzzing_xpdf/install/"
# 컴파일
make
make install
build는 소스 코드에서 실행 가능한 소프트웨어나 라이브러리를 생성하는 과정을 의미한다. 일반적으로 소스 코드의 컴파일, 링크 등이 포함된다.
- PDF 예제 파일을 통한 실행 확인
cd $HOME/fuzzing_xpdf
mkdir pdf_examples && cd pdf_examples
wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf
wget http://www.africau.edu/images/default/sample.pdf
wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf
- pdfinfo 바이너리를 통한 테스트
pdfinfo는 Xpdf 패키지에 포함된 유틸리티 중 하나로, PDF 파일의 메타데이터를 추출하여 출력하는 도구이다. pdfinfo 명령어를 사용하면, PDF 파일에 포함된 제목, 작성자, 주제, 키워드, 생성 및 수정 날짜, 페이지 수, 페이지 크기 등과 같은 기본 정보를 얻을 수 있다.
$HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf
"-box" 옵션은 PDF 파일에서 페이지 박스 정보를 출력하는 옵션으로, 페이지 박스 정보에는 미디어 박스, 크롭 박스 등 PDF 페이지의 다양한 영역 정보가 포함된다.
"-meta" 옵션은 PDF 파일의 메타데이터 정보를 출력하는 옵션으로, 메타데이터에는 저자, 제목, 주제, 키워드 등의 정보가 포함될 수 있다. "$HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf"는 pdfinfo가 분석할 PDF 파일을 지정한다.
3.2 AFL++ 설치
- 의존성 패키지 설치
sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools
sudo apt-get install -y lld-11 llvm-11 llvm-11-dev clang-11 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-dev
- AFL++ 설치 전 필요한 패키지 설치
sudo apt update
sudo apt install -y libffi-dev build-essential
# unicorn pkg build
# Ubuntu
cd $HOME
git clone https://github.com/unicorn-engine/unicorn.git
sudo apt install cmake pkg-config
cd unicorn
# build
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
cd ~/unicorn/bindings/python
python3 setup.py install
unicorn pkg는 QEMU를 기반으로 하는 경량의 다중 플랫폼이며, 다중 아키텍처 CPU 에뮬레이터 프레임워크이다.
본 실습에서는 unicorn 패키지를 사용하지 않지만, AFL++ 설치 과정 중 unicorn pkg 관련 경고가 출력되기에, 사전 설치한다.
cd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd ~/AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install
- AFL++ 정상 설치 확인
- Xpdf(target) 컴파일
AFL은 코드 커버리지 기반으로 작동하기 때문에, 프로그램 실행 도중 어떤 코드 부분이 활성화되는지 추적한다. 이 과정을 위해서는 프로그램 코드에 "Instrumentation" 코드, 즉 추적 코드를 추가해야 한다. 이 코드는 프로그램을 재컴파일하는 과정 중에 삽입되므로, 타켓 프로그램을 재컴파일해야 한다. Instrumentation 코드를 통해 프로그램 실행 중에 어떤 부분이 실행되는지 실시간으로 기록할 수 있다. AFL은 이러한 정보를 활용하여 프로그램을 더 광범위하게 탐색하는 새로운 입력을 생성하여 취약점을 보다 효과적으로 발견할 수 있다.
위와 같은 이유로 타겟 프로그램에 Instrumentation 코드를 삽입하기 위해 타겟 프로그램을 재컴파일한다. 본 실습에서는 afl-clang-fast를 사용하여 Xpdf(target)을 재컴파일 하였다.
#컴파일 결과물 삭제
rm -r $HOME/fuzzing_xpdf/install
#컴파일 내용 삭제
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
# afl++ 컴파일러를 이용한 컴파일을 위한 환경설정
export LLVM_CONFIG="llvm-config-11"
# 컴파일러 변경
# ~/fuzzing_xpdf/xpdf-3.02$ -> 여기서 실행
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/"
cd $HOME/fuzzing_xpdf/xpdf-3.02/
# 컴파일
make
make install
CC = gcc, C Compiler
CXX = g++, C++ Compiler
3.3 AFL ++ 퍼저 실행
- 실행 명령어
afl-fuzz -i $HOME/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing_xpdf/output
- 옵션 설명
- -i : 입력 케이스가 위치한 디렉터리이다.
- -o : 퍼저가 생성한 파일을 저장할 디렉터리이다.
- -s : 정적 랜덤 시드로, 재연 가능성을 높이기 위해 사용된다. 예시에서는 123을 사용한다.
- @@ : AFL이 입력 파일 이름으로 대체할 위 표시자이다. 이 위치에 AFL은 `-i` 옵션으로 지정된 디렉터리 내의 각 입력 파일을 순차적으로 대체하여 사용한다.
- 코드 실행
위와 같은 오류가 발생하였을 경우, 아래 명령어로 해결할 수 있다.
sudo su
echo core >/proc/sys/kernel/core_pattern
exit
- 퍼저 재실행
- 14분 퍼징 돌렸을 때 2개의 Unique Crash를 발견하였다.
3.4 크래시 분석
- cd ~/fuzzing_xpdf/out/default/crashes
- 파일 이름 변경
- sudo ./install/bin/pdftotext ./out/default/crashes/crash2 ./output
- gdb 분석
gdb로 크래시를 분석하기 위해서는 Xpdf를 다시 빌드해야 한다. 재빌드하는 이유는 디버그 정보 및 심볼릭 링크 정보를 얻기 위함이다.
# 기존 컴파일 결과와 내용 삭제
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
# 컴파일러 재설정
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_xpdf/install/"
# 컴파일
make
make install
=> CFLAGS, CXXFLAGS는 컴파일 시 최적화 옵션을 주는 기능이다.
- gdb 실행
- crash1
- ~/fuzzing_xpdf 경로
- gdb --args ./install/bin/pdftotext ./out/default/crashes/crash2 ./output
- gdb -> run
- gdb -> bt (backtrace)
gdb에서 run과 backtrace를 살펴봤을 때, 많은 재귀적인 Parser::getObj 호출을 확인할 수 있다. 이는 CVE-2019-13288의 설명과 일치한다.
- Crash2
- 무한 재귀 -> DoS로 이어진다.
참고자료 및 출처
'5. Fuzzing' 카테고리의 다른 글
Fuzzing Study (0) | 2024.02.02 |
---|