
Fuzzing101/Exercise 1 at main · antonio-morales/Fuzzing101
Exercise 1
Targe : XPDF 3.02
1-day : CVE-2019-13288
학습 목표
- Compiling a target application with instrumentation
- Running a fuzzer (afl-fuzz)
- Triaging crashes with a debugger (GDB)
CVE-2019-13288
Xpdf 4.01.01 버전의 Parser.cc 파일에 있는 Parser::getObj() 함수에 특정 파일을 집어넣으면 무한 재귀로 인한 DoS 발생
디렉토리 생성
mkdir fuzzing_xpdf && cd fuzzing_xpdf/
필수 도구 설치
sudo apt install build-essential
Xpdf 3.02 다운로드
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gz
타겟 빌드
cd xpdf-3.02
sudo apt update && sudo apt install -y build-essential gcc
./configure --prefix="$HOME/fuzzing101/fuzzing_xpdf/install/"
make
make install
소프트웨어 설치 경로
./configure --prefix="~/fuzzing101/fuzzing_xpdf/install/"
./configure: 디렉토리에 있는 configure 스크립트를 실행. 시스템 환경과 의존성을 확인하고 Makefile 등을 생성--prefix=옵션으로 소프트웨어 설치 경로를 지정할수 있다. 기존에는 /usr/local/ 경로에 설치되지만, --prefix 옵션을 통해 원하는 경로에 지정하여 퍼징을 편리하게 해준다.
PDF 예제파일 다운로드
mkdir pdf_examples && cd pdf_examples

sample.pdf에서 에러가 뜸 → 없어도 문제 X
테스트
$HOME/fuzzing101/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing101/fuzzing_xpdf/pdf_examples/helloworld.pdf

의존성 설치
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-14 llvm-14 llvm-14-dev clang-14 || 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 build
cd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-14"
make distrib
sudo make install
afl++ 설치 확인

llvm 버전 확인

이전 컴파일 파일 및 실행 파일 정리
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
export LLVM_CONFIG="llvm-config-14"
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing101/fuzzing_xpdf/install/"
make
make install
컴파일러 변경 및 xpdf 빌드
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing101/fuzzing_xpdf/install/"
- CC CXX : 기본 컴파일러 변경
- ./configure로 빌드
이전에 빌드했던 xpdf를 지우고, 퍼징을 위해 afl llvm으로 xpdf를 다시 빌드

퍼저 실행
afl-fuzz -i $HOME/fuzzing101/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing101/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing101/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing101/fuzzing_xpdf/output
명령어 설명
afl-fuzz -i $HOME/fuzzing101/fuzzing_xpdf/pdf_examples/ \ -o $HOME/fuzzing101/fuzzing_xpdf/out/ \ -s 123 -- \ $HOME/fuzzing101/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing101/fuzzing_xpdf/output
-i : 퍼징에 사용할 input 파일 경로 지정
-o : 퍼징 결과를 저장하는 디렉토리 → 크래시 파일이나 로그가 여깄음
-s 123 : 랜덤 시드를 고정하여 추후에 재현 가능을 보장
-- : 옵션과 인자 구분
$HOME/fuzzing101/fuzzing_xpdf/install/bin/pdftotext : 퍼징 대상 프로그램
@@ : 자동으로 생성한 입력 파일 → fuzzing input 파일
$HOME/fuzzing101/fuzzing_xpdf/output : 크래시 파일이나 버그 관련 정보가 저장
**trouble shooting**
야심차게 돌려볼려고했는데

크래시가 발생했을 때 코어덤프를 수집못한다..?
```bash
echo core | sudo tee /proc/sys/kernel/core_pattern
이렇게 수정하는 것으로 해결 가능하다.


실제로 out에서 crashes 등을 확인할 수 있다.
아래 파일들이 크래시가 발생한 파일 자체이다.
크래시 재현
$HOME/fuzzing101/fuzzing_xpdf/install/bin/pdftotext id:000002,sig:11,src:000937,time:181821,execs:251850,op:havoc,rep:5 output.txt
pdftotext에 크래시 파일을 넣어서 재실행

xpdf 다시 빌드
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
CC=gcc CXX=g++ ./configure --prefix="$HOME/fuzzing101/fuzzing_xpdf/install/"
make
make install
gdb attach
pdftotext를 gdb로 attach + 인자로 crash 났던 파일을 줘야함
gdb --args $HOME/fuzzing101/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing101/fuzzing_xpdf/out/default/crashes/id:000003,sig:11,src:000001,time:241334,execs:326073,op:havoc,rep:14
r로 실행하면
똑같은 에러가 재현되고

bt를 실행해 보면

계속 동일한 부분이 반복됨을 확인할 수 있다.
**Parser::makeStream** → 2. **Object::dictLookup** → 3. **XRef::fetch**
Parser.cc getObj에서 94번째 줄
if ((str = makeStream(obj, fileKey, encAlgorithm, keyLength,
objNum, objGen))) {

makeStream을 호출한다.
Parser.cc makeStream에서 156번째 줄
dict->dictLookup("Length", &obj);

dictLookup을 호출
Object.h dictLookup 에서 253번째 줄
inline Object *Object::dictLookup(char *key, Object *obj)
{ return dict->lookup(key, obj); }

lookup을 호출
XRef.cc fetch에서 823번째 줄
parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL,
encAlgorithm, keyLength, num, gen);

중간에 연결이 끊기는 부분이 있지만, 코드 흐름을 따라가보면, 결국 처음으로 돌아가서