AFL++ Fuzz一個libexif例子

unr4v31發表於2021-09-09

CVE-2009-3895

首先在NVD找到漏洞描述如下:

大致意思是說:libexif 0.6.18 中的 libexif/exif-entry.c 中的 exif_entry_fix 函式中基於堆的緩衝區溢位允許遠端攻擊者導致拒絕服務或可能通過無效的 EXIF 影像執行任意程式碼

接下來找到libexif 0.6.18 ,官方在0.6.19版本修復了此漏洞:

CVE-2012-2836

同樣在NVD中找到漏洞描述:

大致意思:0.6.21 之前的 EXIF 標籤解析庫(又名 libexif)中的 exif-data.c 中的 exif_data_load_data 函式允許遠端攻擊者造成拒絕服務(越界讀取)或可能通過精心設計的方式從程式記憶體中獲取敏感資訊影像中的 EXIF 標籤。簡單來說也就是存在越界訪問漏洞

官方在0.6.21版本修復了此漏洞:

實驗目的

  • 使用外部應用程式Fuzz庫檔案
  • 使用afl-clang-lto,這是一種無碰撞檢測,它比afl-clang-fast更快並提供更好的結果

實驗環境

所有測試都在Ubuntu 20.04.2 LTS上測試過。強烈建議您使用相同的作業系統版本以避免不同的Fuzz結果,並在物理機而不是虛擬機器上執行 AFL++ ,以獲得最佳效能

Fuzz過程

下載相關檔案

首先建立一個資料夾用於存放Fuzz目標:

mkdir libexif && cd libexif

找到libexif檔案,下載並解壓:

wget https://sourceforge.net/projects/libexif/files/libexif/0.6.18/libexif-0.6.18.tar.gz
tar -zxvf libexif-0.6.18.tar.gz
cd libexif-0.6.18

因為libexif編譯後是一個庫檔案,所以還需要下載使用庫介面的應用程式,這裡選擇exif命令列0.6.15

wget https://github.com/libexif/exif/archive/refs/tags/exif-0_6_15-release.tar.gz
tar -xzvf exif-0_6_15-release.tar.gz

構建

首先回到libexif庫檔案目錄下進行編譯:

cd ..
sudo apt-get install autopoint libtool gettext libpopt-dev
autoreconf -fvi
./configure --enable-shared=no --prefix="/home/fuzz/libexif/install/"
make
make install

此時庫檔案已經編譯好了,再進入exif目錄進行編譯:

cd exif-exif-0_6_15-release/
autoreconf -fvi
./configure --enable-shared=no --prefix="/home/fuzz/libexif/install/" PKG_CONFIG_PATH=$HOME/libexif/install/lib/pkgconfig
make
make install

測試exif能否執行只需要輸入:

$HOME/libexif/install/bin/exif

此時應該看到如下內容:

執行Fuzz

首先要確定程式是做什麼的,要輸入什麼內容,希望返回什麼內容,可以先閱讀程式幫助資訊,並測試程式功能。

Exif是一種檔案格式,這是在網上搜尋到的相關描述:

可交換影像檔案格式(英語:Exchangeable image file format,官方簡稱Exif),是專門為數位相機的照片設定的,可以記錄數碼照片的屬性資訊和拍攝資料。

舉個例子,就像下面這樣:

那麼就需要找到這樣的檔案樣本,好在萬能的GitHub啥都有,可以直接下載:

git clone https://github.com/ianare/exif-samples.git

下載好後進入目錄,使用exif命令隨便檢視一張圖片的資訊:

$HOME/libexif/install/bin/exif Nikon_D70.jpg

可以看到如下結果:

現在程式功能已經知道了,需要使用afl編譯器重新編譯程式來執行Fuzz。這次使用afl-clang-lto作為編譯器來構建程式,afl-clang-lto相比於afl-clang-fast是更好的選擇,因為它是一種無碰撞檢測,而且比afl-clang-fast 快。

如果不確定何時使用哪種編譯器,可參考如下內容:

+--------------------------------+
| clang/clang++ 11+ is available | --> use LTO mode (afl-clang-lto/afl-clang-lto++)
+--------------------------------+     see [instrumentation/README.lto.md](instrumentation/README.lto.md)
    |
    | if not, or if the target fails with LTO afl-clang-lto/++
    |
    v
+---------------------------------+
| clang/clang++ 6.0+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++)
+---------------------------------+     see [instrumentation/README.llvm.md](instrumentation/README.llvm.md)
    |
    | if not, or if the target fails with LLVM afl-clang-fast/++
    |
    v
 +--------------------------------+
 | gcc 5+ is available            | -> use GCC_PLUGIN mode (afl-gcc-fast/afl-g++-fast)
 +--------------------------------+    see [instrumentation/README.gcc_plugin.md](instrumentation/README.gcc_plugin.md) and
                                       [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md)
    |
    | if not, or if you do not have a gcc with plugin support
    |
    v
   use GCC mode (afl-gcc/afl-g++) (or afl-clang/afl-clang++ for clang)

使用編譯器重新構建程式:

rm -r $HOME/libexif/install
cd $HOME/libexif/libexif-0.6.18
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/libexif/install"
make
make install
cd $HOME/libexif/libexif-0.6.18/exif-exif-0_6_15-release
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/libexif/install/" PKG_CONFIG_PATH=$HOME/libexif/install/lib/pkgconfig
make
make install

然後可以開始愉快的Fuzz了:

afl-fuzz -i /home/fuzz/libexif/exif_samples/jpg/ -o /home/fuzz/libexif/out -s 123 -- /home/fuzz/libexif/install/bin/exif @@

漏洞復現及修復

漏洞復現

幾分鐘後可獲得多個crash:

使用GDB將crash檔案輸入到程式中:

gdb --args /home/fuzz/libexif/install/bin/exif ./crash1.jpg

檢視棧回溯:

可看出明顯是malloc_printerr使程式crash,那麼再往父函式找,找到exif_content_fix函式,在此處下斷點,重新執行程式,單步跟蹤,看看是什麼原因使程式崩潰。跟蹤到__GI___libc_realloc函式時,在_int_realloc報錯,檢視引數如下:

_int_realloc函式定義:void* _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize, INTERNAL_SIZE_T nb) ,單步跟進_int_realloc,得到如下結果:

檢視堆中內容,觀察到下一chunk的size域已經堆溢位被破壞,無法malloc導致程式crash:

修復

可以在GitHub中檢視修復程式碼:

Fix a buffer overflow on corrupt EXIF data. · libexif/libexif@8ce72b7

Fix a buffer overflow on corrupted JPEG data · libexif/libexif@00986f6

總結

  • 在進行Fuzz時,資訊收集同等重要,如果是復現,需要知曉Fuzz目標的CVE資訊。如果是漏洞挖掘過程,則難度會更上一個等級。

  • 熟悉程式的構建過程、程式的功能是必不可少的步驟,如果完全不知道程式是幹嘛的,那漏洞挖掘也就無從下手。

  • 在復現漏洞時,應該考慮怎麼去證明這個漏洞,以及程式是如何崩潰的,可以在崩潰時的上層函式打上斷點單步跟蹤,觀察引數和函式呼叫過程

  • 另外:可以使用Eclipse-CDT 代替 GDB 進行除錯,這是一個介面化的工具,需要有Java環境,下載連結:

    CDT Downloads | The Eclipse Foundation

    具體使用方法就不贅述了,網上有得很。工具都無所謂,看自己喜歡哪種,我就習慣GDB

相關文章