Fuzzing101-Exercise2 fuzz CVE-2009-3895和CVE-2012-2836

autohr: cxing
date: 2023年4月28日

我们将对libexif 0.6.14进行fuzz,目标是复现CVE-2009-3895CVE-2012-2836 两个漏洞。

0x00 准备工作

我们先了解一下libexif这个库和两个CVE漏洞。
关于libexif的信息如下:

  • is a library written in pure portable C.
  • reads and writes EXIF metainformation from and to image files.
  • is licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1 (LGPL).
  • runs under POSIX systems (e.g. GNU/Linux, xBSD, MacOS X, Windows, etc.).

Digital cameras store a surprising amount of information inside each picture they take, in a normally invisible format known as EXIF. Everything from photo basics like the camera shutter speed and aperture to the GPS location and even the name of the camera owner can be hidden inside each photograph. libexif is a library that lets you access that data from within a computer program. Some simple applications called exif and gexif are also supplied alongside libexif that use it to view EXIF data from the command-line or GUI (respectively).

  • 是一个用纯可移植 C 编写的库。
  • 从图像文件读取和写入 EXIF 元信息。
  • 根据 GNU 宽通用公共许可证版本 2.1 (LGPL) 进行许可。
  • 在POSIX系统(例如GNU/Linux,xBSD,MacOS X,Windows等)下运行。

数码相机在每张照片中存储了惊人的信息量 它们以一种通常不可见的格式(称为EXIF)采用。照片中的所有内容 基本功能,例如相机快门速度和 GPS 位置的光圈以及 甚至相机所有者的名字也可以隐藏在每张照片中。利贝西夫 是一个库,可让您从计算机程序内访问该数据。 一些称为 exif 和 gexif 的简单应用程序也与 libexif 一起提供,这些应用程序使用它来查看来自 命令行或 GUI。

阿里云漏洞库上关于CVE-2009-3895的描述如下:
image.png
关于CVE-2012-2836的描述:
image.png

0x01 环境搭建

在你的工作目录下载fuzz目标库,harness,以及种子语料库(seed corpus)。

# fuzz target
wget https://github.com/libexif/libexif/archive/refs/tags/libexif-0_6_14-release.tar.gz
tar -xzvf libexif-0_6_14-release.tar.gz

# harness
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

# seed corpus
wget https://github.com/ianare/exif-samples/archive/refs/heads/master.zip
unzip master.zip

安装必要库并编译一个调试版本。

sudo apt-get install autopoint libtool gettext libpopt-dev

cd libexif-libexif-0_6_14-release/
autoreconf -fvi
CFLAGS="-g -O0" ./configure --enable-shared=no --prefix=$HOME/fuzzing/libexif/dbg_install/
make -j4
make install

cd exif-exif-0_6_15-release/
autoreconf -fvi
CFLAGS="-g -O0" ./configure --enable-shared=no --prefix=$HOME/fuzzing/libexif/dbg_install/ PKG_CONFIG_PATH=$HOME/fuzzing/libexif/dbg_install/lib/pkgconfig
make -j4
make install

编译出一个fuzz版本

cd libexif-libexif-0_6_14-release/
# export LLVM_CONFIG="llvm-config-14" 
CC=afl-clang-lto ./configure --enable-shared=no --prefix=$HOME/fuzzing/libexif/fuzz_install/
make clean
make -j4
make install

cd exif-exif-0_6_15-release/
CC=afl-clang-lto ./configure --enable-shared=no --prefix=$HOME/fuzzing/libexif/fuzz_install/ PKG_CONFIG_PATH=$HOME/fuzzing/libexif/fuzz_install/lib/pkgconfig
make clean
make -j4
make install

注1:export LLVM_CONFIG="llvm-config-14"对于使用官方docker进行fuzz的人来说,这条命令是非必要的,因为官方镜像中已经设置好永久的环境变量了。可以检测自己的环境中是否存在该变量,若没有则需要执行该语句添加环境变量,具体的值需要根据你本机环境中llvm的版本来确定,最好的默认版本,版本至少是llvm-11或更高。
注2:由于是库文件,我们没办法直接输入,因此需要一个harness。假设当前需要对产品进行Fuzzing测试,一般需要一个支持命令行的测试程序,通常称为harness。具体到本案例,你可以简单理解位库函数提供的是接口,而我们需要让接口接入我们的fuzz,即我们需要编写一些代码去调用这些接口,然后我们才能不断的用语料库的输入去测试库函数的接口。这里我们没有使用自己写的harness,而寻找到了一些现场的使用这些接口的应用程序,对他们的测试可以直接测试到libexif,只需要静态编译即可。
注3:如果你不太理解configure文件的参数,可以自行运行-h,里面有详细说明。
注4:我们使用的是afl-clang-lto,afl-clang-lto通常是最好的选择,关于afl-clang-lto的更多信息在官方库有非常详细的介绍,包括其工作原理,具体的开源查阅这里https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.lto.md

0x02 fuzzing

现在我们已经准备就绪,开始fuzz!

afl-fuzz -i $HOME/fuzzing/libexif/exif-samples-master/jpg/ -o $HOME/fuzzing/libexif/fuzz_out/ -s 123 -- $HOME/fuzzing/libexif/fuzz_install/bin/exif @@

注5:-s是设置随机变异种子,该值将影响种子语料的变异,--之后是目标应用程序运行命令,其中@@表示我们的语料库以文件形式输入被应用程序读取。

运行大约十分钟后,我们获得了17个crash和1个hangs。
image.png

0x03 输入样本分析

