使用AFL++復現歷史CVE
這是我做Fuzzing101的一些筆記,通過復現CVE的方式熟悉AFL++的基本使用方式,過程對我這樣的萌新十分友好,同時中間涉及到的代碼審計等方面還是值得后續學習的。
Exercise 1 - Xpdf
CVE-2019-13288(https://www.cvedetails.com/cve/CVE-2019-13288/) in XPDF 3.02 (infinite recursion)
安裝調試目標
從github等途徑下載并解壓。
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gztar -xvzf xpdf-3.02.tar.gz
安裝依賴和目標。
sudo apt update && sudo apt install -y build-essential gcc./configure --prefix="$HOME/fuzz_target/fuzzing_xpdf/install/"makemake install
配置configure時有各種環境變量需要設置,比較常用的有:
- AS:匯編程序名稱
- CC:C編譯器名稱
- CXX:C++編譯器名稱
- CPP:C預編譯器名稱
- **FLAGS:**為不同編譯器名稱,表示對應編譯器的參數
- LD:鏈接器名稱
- AR:歸檔器archiver名稱
- RANLIB:符號表添加器名稱(AR和RANLIB是什么具體看這里)
獲取樣本
- 自己隨便寫,fuzzer會自己變異,但效率較低。
- 從網上(github、官網、壓縮包自帶)找現成的樣本sample。
cd $HOME/fuzz_target/fuzzing_xpdfmkdir pdf_examples && cd pdf_exampleswget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdfwget http://www.africau.edu/images/default/sample.pdfwget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf
測試安裝程序運行
$HOME/fuzz_target/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzz_target/fuzzing_xpdf/pdf_examples/helloworld.pdf
使用fuzz編譯器編譯(afl-clang-fast)
先刪除原先的安裝,重新編譯安裝庫。
rm -r installcd xpdf-3.02make cleanexport LLVM_CONFIG="llvm-config-12"CC=afl-clang-fast CXX=afl-clang-fast++ ./configure --prefix="$HOME/fuzz_target/fuzzing_xpdf/install/"makemake install
fuzz
afl-fuzz -i $HOME/fuzz_target/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzz_target/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzz_target/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzz_target/fuzzing_xpdf/output
參數
-i:輸入樣本路徑
-o:輸出存儲路徑
-s:fuzzing時隨機數使用的種子,這里為了盡量保證復現結果,設為123
--:目標程序
這里的@@不能少,雖然初始輸入都來源于設置的-i參數,但我們需要根據程序讀取輸入的方式進行調整此參數。
- 加@@:被fuzz的程序從文件讀取輸入。
- 不加@@:被fuzz的程序從標準輸入輸出流讀取輸入。
跑一會就能出結果。

動態調試
源碼編譯出帶調試符號的文件。
make cleanCFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzz_target/fuzzing_xpdf/install/"makemake install
運行gdb。
gdb --args $HOME/fuzz_target/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzz_target/fuzzing_xpdf/out/default/crashes/ $HOME/fuzz_target/fuzzing_xpdf/output
追蹤crash路徑:
bt:跑出crash后查看調用路徑。

報錯信息Program received signal SIGSEGV, Segmentation fault,存在內存泄漏。
報錯位置 _int_malloc (av=av@entry=0x7ffff7c6bb80 , bytes=bytes@entry=128) at malloc.c:3679,glibc報了個錯,顯然是堆內存出了問題。
執行流信息,分析一下可以看出調用過程是循環的,判斷為無限循環漏洞。
根據函數調用找到漏洞位置。
從xpdf/Parse.cc 94行的makeStream調用,一路跟著報錯往下翻就會找到這個套娃,這里就不細說了。

漏洞修復
下個xpdf4.02源碼對比一下就好,修復方式比較簡單,加了個變量,記錄循環次數,超過一定次數就結束進程。
wget https://dl.xpdfreader.com/old/xpdf-4.02.tar.gz
Exercise 2 - libexif
CVE-2009-3895(https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3895) (heap-based buffer overflow)and CVE-2012-2836(https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2836) (Out-of-bounds Read)in libexif 0.6.14
安裝調試目標
從github等途徑下載并解壓。
tar -xzvf libexif-0_6_14-release.tar.gz
安裝依賴。?
apt-get install
配置configure并安裝。
autoreconf -fvi 用于適配系統環境,簡化config命令//安裝autoreconf sudo apt-get install autopoint libtool gettext libpopt-dev./configure --enable-shared=no (如果是庫文件,必須編譯成靜態庫) --prefix="/root/fuzz_target/fuzzing_libexif/install/"makemake install
獲取交互應用(如果調試的是庫,需要調用接口fuzz)
- 自己寫一個c程序調用接口,用afl提供的編譯器編譯出來。
- 直接找調用了庫文件的應用,這是這題采用的方法。
使用fuzz編譯器編譯(afl-clang-lto)
先刪除原先的安裝,重新編譯安裝庫。
make cleanexport LLVM_CONFIG="llvm-config-12"CC=/root/fuzz/AFLplusplus/afl-clang-lto ./configure --enable-shared=no --prefix="/root/fuzz_target/fuzzing_libexif/install/"makemake install
如果編譯不通過,可以加 AR=llvm-ar RANLIB=llvm-ranlib LD=afl-clang-lto
重新編譯應用。
make cleanexport LLVM_CONFIG="llvm-config-12"CC=/root/fuzz/AFLplusplus/afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzz_target/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzz_target/fuzzing_libexif/install/lib/pkgconfigmakemake install
測試運行。
$HOME/fuzz_target/fuzzing_libexif/install/bin/exif $HOME/fuzz_target/fuzzing_libexif/exif-samples-master/jpg/Canon_40D_photoshop_import.jpg
fuzz
afl-fuzz -i $HOME/fuzz_target/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/fuzz_target/fuzzing_libexif/out/ -s 123 -- $HOME/fuzz_target/fuzzing_libexif/install/bin/exif @@
動態調試
編譯出帶調試信息的可執行文件。
cd libexif-libexif-0_6_14-releasemake cleanCFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzz_target/fuzzing_libexif/install/"makemake install cd exif-exif-0_6_15-releasemake cleanCFLAGS="-g -O0" CXXFLAGS="-g -O0" PKG_CONFIG_PATH=$HOME/fuzz_target/fuzzing_libexif/install/lib/pkgconfig ./configure --prefix="$HOME/fuzz_target/fuzzing_libexif/install/"makemake install
丟進gdb,跑出crash。
crash1
gdb --args ./install/bin/exif ./out/default/crashes/id\:000000\,sig\:11\,src\:000281\,time\:64869\,execs\:64957\,op\:havoc\,rep\:16
報錯信息Program received signal SIGSEGV, Segmentation fault.,存在內存泄漏。
報錯位置exif_get_sshort (buf=0x555655563195 , order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:92,注意這里的報錯,內存地址無法訪問,再看地址,估計為堆緩沖區溢出。
crash2
gdb --args ./install/bin/exif ./out/default/crashes/id\:000002\,sig\:11\,src\:000301\,time\:126417\,execs\:126621\,op\:havoc\,rep\:8
報錯信息Program received signal SIGSEGV, Segmentation fault.,存在內存泄露。
報錯位置__memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:345。
crash3
gdb --args ./install/bin/exif ./out/default/crashes/id\:000006\,sig\:11\,src\:000492+000181\,time\:341313\,execs\:358541\,op\:splice\,rep\:8
報錯信息Program received signal SIGSEGV, Segmentation fault,存在內存泄露。
報錯位置exif_get_slong (b=0x555555582000 , order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:135,與1類似。
漏洞修復
https://github.com/libexif/libexif/commit/8ce72b7f81e61ef69b7ad5bdfeff1516c90fa361
https://github.com/libexif/libexif/commit/00986f6fa979fe810b46e376a462c581f9746e06
Exercise 3 - tcpdump(使用ASAN)
CVE-2017-13028(https://www.cvedetails.com/cve/CVE-2017-13028/) in TCPdump 4.9.2(Out-of-bounds Read)
libcap是tcpdump的依賴庫,可以不install,但需要保證目錄位置與tcpdump根目錄相同,且名稱可識別。
使用ASAN編譯
cd $HOME/fuzz_target/fuzzing_tcpdump/libpcap-1.8.0/export LLVM_CONFIG="llvm-config-12"CC=/root/fuzz/AFLplusplus/afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzz_target/fuzzing_tcpdump/install/"AFL_USE_ASAN=1 makeAFL_USE_ASAN=1 make install cd $HOME/fuzz_target/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/AFL_USE_ASAN=1 CC=/root/fuzz/AFLplusplus/afl-clang-lto ./configure --prefix="$HOME/fuzz_target/fuzzing_tcpdump/install/"AFL_USE_ASAN=1 makeAFL_USE_ASAN=1 make install
這里配置tcpdump的configure時也要加AFL_USE_ASAN=1,因為它的依賴庫也加了ASAN。
fuzz
afl-fuzz -m none -i ./tcpdump-tcpdump-4.9.2/tests/ -o ./afl_out/ -s 123 -- ./install/sbin/tcpdump -vvvvXX -ee -nn -r @@
ASAN會消耗大量內存,使用-m none不限制內存使用。
這個我跑了比較久(掛著進程容易忘關)。

動態調試
有ASAN就不用再重新編譯整個文件來調試了(這里如果用普通編譯來運行crash反而得不到報錯信息,顯然這里的內存泄露不會直接導致crash)。
./install/sbin/tcpdump -vvvvXX -ee -nn -r ./afl_out/default/crashes/id\:000000\,sig\:06\,src\:011483\,time\:43941578\,execs\:17770128\,op\:havoc\,rep\:8
直接運行crash,ASAN會給出較為詳細的報錯和調用棧。

報錯信息:AddressSanitizer: heap-buffer-overflow /root/fuzz_target/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/./extract.h:184:20 in EXTRACT_16BITS,直接說明是堆溢出。
漏洞修復
https://github.com/the-tcpdump-group/tcpdump/commit/85078eeaf4bf8fcdc14a4e79b516f92b6ab520fc#diff-05f854a9033643de07f0d0059bc5b98f3b314eeb1e2499ea1057e925e6501ae8L381
Exercise 4 - libtiff(coverage優化)
CVE-2016-9297(https://www.cvedetails.com/cve/CVE-2016-9297/) in libtiff 4.0.4 (Out-of-bounds Read)
使用lvoc(覆蓋率檢測)編譯libtiff
CFLAGS="--coverage" LDFLAGS="--coverage" ./configure --prefix="$HOME/fuzz_target/fuzzing_tiff/install/" --disable-sharedmakemake install
獲取覆蓋率
cd $HOME/fuzzing_tiff/tiff-4.0.4/lcov --zerocounters --directory ./lcov --capture --initial --directory ./ --output-file app.info$HOME/fuzz_target/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w $HOME/fuzz_target/fuzzing_tiff/tiff-4.0.4/test/images/palette-1c-1b.tifflcov --no-checksum --directory ./ --capture --output-file app2.info
lcov --zerocounters --directory ./:重置計數器。
lcov --capture --initial --directory ./ --output-file app.info:為每個instrumented line返回覆蓋率數據的初始化基準。
$HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w $HOME/fuzzing_tiff/tiff-4.0.4/test/images/palette-1c-1b.tiff0:運行需要分析的應用,可以用多個樣本運行多次。
lcov --no-checksum --directory ./ --capture --output-file app2.info:保存覆蓋率狀態。
將結果轉化生成HTML輸出。
genhtml --highlight --legend -output-directory ./html-coverage/ ./app2.info
編譯文件
export LLVM_CONFIG="llvm-config-12"CC=/root/fuzz/AFLplusplus/afl-clang-lto ./configure --prefix="$HOME/fuzz_target/fuzzing_tiff/install/" --disable-sharedAFL_USE_ASAN=1 make -j4AFL_USE_ASAN=1 make installafl-fuzz -m none -i $HOME/fuzz_target/fuzzing_tiff/tiff-4.0.4/test/images/ -o $HOME/fuzz_target/fuzzing_tiff/out/ -s 123 -- $HOME/fuzz_target/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w @@
這里使用盡可能多的參數,增大fuzz到漏洞代碼的概率。
fuzz
afl-fuzz -m none -i $HOME/fuzz_target/fuzzing_tiff/tiff-4.0.4/test/images/ -o $HOME/fuzz_target/fuzzing_tiff/afl_out/ -s 123 -- $HOME/fuzz_target/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w @@
動態調試
查看報錯
./install/bin/tiffinfo -D -j -c -r -s -w ./out/default/crashes/id\:000000\,sig\:06\,src\:000016\,time\:556815\,execs\:377779\,op\:havoc\,rep\:4

報錯信息:AddressSanitizer: heap-buffer-overflow (/root/fuzz_target/fuzzing_tiff/install/bin/tiffinfo+0x2aaf11) in fputs,堆溢出。
漏洞修復
https://github.com/vadz/libtiff/commit/30c9234c7fd0dd5e8b1e83ad44370c875a0270ed
Exercise 5 - libxml2(自定義字典、并行)
CVE-2017-9048(https://nvd.nist.gov/vuln/detail/CVE-2017-9048) in LibXML2 2.9.4(stack buffer overflow)
字典用途
本質上就是有一定意義的字符串token。
- Override:直接覆蓋樣本中的n個字節
- Insert:在樣本中插入n個字節
AFL++提供了先成的字典。https://github.com/AFLplusplus/AFLplusplus/tree/stable/dictionaries(可以湊合)
也可以自己手動構建,用codeql(在線平臺LGTM)可以快速查詢我們需要的特征字符串如:
- 判斷的條件
- strcmp、memcmp中的參數
- 聲明的常量等
并行
將fuzzer分為master和slave,實現共享instance
./afl-fuzz -i afl_in -o afl_out -M Master -- ./program @@ ./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 @@
編譯文件
sudo apt-get install python-devCC=/root/fuzz/AFLplusplus/afl-clang-lto CXX=/root/fuzz/AFLplusplus/afl-clang-lto++ CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" ./configure --prefix="$HOME/fuzz_target/fuzzing_libxml2/libxml2/install" --disable-shared --without-debug --without-ftp --without-http --without-legacy --without-python LIBS='-ldl'AFL_USE_ASAN=1 AFL_MAP_SIZE=262144 make -j$(nproc)AFL_USE_ASAN=1 AFL_MAP_SIZE=262144 make install
這里在編譯時沒有直接用ASAN,而是用了編譯器自帶的fsanitize,功能如下:
-fsanitize=leak:檢測內存泄漏。
-fsanitize=address:檢測內存越界,這等于ASAN。
編譯時設置AFL_MAP_SIZE=262144,決定共享空間大小,因為程序較大,不改成一個較大值會給彈一個警告,最好設置一下。
獲取樣本和字典
這里用的是fuzzing101提供的樣本以及test中的dtd9(DTD,它們會定義 XML 文檔的結構和合法元素/屬性,并用于確定 xml 文檔是否有效)。
mkdir afl_in && cd afl_inwget https://raw.githubusercontent.com/antonio-morales/Fuzzing101/main/Exercise%205/SampleInput.xmlcd ..
使用AFL++提供的字典。
mkdir dictionaries && cd dictionarieswget https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/dictionaries/xml.dictcd ..
fuzz
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 @@
-D:打開persistent mutations
然后要跑很久,居然是靠havoc出的讓我很意外。

動態調試
先手動編譯出不插樁的程序,丟進gdb里調試。
gdb --args ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude ./afl_out/slave1/crashes/id:000009,sig:06,src:009269,time:119653664,execs:60774850,op:havoc,rep:4

報錯信息*** stack smashing detected ***: terminated,判斷為棧溢出漏洞。
漏洞位置__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50,找到問題代碼。
漏洞修復
https://github.com/GNOME/libxml2/commit/932cc9896ab41475d4aa429c27d9afd175959d74




