app/xcf/xcf-load.c 파일의 xcf_load_image 함수에서 발생한 UAF 취약점입니다.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
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
./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
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
--- ../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;
patch ./app/xcf/xcf.c -i persistent.patch
mkdir afl_in && cd afl_in
wget https://github.com/antonio-morales/Fuzzing101/blob/main/Exercise%206/SampleInput.xcf
rm ./install/lib/gimp/2.0/plug-ins/*
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 @@
rm ./install/lib/gimp/2.0/plug-ins/*

| Bug 767873 – Multiple Use-After-Free when parsing XCF channel and layer properties
**pwndbg> bt
#0 0x00007ffff6c50295 in () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#1 0x00007ffff6c51579 in g_logv () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#2 0x00007ffff6c51743 in g_log () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#3 0x00005555558e9687 in gimp_image_set_active_layer (image=0x6250001375e0, layer=0x625000139270) at gimpimage.c:3410
#4 0x00005555556ce986 in xcf_load_image (gimp=0x629000006030, info=0x7fffffffc870, error=0x7fffffffd290) at xcf-load.c:373
#5 0x00005555556ccf21 in xcf_load_invoker (procedure=0x625000006180, gimp=0x629000006030, context=0x625000137280, progress=0x0, args=0x619000487300, error=0x7fffffffd290) at xcf.c:335
#6 0x00005555557a91df in gimp_procedure_real_execute (procedure=0x625000006180, gimp=0x629000006030, context=0x625000137280, progress=0x0, args=0x619000487300, error=0x7fffffffd290) at gimpprocedure.c:169
#7 0x00005555557ca99a in gimp_plug_in_procedure_execute (procedure=0x625000006180, gimp=0x629000006030, context=0x625000137280, progress=0x0, args=0x619000487300, error=0x7fffffffd290) at gimppluginprocedure.c:204
#8 0x00005555557aa5b7 in gimp_procedure_execute (procedure=0x625000006180, gimp=0x629000006030, context=0x625000137280, progress=0x0, args=0x619000487300, error=0x7fffffffd290) at gimpprocedure.c:331
#9 0x000055555579e403 in gimp_pdb_execute_procedure_by_name_args (pdb=0x61d000005420, context=0x62500000d230, progress=0x0, error=0x7fffffffd290, name=0x555555af64a0 "gimp-xcf-load", args=0x619000487300) at gimppdb.c:331
#10 0x000055555579f8ba in gimp_pdb_execute_procedure_by_name (pdb=0x61d000005420, context=0x62500000d230, progress=0x0, error=0x7fffffffd290, name=0x555555af64a0 "gimp-xcf-load") at gimppdb.c:459
#11 0x00005555559d2cc7 in file_open_image (gimp=0x629000006030, context=0x62500000d230, progress=0x0, uri=0x60600008fa20 "file:///home/ion/Fuzzing_gimp/gimp-2.8.16/crash/uaf.xcf", entered_filename=0x60600008fa20 "file:///home/ion/Fuzzing_gimp/gimp-2.8.16/crash/uaf.xcf", as_new=0, file_proc=0x625000006180, run_mode=GIMP_RUN_INTERACTIVE, status=0x7fffffffd280, mime_type=0x7fffffffd120, error=0x7fffffffd290) at file-open.c:158
#12 0x00005555559d453d in file_open_with_proc_and_display (gimp=0x629000006030, context=0x62500000d230, progress=0x0, uri=0x60600008fa20 "file:///home/ion/Fuzzing_gimp/gimp-2.8.16/crash/uaf.xcf", entered_filename=0x60600008fa20 "file:///home/ion/Fuzzing_gimp/gimp-2.8.16/crash/uaf.xcf", as_new=0, file_proc=0x0, status=0x7fffffffd280, error=0x7fffffffd290) at file-open.c:396
#13 0x00005555559d40ce in file_open_with_display (gimp=0x629000006030, context=0x62500000d230, progress=0x0, uri=0x60600008fa20 "file:///home/ion/Fuzzing_gimp/gimp-2.8.16/crash/uaf.xcf", as_new=0, status=0x7fffffffd280, error=0x7fffffffd290) at file-open.c:372
#14 0x00005555559d5545 in file_open_from_command_line (gimp=0x629000006030, filename=0x6060000015e0 "/home/ion/Fuzzing_gimp/gimp-2.8.16/crash/uaf.xcf", as_new=0) at file-open.c:575
#15 0x00005555556c6bf5 in app_run (full_prog_name=0x606000001520 "/home/ion/Fuzzing_gimp/gimp-2.8.16/debug/bin/gimp-console-2.8", filenames=0x602000001470, alternate_system_gimprc=0x0, alternate_gimprc=0x0, session_name=0x0, batch_interpreter=0x0, batch_commands=0x0, as_new=0, no_interface=1, no_data=0, no_fonts=0, no_splash=0, be_verbose=0, use_shm=0, use_cpu_accel=1, console_messages=0, use_debug_handler=0, stack_trace_mode=GIMP_STACK_TRACE_NEVER, pdb_compat_mode=GIMP_PDB_COMPAT_ON) at app.c:253
#16 0x00005555556cb961 in main (argc=2, argv=0x603000000a00) at main.c:488
#17 0x00007ffff68b6083 in __libc_start_main (main=0x5555556cb2c1 <main>, argc=2, argv=0x7fffffffd6d8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffd6c8) at ../csu/libc-start.c:308
#18 0x00005555556c65fe in _start ()**
xcf_load_image 함수에서 취약점이 발생했습니다.(gimp-console-2.8:3017): Gimp-Core-[1;35mCRITICAL[0m **: [34m02:29:03.282[0m: gimp_image_set_active_layer: assertion 'layer == NULL || GIMP_IS_LAYER (layer)' failed
layer 변수에서 문제가 발생한거 같습니다.GimpLayer *
gimp_image_set_active_layer (GimpImage *image,
GimpLayer *layer)
{
GimpImagePrivate *private;
GimpLayer *floating_sel;
GimpLayer *active_layer;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), NULL);
g_return_val_if_fail (layer == NULL ||
(gimp_item_is_attached (GIMP_ITEM (layer)) &&
gimp_item_get_image (GIMP_ITEM (layer)) == image),
NULL);
gimp_image_set_active_layer 함수를 봐보면 에러 메시지와 동일한 코드가 보입니다. if (info->active_layer)
gimp_image_set_active_layer (image, info->active_layer);
layer 변수 자리에 info->active_layer 가 전달되는데, 해당 변수에서 UAF 취약점이 발생한거 같습니다.➡️ 해당 변수를 참고해서 UAF 취약점 분석을 하려고 했는데, ASAN 로그가 없어서 분석이 쉽지 않았습니다. 그래서 취약점에 대한 상세한 설명을 참고하여 분석을 진행하였습니다.
Created attachment 330078 [details] [review]
Patch
The properties PROP_ACTIVE_LAYER, PROP_FLOATING_SELECTION, PROP_ACTIVE_CHANNEL saves the current object pointer the @info structure. Others like PROP_SELECTION (for channel) and PROP_GROUP_ITEM (for layer) will delete the current object and create a new object, leaving the pointers in @info invalid (dangling).
Therefore, if a property from the first type will come before the second, the result will be an UaF in the last lines of xcf_load_image (when it actually using the pointers from @info).
I wasn't able to exploit this bug because that g_type_instance->c_class gets cleared by the last g_object_unref and GIMP_IS_{LAYER,CHANNEL} detects that and return FALSE. It isn't possible to put a fake a GTypeInstanceClass and put it instead because that the GType is kind of random (based on the address of the GTypeInstanceClass which is the return value of g_malloc).
I have attached a patch that should fix that and a XCF file that will trigger this bug.
Example for running gimp_uaf.xcf on master:
$ gimp Gimp_UaF.xcf
This is a development version of GIMP. Debug messages may appear here.
...
(gimp:18010): Gimp-Core-CRITICAL **: gimp_image_set_active_layer: assertion 'layer == NULL || GIMP_IS_LAYER (layer)' failed
layer 에서 취약점이 발생했습니다.layer 부분 설명을 참고하면 될거 같습니다.PROP_ACTIVE_LAYER 에서 info 구조체에 현재 객체 포인터를 저장하고, PROP_GROUP_ITEM 에서 현재 객체를 제거하고 새로운 객체를 생성합니다.info 구조체에 저장된 layer 객체가 dangling pointer가 됩니다.gimp_image_set_active_layer 함수 호출시 인자로 info->active_layer 가 전달되게 되는데, 이때 UAF 취약점이 발생합니다.
struct _XcfInfo
{
Gimp *gimp;
GimpProgress *progress;
FILE *fp;
guint cp;
const gchar *filename;
GimpTattoo tattoo_state;
GimpLayer *active_layer;
GimpChannel *active_channel;
GimpDrawable *floating_sel_drawable;
GimpLayer *floating_sel;
guint floating_sel_offset;
gint swap_num;
gint *ref_count;
XcfCompressionType compression;
gint file_version;
};
case PROP_ACTIVE_LAYER:
info->active_layer = *layer;
break;
info->active_layer 에 현재 layer 저장case PROP_GROUP_ITEM:
{
GimpLayer *group;
// 새로운 그룹 레이어 생성
group = gimp_group_layer_new (image);
// 새 그룹 레이어의 이름을 기존 레이어의 이름으로 설정
gimp_object_set_name (GIMP_OBJECT (group),
gimp_object_get_name (*layer));
// 새 그룹 레이어의 타입을 기존 레이어의 타입으로 설정
GIMP_DRAWABLE (group)->private->type =
gimp_drawable_type (GIMP_DRAWABLE (*layer));
// 기존 레이어의 참조 카운트 증가
g_object_ref_sink (*layer);
// 기존 레이어의 참조 해제
g_object_unref (*layer);
// 레이어 포인터를 새 그룹 레이어로 업데이트
*layer = group;
}
break;
layer 를 초기화info->active_layer 는 dangling pointer가 됩니다. if (info->active_layer)
gimp_image_set_active_layer (image, info->active_layer);
info->active_layer 가 전달되는데, 해당 변수는 dangling pointer 이기 때문에 UAF 취약점 발생위의 과정이 이루어지는 과정을 디버깅을 통해 살펴보겠습니다.
이를 위해 디버깅용 환경변수를 설정해줘야 합니다.

G_DEBUG=fatal-warnings 라는 환경변수를 추가해줍니다.

xcf_load_layer 함수로 레이어를 로드하기 전에는 layer 변수 값이 0x0 입니다.
gimp_layer_new 함수로 새로운 레이어를 생성하여 layer 변수에 대입해줬기 때문에, layer 변수 값이 세팅되었습니다.xcf_load_layer_props 함수에서 레이어 프로퍼티를 읽어오게 됩니다.PROP_ACTIVE_LAYER

0x0 이었던 info->active_layer 값이
layer 값으로 세팅됩니다.PROP_GROUP_ITEM
이후 layer 와 info->active_layer 값을 살펴보면

layer 변수에는 새로 생성된 레이어 값이 들어가 있습니다.info->active_layer 값은 그대로 입니다.
gimp_image_set_active_layer(image, info->active_layer) 에서 dnagling pointer에 접근하게 되어 UAF 취약점이 발생합니다.
g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), NULL); 코드 부분이 실행되면새로운 레이어 생성 후 layer 변수 값을 초기화할 때, info->active_layer 도 dangling pointer가 되지 않도록 NULL 로 초기화 해줘야 합니다.
case PROP_GROUP_ITEM:
{
GimpLayer *group;
gboolean is_active_layer;
/* We're going to delete *layer, Don't leave its pointers
* in @info. After that, we'll restore them back with the
* new pointer. See bug #767873.
*/
is_active_layer = (*layer == info->active_layer);
if (is_active_layer)
info->active_layer = NULL;
if (*layer == info->floating_sel)
info->floating_sel = NULL;
group = gimp_group_layer_new (image);
g_object_ref_sink (*layer);
g_object_unref (*layer);
*layer = group;
if (is_active_layer)
info->active_layer = *layer;
/* Don't restore info->floating_sel because group layers
* can't be floating selections
*/
}
break;
{
GimpChannel *mask;
/* We're going to delete *channel, Don't leave its pointer
* in @info. See bug #767873.
*/
if (*channel == info->active_channel)
info->active_channel = NULL;
mask =
gimp_selection_new (image,
gimp_item_get_width (GIMP_ITEM (*channel)),
*channel = mask;
(*channel)->boundary_known = FALSE;
(*channel)->bounds_known = FALSE;
/* Don't restore info->active_channel because the
* selection can't be the active channel
*/
}
break;
| Bug 767873 - (CVE-2016-4994) Multiple Use-After-Free when parsing...