我们利用调试版本编译的对这些样本进行分析,经过gdb调试分析(也可以利用类似于afl-collect的工具进行自动的分类,但因为crash样本并不多我就直接手动分析了),我们总共分类出五类,其中四类是crash,一类是hangs。
可以看到本质上crash就是两类,分别是最终结果exif_entry_fixexif_data_load_data,而hangs是一个无线递归漏洞。exif_entry_fix处发生的漏洞就是CVE-2009-3895,exif_data_load_data处的漏洞就是CVE-2012-2836。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// crash_1
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   87 exif_get_sshort (const unsigned char *buf, ExifByteOrder order)
   88 {
   89         if (!buf) return 0;
   90         switch (order) {
   91         case EXIF_BYTE_ORDER_MOTOROLA:
 ► 92                 return ((buf[0] << 8) | buf[1]);
   93         case EXIF_BYTE_ORDER_INTEL:
   94                 return ((buf[1] << 8) | buf[0]);
   95         }

#0  0x000055555556de54 in exif_get_sshort (buf=0x555655593e93 <error: Cannot access memory at address 0x555655593e93>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:92
#1  0x000055555556debd in exif_get_short (buf=0x555655593e93 <error: Cannot access memory at address 0x555655593e93>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:104
#2  0x0000555555564dbb in exif_data_load_data (data=0x555555594320, d_orig=0x555555593e90 "Exif", ds_orig=824) at exif-data.c:819
#3  0x000055555556c86f in exif_loader_get_data (loader=0x555555593e40) at exif-loader.c:387
#4  0x000055555556002c in main (argc=2, argv=0x7fffffffdec8) at main.c:438
#5  0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdec8) at ../sysdeps/nptl/libc_start_call_main.h:58
#6  0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdec8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:392
#7  0x000055555555d7a5 in _start ()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// crash_2
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   130 exif_get_slong (const unsigned char *b, ExifByteOrder order)
   131 {
   132         if (!b) return 0;
   133         switch (order) {
   134         case EXIF_BYTE_ORDER_MOTOROLA:
 ► 135                 return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
   136         case EXIF_BYTE_ORDER_INTEL:
   137                 return ((b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]);
   138         }

#0  0x000055555556df97 in exif_get_slong (b=0x5555555b2000 <error: Cannot access memory at address 0x5555555b2000>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:135
#1  0x000055555556e101 in exif_get_long (buf=0x5555555b2000 <error: Cannot access memory at address 0x5555555b2000>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:167
#2  0x0000555555566188 in exif_entry_fix (e=0x5555555965d0) at exif-entry.c:193
#3  0x000055555556288f in fix_func (e=0x5555555965d0, data=0x0) at exif-content.c:231
#4  0x0000555555562726 in exif_content_foreach_entry (content=0x555555594450, func=0x55555556286f <fix_func>, data=0x0) at exif-content.c:200
#5  0x00005555555628eb in exif_content_fix (c=0x555555594450) at exif-content.c:247
#6  0x0000555555565a22 in fix_func (c=0x555555594450, data=0x0) at exif-data.c:1169
#7  0x00005555555655b6 in exif_data_foreach_content (data=0x555555594320, func=0x555555565960 <fix_func>, user_data=0x0) at exif-data.c:1031
#8  0x0000555555565a51 in exif_data_fix (d=0x555555594320) at exif-data.c:1176
#9  0x0000555555565008 in exif_data_load_data (data=0x555555594320, d_orig=0x555555593e90 "Exif", ds_orig=1126) at exif-data.c:871
#10 0x000055555556c86f in exif_loader_get_data (loader=0x555555593e40) at exif-loader.c:387
#11 0x000055555556002c in main (argc=2, argv=0x7fffffffdec8) at main.c:438
#12 0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdec8) at ../sysdeps/nptl/libc_start_call_main.h:58
#13 0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdec8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:392
#14 0x000055555555d7a5 in _start ()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// crash_3
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

───────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────
 ► 0x7ffff7da0ed8 <__memmove_avx_unaligned_erms+1432>    vmovdqu ymm13, ymmword ptr [rsi + 0x3020]
   0x7ffff7da0ee0 <__memmove_avx_unaligned_erms+1440>    vmovdqu ymm14, ymmword ptr [rsi + 0x3040]
   0x7ffff7da0ee8 <__memmove_avx_unaligned_erms+1448>    vmovdqu ymm15, ymmword ptr [rsi + 0x3060]
   0x7ffff7da0ef0 <__memmove_avx_unaligned_erms+1456>    sub    rsi, -0x80
   0x7ffff7da0ef4 <__memmove_avx_unaligned_erms+1460>    vmovntdq ymmword ptr [rdi], ymm0
   0x7ffff7da0ef8 <__memmove_avx_unaligned_erms+1464>    vmovntdq ymmword ptr [rdi + 0x20], ymm1
   0x7ffff7da0efd <__memmove_avx_unaligned_erms+1469>    vmovntdq ymmword ptr [rdi + 0x40], ymm2
   0x7ffff7da0f02 <__memmove_avx_unaligned_erms+1474>    vmovntdq ymmword ptr [rdi + 0x60], ymm3
   0x7ffff7da0f07 <__memmove_avx_unaligned_erms+1479>    vmovntdq ymmword ptr [rdi + 0x1000], ymm4
   0x7ffff7da0f0f <__memmove_avx_unaligned_erms+1487>    vmovntdq ymmword ptr [rdi + 0x1020], ymm5

#0  0x00007ffff7da0ed8 in __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:874
#1  0x0000555555571991 in exif_mnote_data_olympus_load (en=0x555555598c60, buf=0x5555555969b0 "Exif", buf_size=4786) at exif-mnote-data-olympus.c:357
#2  0x000055555556d163 in exif_mnote_data_load (d=0x555555598c60, buf=0x5555555969b0 "Exif", buf_size=4786) at exif-mnote-data.c:84
#3  0x0000555555564fea in exif_data_load_data (data=0x555555593e90, d_orig=0x5555555969b0 "Exif", ds_orig=4786) at exif-data.c:867
#4  0x000055555556c86f in exif_loader_get_data (loader=0x555555593e40) at exif-loader.c:387
#5  0x000055555556002c in main (argc=2, argv=0x7fffffffdec8) at main.c:438
#6  0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdec8) at ../sysdeps/nptl/libc_start_call_main.h:58
#7  0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdec8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:392
#8  0x000055555555d7a5 in _start ()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// crash_4
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
───────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────
 ► 0x7ffff7da0ed0 <__memmove_avx_unaligned_erms+1424>    vmovdqu ymm12, ymmword ptr [rsi + 0x3000]
   0x7ffff7da0ed8 <__memmove_avx_unaligned_erms+1432>    vmovdqu ymm13, ymmword ptr [rsi + 0x3020]
   0x7ffff7da0ee0 <__memmove_avx_unaligned_erms+1440>    vmovdqu ymm14, ymmword ptr [rsi + 0x3040]
   0x7ffff7da0ee8 <__memmove_avx_unaligned_erms+1448>    vmovdqu ymm15, ymmword ptr [rsi + 0x3060]
   0x7ffff7da0ef0 <__memmove_avx_unaligned_erms+1456>    sub    rsi, -0x80

#0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:874
#1  0x00005555555633d9 in exif_data_load_data_thumbnail (data=0x555555593e90, d=0x5555555969b6 "II*", ds=4754, offset=1, size=4294967295) at exif-data.c:292
#2  0x00005555555638c8 in exif_data_load_data_content (data=0x555555593e90, ifd=EXIF_IFD_INTEROPERABILITY, d=0x5555555969b6 "II*", ds=4754, offset=2, recursion_depth=1) at exif-data.c:374
#3  0x0000555555563898 in exif_data_load_data_content (data=0x555555593e90, ifd=EXIF_IFD_0, d=0x5555555969b6 "II*", ds=4754, offset=10, recursion_depth=0) at exif-data.c:369
#4  0x0000555555564d8a in exif_data_load_data (data=0x555555593e90, d_orig=0x5555555969b0 "Exif", ds_orig=4760) at exif-data.c:813
#5  0x000055555556c86f in exif_loader_get_data (loader=0x555555593e40) at exif-loader.c:387
#6  0x000055555556002c in main (argc=2, argv=0x7fffffffdec8) at main.c:438
#7  0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdec8) at ../sysdeps/nptl/libc_start_call_main.h:58
#8  0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdec8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:392
#9  0x000055555555d7a5 in _start ()


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// hang_1
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
In file: /home/cxing/fuzzing/libexif/exif-exif-0_6_15-release/exif/main.c
   190         } while (0)
   191 
   192 static void
   193 log_func_exit (ExifLog *log, ExifLogCode code, const char *domain,
   194                 const char *format, va_list args, void *data)
 ► 195 {
   196         switch (code) {
   197         case -1:
   198                 put_colorstring (stderr, COL_RED);
   199                 vfprintf (stderr, format, args);
   200                 fprintf (stderr, "\n");

pwndbg> bt 20
#0  0x000055555555eeb4 in log_func_exit (log=0x555555593de0, code=EXIF_LOG_CODE_DEBUG, domain=0x555555579b96 "ExifLoader", format=0x555555579ba8 "Scanning %i byte(s) of data...", args=<error reading variable: Cannot access memory at address 0x7fffff7feff8>, data=<error reading variable: Cannot access memory at address 0x7fffff7feff0>) at main.c:195
#1  0x000055555556ccbe in exif_logv (log=0x555555593de0, code=EXIF_LOG_CODE_DEBUG, domain=0x555555579b96 "ExifLoader", format=0x555555579ba8 "Scanning %i byte(s) of data...", args=0x7fffff7ff090) at exif-log.c:147
#2  0x000055555556cc42 in exif_log (log=0x555555593de0, code=EXIF_LOG_CODE_DEBUG, domain=0x555555579b96 "ExifLoader", format=0x555555579ba8 "Scanning %i byte(s) of data...") at exif-log.c:137
#3  0x000055555556c196 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:179
#4  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#5  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#6  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#7  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#8  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#9  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#10 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#11 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#12 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#13 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#14 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#15 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#16 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#17 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#18 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#19 0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
(More stack frames follow...)

#0  exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:151
#1  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#2  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#3  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#4  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd1b0 "", len=0) at exif-loader.c:301
#5  0x000055555556c631 in exif_loader_write (eld=0x555555593e40, buf=0x7fffffffd19c "Ex", '\330' <repeats 17 times>, <incomplete sequence \330>, len=20) at exif-loader.c:301
#6  0x000055555556bf65 in exif_loader_write_file (l=0x555555593e40, path=0x555555593d90 "../../hang_1") at exif-loader.c:120
#7  0x000055555556001d in main (argc=2, argv=0x7fffffffdeb8) at main.c:437
#8  0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdeb8) at ../sysdeps/nptl/libc_start_call_main.h:58
#9  0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdeb8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdea8) at ../csu/libc-start.c:392
#10 0x000055555555d7a5 in _start ()

CVE-2009-3895 crash分析

我们先分析exif_entry_fix导致的CVE-2009-3895。我们看crash_2。根据堆栈回溯#0和#1提示buf/b=0x5555555b2000 <error: Cannot access memory at address 0x5555555b2000>
查看内存布局,显然buf和b本来是堆内存,但是不知道怎么的变成指向堆的边界之外了,我们回到exif_entry_fix查看具体是什么情况。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// crash_2
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   130 exif_get_slong (const unsigned char *b, ExifByteOrder order)
   131 {
   132         if (!b) return 0;
   133         switch (order) {
   134         case EXIF_BYTE_ORDER_MOTOROLA:
 ► 135                 return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
   136         case EXIF_BYTE_ORDER_INTEL:
   137                 return ((b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]);
   138         }

#0  0x000055555556df97 in exif_get_slong (b=0x5555555b2000 <error: Cannot access memory at address 0x5555555b2000>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:135
#1  0x000055555556e101 in exif_get_long (buf=0x5555555b2000 <error: Cannot access memory at address 0x5555555b2000>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:167
#2  0x0000555555566188 in exif_entry_fix (e=0x5555555965d0) at exif-entry.c:193
#3  0x000055555556288f in fix_func (e=0x5555555965d0, data=0x0) at exif-content.c:231
#4  0x0000555555562726 in exif_content_foreach_entry (content=0x555555594450, func=0x55555556286f <fix_func>, data=0x0) at exif-content.c:200
#5  0x00005555555628eb in exif_content_fix (c=0x555555594450) at exif-content.c:247
#6  0x0000555555565a22 in fix_func (c=0x555555594450, data=0x0) at exif-data.c:1169
#7  0x00005555555655b6 in exif_data_foreach_content (data=0x555555594320, func=0x555555565960 <fix_func>, user_data=0x0) at exif-data.c:1031
#8  0x0000555555565a51 in exif_data_fix (d=0x555555594320) at exif-data.c:1176
#9  0x0000555555565008 in exif_data_load_data (data=0x555555594320, d_orig=0x555555593e90 "Exif", ds_orig=1126) at exif-data.c:871
#10 0x000055555556c86f in exif_loader_get_data (loader=0x555555593e40) at exif-loader.c:387
#11 0x000055555556002c in main (argc=2, argv=0x7fffffffdec8) at main.c:438
#12 0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdec8) at ../sysdeps/nptl/libc_start_call_main.h:58
#13 0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdec8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:392
#14 0x000055555555d7a5 in _start ()

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
             Start                End Perm     Size Offset File
    0x555555554000     0x55555555d000 r--p     9000      0 /home/cxing/fuzzing/libexif/dbg_install/bin/exif
    0x55555555d000     0x555555577000 r-xp    1a000   9000 /home/cxing/fuzzing/libexif/dbg_install/bin/exif
    0x555555577000     0x555555585000 r--p     e000  23000 /home/cxing/fuzzing/libexif/dbg_install/bin/exif
    0x555555585000     0x555555586000 r--p     1000  30000 /home/cxing/fuzzing/libexif/dbg_install/bin/exif
    0x555555586000     0x555555590000 rw-p     a000  31000 /home/cxing/fuzzing/libexif/dbg_install/bin/exif
    0x555555590000     0x5555555b2000 rw-p    22000      0 [heap]
    0x7ffff6e00000     0x7ffff7be7000 r--p   de7000      0 /usr/lib/locale/locale-archive
    0x7ffff7c00000     0x7ffff7c28000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7c28000     0x7ffff7dbd000 r-xp   195000  28000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7dbd000     0x7ffff7e15000 r--p    58000 1bd000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7e15000     0x7ffff7e19000 r--p     4000 214000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7e19000     0x7ffff7e1b000 rw-p     2000 218000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7e1b000     0x7ffff7e28000 rw-p     d000      0 [anon_7ffff7e1b]
    0x7ffff7eb1000     0x7ffff7eb4000 rw-p     3000      0 [anon_7ffff7eb1]
    0x7ffff7eb4000     0x7ffff7eb6000 r--p     2000      0 /usr/lib/x86_64-linux-gnu/libpopt.so.0.0.1
    0x7ffff7eb6000     0x7ffff7ebe000 r-xp     8000   2000 /usr/lib/x86_64-linux-gnu/libpopt.so.0.0.1
    0x7ffff7ebe000     0x7ffff7ec0000 r--p     2000   a000 /usr/lib/x86_64-linux-gnu/libpopt.so.0.0.1
    0x7ffff7ec0000     0x7ffff7ec1000 r--p     1000   b000 /usr/lib/x86_64-linux-gnu/libpopt.so.0.0.1
    0x7ffff7ec1000     0x7ffff7ec2000 rw-p     1000   c000 /usr/lib/x86_64-linux-gnu/libpopt.so.0.0.1
    0x7ffff7ec2000     0x7ffff7ed0000 r--p     e000      0 /usr/lib/x86_64-linux-gnu/libm.so.6
    0x7ffff7ed0000     0x7ffff7f4c000 r-xp    7c000   e000 /usr/lib/x86_64-linux-gnu/libm.so.6
    0x7ffff7f4c000     0x7ffff7fa7000 r--p    5b000  8a000 /usr/lib/x86_64-linux-gnu/libm.so.6
    0x7ffff7fa7000     0x7ffff7fa8000 r--p     1000  e4000 /usr/lib/x86_64-linux-gnu/libm.so.6
    0x7ffff7fa8000     0x7ffff7fa9000 rw-p     1000  e5000 /usr/lib/x86_64-linux-gnu/libm.so.6
    0x7ffff7fbb000     0x7ffff7fbd000 rw-p     2000      0 [anon_7ffff7fbb]
    0x7ffff7fbd000     0x7ffff7fc1000 r--p     4000      0 [vvar]
    0x7ffff7fc1000     0x7ffff7fc3000 r-xp     2000      0 [vdso]
    0x7ffff7fc3000     0x7ffff7fc5000 r--p     2000      0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fc5000     0x7ffff7fef000 r-xp    2a000   2000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fef000     0x7ffff7ffa000 r--p     b000  2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffb000     0x7ffff7ffd000 r--p     2000  37000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffd000     0x7ffff7fff000 rw-p     2000  39000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp     1000      0 [vsyscall]
pwndbg> x/16gx 0x5555555b1FF0
0x5555555b1ff0: 0x0000000000000000      0x0000000000000000
0x5555555b2000: Cannot access memory at address 0x5555555b2000
pwndbg> 

exif_entry_fix调用exif_get_long代码如下。

void
exif_entry_fix (ExifEntry *e)
{
	unsigned int i;
	ExifByteOrder o;
	ExifRational r;
	ExifSRational sr;

	if (!e || !e->priv) return;

	switch (e->tag) {
	
	/* These tags all need to be of format SHORT. */
    case xxx: // skip many case
	case EXIF_TAG_SHARPNESS:
		switch (e->format) {
		case EXIF_FORMAT_LONG:
			if (!e->parent || !e->parent->parent) break;
			o = exif_data_get_byte_order (e->parent->parent);
			for (i = 0; i < e->components; i++)
				exif_set_short (
					e->data + i *
					exif_format_get_size (
					EXIF_FORMAT_SHORT), o,
					(ExifShort) exif_get_long ( // here
					e->data + i *
					exif_format_get_size (
					EXIF_FORMAT_LONG), o));
			e->format = EXIF_FORMAT_SHORT;
			e->size = e->components *
				exif_format_get_size (e->format);
			e->data = exif_entry_realloc (e, e->data, e->size);
			exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
				_("Tag '%s' was of format '%s' (which is "
				"against specification) and has been "
				"changed to format '%s'."),
				exif_tag_get_name (e->tag), 
				exif_format_get_name (EXIF_FORMAT_LONG),
				exif_format_get_name (EXIF_FORMAT_SHORT));
			break;
		case EXIF_FORMAT_SHORT:
		default:
			break;
		}
		break;
// .... skip 

我们发现exif_get_long的第一个参数buf,是由这样一个算式决定的e->data + i* exif_format_get_size (EXIF_FORMAT_LONG)决定的。若e->data在堆的范围中是正常的那么便是后面加上的i *exif_format_get_size (EXIF_FORMAT_LONG)的值太大,以至于使得整个算式的结果太大越过堆的边界,所以我们关注到底是i的值太大还是exif_format_get_size (EXIF_FORMAT_LONG)返回的值太大,其中EXIF_FORMAT_LONG是一个等于4的常量,因此按理来说问题应该出在i上。我们在gdb中严重发现结果果然如此,如下框。
exif_entry_fix的代码有这样一句,e->size = e->components * exif_format_get_size (e->format);,从语义上理解,很自然的能够知道data的边界显然被size所规定,而该语句也说明正常情况下 e->components * var不应该超过size。

In file: /home/cxing/fuzzing/libexif/libexif-0_6_14-release/libexif/exif-entry.c
   188                         for (i = 0; i < e->components; i++)
   189                                 exif_set_short (
   190                                         e->data + i *
   191                                         exif_format_get_size (
   192                                         EXIF_FORMAT_SHORT), o,
 ► 193                                         (ExifShort) exif_get_long (
   194                                         e->data + i *
   195                                         exif_format_get_size (
   196                                         EXIF_FORMAT_LONG), o));
   197                         e->format = EXIF_FORMAT_SHORT;
   198                         e->size = e->components *
──────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd390 —▸ 0x7fffffffd490 —▸ 0x7fffffffd4b0 —▸ 0x7fffffffd4f0 —▸ 0x7fffffffd510 ◂— ...
01:0008│     0x7fffffffd398 —▸ 0x5555555965d0 ◂— 0x40000a401
02:0010│     0x7fffffffd3a0 —▸ 0x555555578690 ◂— 'No thumbnail but entries on thumbnail. These entries have been removed.'
03:0018│     0x7fffffffd3a8 ◂— 0x0
04:0020│     0x7fffffffd3b0 ◂— 0x100000000
05:0028│     0x7fffffffd3b8 —▸ 0x555555593de0 ◂— 0x8
06:0030│ r8  0x7fffffffd3c0 ◂— 0x3000000020 /* ' ' */
07:0038│     0x7fffffffd3c8 ◂— 0x0
────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0   0x55555556617b exif_entry_fix+460
   f 1   0x55555556288f fix_func+32
   f 2   0x555555562726 exif_content_foreach_entry+83
   f 3   0x5555555628eb exif_content_fix+89
   f 4   0x555555565a22 fix_func+194
   f 5   0x5555555655b6 exif_data_foreach_content+74
   f 6   0x555555565a51 exif_data_fix+43
   f 7   0x555555565008 exif_data_load_data+1926
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> bt
#0  exif_entry_fix (e=0x5555555965d0) at exif-entry.c:193
#1  0x000055555556288f in fix_func (e=0x5555555965d0, data=0x0) at exif-content.c:231
#2  0x0000555555562726 in exif_content_foreach_entry (content=0x555555594450, func=0x55555556286f <fix_func>, data=0x0) at exif-content.c:200
#3  0x00005555555628eb in exif_content_fix (c=0x555555594450) at exif-content.c:247
#4  0x0000555555565a22 in fix_func (c=0x555555594450, data=0x0) at exif-data.c:1169
#5  0x00005555555655b6 in exif_data_foreach_content (data=0x555555594320, func=0x555555565960 <fix_func>, user_data=0x0) at exif-data.c:1031
#6  0x0000555555565a51 in exif_data_fix (d=0x555555594320) at exif-data.c:1176
#7  0x0000555555565008 in exif_data_load_data (data=0x555555594320, d_orig=0x555555593e90 "Exif", ds_orig=1126) at exif-data.c:871
#8  0x000055555556c86f in exif_loader_get_data (loader=0x555555593e40) at exif-loader.c:387
#9  0x000055555556002c in main (argc=2, argv=0x7fffffffdeb8) at main.c:438
#10 0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdeb8) at ../sysdeps/nptl/libc_start_call_main.h:58
#11 0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdeb8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdea8) at ../csu/libc-start.c:392
#12 0x000055555555d7a5 in _start ()
pwndbg> p e->data
$6 = (unsigned char *) 0x555555596630 "\033"
pwndbg> p e->components 
$7 = 2147483899
pwndbg> python-interactive 
>>> hex(2147483899)
'0x800000fb'
>>> 
pwndbg> p *e
$1 = {
  tag = EXIF_TAG_CUSTOM_RENDERED,
  format = EXIF_FORMAT_LONG,
  components = 2147483899,
  data = 0x555555596630 "\033",
  size = 1004,
  parent = 0x555555594450,
  priv = 0x555555596610
}

我们直接对e->components下内存断点追踪,我们追踪到如下这里。下面第164行entry->components = exif_get_long (d + offset + 4, data->priv->order);就是玩我们追踪的components,我们查阅其值,显然是已经写入了过大的值。
显然问题出在exif_get_long (d + offset + 4, data->priv->order)上,我们查看d发现其就是图像文件中exif部分的data部分。

─────────────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────────────────────────────────────────────
 ► 0x555555562d92 <exif_data_load_data_entry+158>    mov    rax, qword ptr [rbp - 0x20]
   0x555555562d96 <exif_data_load_data_entry+162>    mov    eax, dword ptr [rax]
   0x555555562d98 <exif_data_load_data_entry+164>    mov    edi, eax
   0x555555562d9a <exif_data_load_data_entry+166>    call   exif_tag_get_name                <exif_tag_get_name>
 
   0x555555562d9f <exif_data_load_data_entry+171>    mov    rdx, rax
   0x555555562da2 <exif_data_load_data_entry+174>    mov    rax, qword ptr [rbp - 0x20]
   0x555555562da6 <exif_data_load_data_entry+178>    mov    ecx, dword ptr [rax]
   0x555555562da8 <exif_data_load_data_entry+180>    mov    rax, qword ptr [rbp - 0x18]
   0x555555562dac <exif_data_load_data_entry+184>    mov    rax, qword ptr [rax + 0x38]
   0x555555562db0 <exif_data_load_data_entry+188>    mov    rax, qword ptr [rax + 0x10]
   0x555555562db4 <exif_data_load_data_entry+192>    mov    r9, rdx
──────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────────────────────────────────────────────────────────
In file: /home/cxing/fuzzing/libexif/libexif-0_6_14-release/libexif/exif-data.c
   161 
   162         entry->tag        = exif_get_short (d + offset + 0, data->priv->order);
   163         entry->format     = exif_get_short (d + offset + 2, data->priv->order);
   164         entry->components = exif_get_long  (d + offset + 4, data->priv->order);
   165 
 ► 166         exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
   167                   "Loading entry 0x%x ('%s')...", entry->tag,
   168                   exif_tag_get_name (entry->tag));
   169 
   170         /*
   171          * Size? If bigger than 4 bytes, the actual data is not
──────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd440 ◂— 0x46000000274
01:0008│     0x7fffffffd448 —▸ 0x555555593e96 ◂— 0x80000002a004d4d /* 'MM' */
02:0010│     0x7fffffffd450 —▸ 0x5555555965d0 ◂— 0x40000a401
03:0018│     0x7fffffffd458 —▸ 0x555555594320 —▸ 0x5555555943b0 —▸ 0x555555594820 —▸ 0x555555594540 ◂— ...
04:0020│     0x7fffffffd460 ◂— 0xffffd4c0
05:0028│     0x7fffffffd468 —▸ 0x5555555965d0 ◂— 0x40000a401
06:0030│ rbp 0x7fffffffd470 —▸ 0x7fffffffd4c0 —▸ 0x7fffffffd510 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 ◂— ...
07:0038│     0x7fffffffd478 —▸ 0x555555563a48 (exif_data_load_data_content+1642) ◂— test eax, eax
────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0   0x555555562d92 exif_data_load_data_entry+158
   f 1   0x555555563a48 exif_data_load_data_content+1642
   f 2   0x555555563706 exif_data_load_data_content+808
   f 3   0x555555564d8a exif_data_load_data+1288
   f 4   0x55555556c86f exif_loader_get_data+103
   f 5   0x55555556002c main+2489
   f 6   0x7ffff7c29d90 __libc_start_call_main+128
   f 7   0x7ffff7c29e40 __libc_start_main+128
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> p entry->components
$12 = 2147483899
pwndbg> x/16gs d
warning: Unable to display strings with size 'g', using 'b' instead.
0x555555593e96: "MM"
0x555555593e99: "*"
0x555555593e9b: ""
0x555555593e9c: ""
0x555555593e9d: "\b"
0x555555593e9f: "\f\001\017"
0x555555593ea3: "\002"
0x555555593ea5: ""
0x555555593ea6: ""
0x555555593ea7: "\t"
0x555555593ea9: ""
0x555555593eaa: ""
0x555555593eab: "\236\001\020"
0x555555593eaf: "\002\362\377"
0x555555593eb3: "\020"
0x555555593eb5: ""

下图中crash_2 Exif...MM即d所指向的部分。
image.png
image.png
显然由于其没有对Exif data中字段严格的校验,使得我们可以通过精心构造的exif data指定一个巨大的components,使得程序在读写data时发生越界,而data本身在堆上也就出现了堆溢出,也就是说可能存在堆上的越界读写行为,将有可能造成远程代码执行。具体的,我们将不再分析。

CVE-2009-3895 修复建议

我们知道由于对 exif结构中字段的解析没有严格按照去语义进行,即通过component读取data时不能超过size指定的边界,因此应该在写入Components时检测components的值,若存在可能超过size边界,则立刻抛出异常,终止解析跳过该部分。具体的修复代码我个人就不动手了,相信看完我的分析对诸位读者来说已经是小菜一碟了。

CVE-2012-2836 crash分析

先来看一下crash_1的堆栈回溯。我们通用可以看到堆栈回溯中出现了这样的提醒buf=0x555655593e93 <error: Cannot access memory at address 0x555655593e93>,这显然又是堆空间发生了越界读写现象,读写到了非法内存地址导致程序崩溃。我们从函数参数都正常的exif_data_load_data开始分析。

cxing@cxing-virtual-machine:~/fuzzing/libexif/dbg_install/bin$ gdb --args ./exif ../../crash_1

------- tip of the day (disable with set show-tips off) -------
Pwndbg resolves kernel memory maps by parsing page tables (default) or via monitor info mem QEMU gdbstub command (use set kernel-vmmap-via-page-tables off for that)
pwndbg> r
Starting program: /home/cxing/fuzzing/libexif/dbg_install/bin/exif ../../crash_1
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x000055555556de54 in exif_get_sshort (buf=0x555655593e93 <error: Cannot access memory at address 0x555655593e93>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:92
92                      return ((buf[0] << 8) | buf[1]);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────────────────────────────────────────────
*RAX  0x555655593e93
 RBX  0x0
*RCX  0x100000003
 RDX  0x0
*RDI  0x555655593e93
 RSI  0x0
*R8   0xfffffffd
 R9   0x0
*R10  0x55555555ee99 (log_func_exit) ◂— endbr64 
*R11  0x7ffff7e19ce0 (main_arena+96) —▸ 0x555555595990 ◂— 0x0
*R12  0x7fffffffdeb8 —▸ 0x7fffffffe1d2 ◂— '/home/cxing/fuzzing/libexif/dbg_install/bin/exif'
*R13  0x55555555f673 (main) ◂— endbr64 
*R14  0x555555585bd8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x55555555d820 (__do_global_dtors_aux) ◂— endbr64 
*R15  0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
*RBP  0x7fffffffd4f0 —▸ 0x7fffffffd510 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 —▸ 0x7fffffffdda0 ◂— ...
*RSP  0x7fffffffd4f0 —▸ 0x7fffffffd510 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 —▸ 0x7fffffffdda0 ◂— ...
*RIP  0x55555556de54 (exif_get_sshort+47) ◂— movzx eax, byte ptr [rax]
─────────────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────────────────────────────────────────────
 ► 0x55555556de54 <exif_get_sshort+47>     movzx  eax, byte ptr [rax]
   0x55555556de57 <exif_get_sshort+50>     movzx  eax, al
   0x55555556de5a <exif_get_sshort+53>     shl    eax, 8
   0x55555556de5d <exif_get_sshort+56>     mov    edx, eax
   0x55555556de5f <exif_get_sshort+58>     mov    rax, qword ptr [rbp - 8]
   0x55555556de63 <exif_get_sshort+62>     add    rax, 1
   0x55555556de67 <exif_get_sshort+66>     movzx  eax, byte ptr [rax]
   0x55555556de6a <exif_get_sshort+69>     movzx  eax, al
   0x55555556de6d <exif_get_sshort+72>     or     eax, edx
   0x55555556de6f <exif_get_sshort+74>     jmp    exif_get_sshort+114                <exif_get_sshort+114>
    ↓
   0x55555556de97 <exif_get_sshort+114>    pop    rbp
──────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────────────────────────────────────────────────────────
In file: /home/cxing/fuzzing/libexif/libexif-0_6_14-release/libexif/exif-utils.c
   87 exif_get_sshort (const unsigned char *buf, ExifByteOrder order)
   88 {
   89         if (!buf) return 0;
   90         switch (order) {
   91         case EXIF_BYTE_ORDER_MOTOROLA:
 ► 92                 return ((buf[0] << 8) | buf[1]);
   93         case EXIF_BYTE_ORDER_INTEL:
   94                 return ((buf[1] << 8) | buf[0]);
   95         }
   96 
   97         /* Won't be reached */
──────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rbp rsp 0x7fffffffd4f0 —▸ 0x7fffffffd510 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 —▸ 0x7fffffffdda0 ◂— ...
01:0008│         0x7fffffffd4f8 —▸ 0x55555556debd (exif_get_short+36) ◂— leave 
02:0010│         0x7fffffffd500 ◂— 0x5555f673
03:0018│         0x7fffffffd508 ◂— 0x555655593e93
04:0020│         0x7fffffffd510 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 —▸ 0x7fffffffdda0 ◂— 0x2
05:0028│         0x7fffffffd518 —▸ 0x555555564dbb (exif_data_load_data+1337) ◂— mov word ptr [rbp - 0x2a], ax
06:0030│         0x7fffffffd520 —▸ 0x7fffffffd540 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 —▸ 0x7fffffffdda0 ◂— ...
07:0038│         0x7fffffffd528 ◂— 0x338555627bd
────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0   0x55555556de54 exif_get_sshort+47
   f 1   0x55555556debd exif_get_short+36
   f 2   0x555555564dbb exif_data_load_data+1337
   f 3   0x55555556c86f exif_loader_get_data+103
   f 4   0x55555556002c main+2489
   f 5   0x7ffff7c29d90 __libc_start_call_main+128
   f 6   0x7ffff7c29e40 __libc_start_main+128
   f 7   0x55555555d7a5 _start+37
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> bt
#0  0x000055555556de54 in exif_get_sshort (buf=0x555655593e93 <error: Cannot access memory at address 0x555655593e93>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:92
#1  0x000055555556debd in exif_get_short (buf=0x555655593e93 <error: Cannot access memory at address 0x555655593e93>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:104
#2  0x0000555555564dbb in exif_data_load_data (data=0x555555594320, d_orig=0x555555593e90 "Exif", ds_orig=824) at exif-data.c:819
#3  0x000055555556c86f in exif_loader_get_data (loader=0x555555593e40) at exif-loader.c:387
#4  0x000055555556002c in main (argc=2, argv=0x7fffffffdeb8) at main.c:438
#5  0x00007ffff7c29d90 in __libc_start_call_main (main=main@entry=0x55555555f673 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdeb8) at ../sysdeps/nptl/libc_start_call_main.h:58
#6  0x00007ffff7c29e40 in __libc_start_main_impl (main=0x55555555f673 <main>, argc=2, argv=0x7fffffffdeb8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdea8) at ../csu/libc-start.c:392
#7  0x000055555555d7a5 in _start ()
pwndbg> 

显然在819行 n = exif_get_short (d + 6 + offset, data->priv->order);语句执行完毕后,其中第一个参数指向了非常内存地址,即d + 6 + offset这算式存在问题。从gdb中可以看见,d是指向exif结构的基地址,是正常的那么问题就出来了offset上,我们看到他的值为4294967293太大了,显然是不正常的。我们需要寻找offset赋值处。
我们在gdb中向上翻阅代码l -20发现这样一条语句offset = exif_get_long (d + 10, data->priv->order);,这时从d + 10处取4字节转为long类型赋值给offset。我们还发现了对offset字段的检查offset + 6 + 2 > ds。注意offset是uint32_t类型,也就是说我们只需要将offset + 8溢出为0,那么必然可以通过检查,所以offset的值在[-8, 0)这个区间,必然发生溢出,通过检查。

In file: /home/cxing/fuzzing/libexif/libexif-0_6_14-release/libexif/exif-data.c
   814 
   815         /* IFD 1 offset */
   816         if (offset + 6 + 2 > ds) {
   817                 return;
   818         }
 ► 819         n = exif_get_short (d + 6 + offset, data->priv->order);
   820         if (offset + 6 + 2 + 12 * n + 4 > ds) {
   821                 return;
   822         }
   823         offset = exif_get_long (d + 6 + offset + 2 + 12 * n, data->priv->order);
   824         if (offset) {
──────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd520 —▸ 0x7fffffffd540 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 —▸ 0x7fffffffdda0 ◂— ...
01:0008│     0x7fffffffd528 ◂— 0x338555627bd
02:0010│     0x7fffffffd530 —▸ 0x555555593e90 ◂— 0x4d4d000066697845 /* 'Exif' */
03:0018│     0x7fffffffd538 —▸ 0x555555594320 —▸ 0x5555555943b0 ◂— 0x0
04:0020│     0x7fffffffd540 —▸ 0x7fffffffd570 —▸ 0x7fffffffd5a0 —▸ 0x7fffffffdda0 ◂— 0x2
05:0028│     0x7fffffffd548 ◂— 0x555500000338
06:0030│     0x7fffffffd550 ◂— 0xfffffffd55593de0
07:0038│     0x7fffffffd558 —▸ 0x555555593e90 ◂— 0x4d4d000066697845 /* 'Exif' */
────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0   0x555555564d99 exif_data_load_data+1303
   f 1   0x55555556c86f exif_loader_get_data+103
   f 2   0x55555556002c main+2489
   f 3   0x7ffff7c29d90 __libc_start_call_main+128
   f 4   0x7ffff7c29e40 __libc_start_main+128
   f 5   0x55555555d7a5 _start+37
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> p d
$3 = (const unsigned char *) 0x555555593e90 "Exif"
pwndbg> p offset
$2 = 4294967293
pwndbg> bt
pwndbg> l -20
804             if (exif_get_short (d + 8, data->priv->order) != 0x002a)
805                     return;
806
807             /* IFD 0 offset */
808             offset = exif_get_long (d + 10, data->priv->order);
809             exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", 
810                       "IFD 0 at %i.", (int) offset);
811
812             /* Parse the actual exif data (usually offset 14 from start) */
813             exif_data_load_data_content (data, EXIF_IFD_0, d + 6, ds - 6, offset, 0);
pwndbg> 

那么到此为止,我们分析完了两个CVE的成因。

CVE-2012-2836 修复建议

我们只需要检查offset的值是否在[-8, 0)这个溢出区间即可,包括后续又几个对offset的检查,我们可以适当扩大一些,一个字节即[-255,0)区间的检查。

尾声

在gdb上调试分析的体验比Windows上差太多了,没有像x64dbg和od这样方便的图形化工具,还是有些麻烦的。

posted @ 2023-04-28 17:55  辰星-cxing  阅读(592)  评论(0编辑  收藏  举报