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

들어가며

GIMP라는 이미지에디터를 퍼징한다. GIMP 2.8.16에서 발생하는 CVE-2016-4994을 분석한다. 그리고 발견한 크래시에서 코드 커버리지를 분석한다.

CVE-2017-9048은 Use-After-Free 취약점이다. 동적할당된 heap을 free하고 다시 재사용할 때 취약점이 발생한다. GIMP에서는 조작된 XCF 파일에서 발생한다.

공격자는 유효한 데이터의 손상에서 임의 코드 실행까지 다양한 악의적인 행위를 할 수 있다.

준비

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

다음의 내용을 학습한다.

  • 퍼징 속도 향상을 위한 persist mode 사용
  • 상호작용바이너리 퍼징 / GUI 프로그램 퍼징

Persist mode

AFL 지속 모드(Persist mode)는 단일 프로세스를 사용하는 퍼저를 기반으로 한다. 퍼저는 하나의 타겟 프로세스에 코드를 주입하여 값을 변조하거나 삽입한다.

afl-fuzz는 프로세스 내 퍼징의 이점과 멀티 프로세스 도구인 지속 모드를 결합한 작업 방법을 지원한다.

지속 모드에서 AFL++는 각 퍼즈 단계 실행을 위한 새로운 프로세스를 생성하는 대신 단일 분기 프로세스에서 대상을 여러번 퍼즈한다. 이 모드는 퍼징 속도를 20배까지 향상시킬 수 있다.

기본 구조는 아래와 같다.

//Program initialization

  while (__AFL_LOOP(10000)) {

    /* Read input data. */
    /* Call library code to be fuzzed. */
    /* Reset state. */
  }
  
  //End of fuzzing

 

Download and Build

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

cd $HOME
mkdir Fuzzing_gimp && cd Fuzzing_gimp
sudo apt-get install build-essential libatk1.0-dev libfontconfig1-dev libcairo2-dev libgudev-1.0-0 libdbus-1-dev libdbus-glib-1-dev libexif-dev libxfixes-dev libgtk2.0-dev python2.7-dev libpango1.0-dev libglib2.0-dev zlib1g-dev intltool libbabl-dev

 

2. GEGL 0.2(Generic Graphics Library)의 설치가 필요하다.

wget https://download.gimp.org/pub/gegl/0.2/gegl-0.2.0.tar.bz2
tar xvf gegl-0.2.0.tar.bz2 && cd gegl-0.2.0
sed -i 's/CODEC_CAP_TRUNCATED/AV_CODEC_CAP_TRUNCATED/g' ./operations/external/ff-load.c
sed -i 's/CODEC_FLAG_TRUNCATED/AV_CODEC_FLAG_TRUNCATED/g' ./operations/external/ff-load.c

 

3. build

./configure --enable-debug --disable-glibtest  --without-vala --without-cairo --without-pango --without-pangocairo --without-gdk-pixbuf --without-lensfun --without-libjpeg --without-libpng --without-librsvg --without-openexr --without-sdl --without-libopenraw --without-jasper --without-graphviz --without-lua --without-libavformat --without-libv4l --without-libspiro --without-exiv2 --without-umfpack
make -j$(nproc)
sudo make install

 

4. GIMP 2.8.16 다운로드 및 압축 해제

cd ..
wget https://mirror.klaus-uwe.me/gimp/pub/gimp/v2.8/gimp-2.8.16.tar.bz2
tar xvf gimp-2.8.16.tar.bz2 && cd gimp-2.8.16/
CC=afl-clang-lto CXX=afl-clang-lto++ PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$HOME/Fuzzing_gimp/gegl-0.2.0/ CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" ./configure --disable-gtktest --disable-glibtest --disable-alsatest --disable-nls --without-libtiff --without-libjpeg --without-bzip2 --without-gs --without-libpng --without-libmng --without-libexif --without-aa --without-libxpm --without-webkit --without-librsvg --without-print --without-poppler --without-cairo-pdf --without-gvfs --without-libcurl --without-wmf --without-libjasper --without-alsa --without-gudev --disable-python --enable-gimp-console --without-mac-twain --without-script-fu --without-gudev --without-dbus --disable-mp --without-linux-input --without-xvfb-run --with-gif-compression=none --without-xmc --with-shm=none --enable-debug  --prefix="$HOME/Fuzzing_gimp/gimp-2.8.16/install"
make -j$(nproc)
make install

Persistent mode

두가지 방법이 있다.

  • app.c 파일을 수정하는 방법
  • AFL_LOOP 매크로를 for 반복 루프 내에 포함시킨다.
if ( filenames ){
gint i;

for(i=0;filenames[i] != NULL; i++){
	if (run_loop){
		#ifdef __AFL_COMPILER
			while(__AFL_LOOP(1000)){
				file_open_from_command_line (gimp, filenames[i], as_new);
			}
			exit(0);

		#else
			file_open_from_command_line (gimp, filenames[i], as_new);
		#endif
		}
  • AFL_LOOP를 xcf_load_invoker 함수 내에 포함시킨다.
--- ../xcf.c	2014-08-20 08:27:58.000000000 -0700
+++ ./app/xcf/xcf.c	2021-10-11 13:02:42.800831192 -0700
@@ -277,6 +277,10 @@
 
   filename = g_value_get_string (&args->values[1]);
 
+#ifdef __AFL_COMPILER
+  while(__AFL_LOOP(10000)){
+#endif
+
   info.fp = g_fopen (filename, "rb");
 
   if (info.fp)
@@ -366,6 +370,12 @@
   if (success)
     gimp_value_set_image (&return_vals->values[1], image);
 
+#ifdef __AFL_COMPILER
+  }
+#endif
+
+  exit(0);
+
   gimp_unset_busy (gimp);
 
   return return_vals;

첫 번째 방법은 다른 입력 형식도 타겟으로 할 수 있지만 두 번째 방법은 xcf만 대상으로 하므로 더 빠르게 버그를 찾을 수 있다.

Seed corpus creation

샘플 xcf 파일을 구해서 AFL input 폴더에 넣는다.

Fuzzing

GIMP 바이너리를 대상으로 한 퍼징이므로 불필요한 플러그인을 제거한다.

rm ./install/lib/gimp/2.0/plug-ins/*

 

Fuzzing!

ASAN_OPTIONS=detect_leaks=0,abort_on_error=1,symbolize=0 afl-fuzz -i './afl_in' -o './afl_out' -D -t 100 -- ./install/bin/gimp-console-2.8 --verbose -d -f @@
  • gimp-console-2.8은 GIMP의 CLI 버전이다.
  • 결정론적 변이 옵션을 허용했다. (-D)
  • 코드에 무한 루프 버그가 있으므로 시간 제한(-t 1000)을 설정해야 한다. 시스템 성능에 따라 다를 수 있기 때문에 적절히 조절한다.

Triage

ASan 트레이스를 지원하므로 크래시가 발생한 파일을 매개변수로 전달하여 실행하면 된다.

반응형