Fuzzing101-Exercise2 fuzz CVE-2009-3895和CVE-2012-2836
autohr: cxing
date: 2023年4月28日
我们将对libexif 0.6.14进行fuzz,目标是复现CVE-2009-3895 和CVE-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的描述如下:
关于CVE-2012-2836的描述:
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。
0x03 输入样本分析
我们利用调试版本编译的对这些样本进行分析,经过gdb调试分析(也可以利用类似于afl-collect的工具进行自动的分类,但因为crash样本并不多我就直接手动分析了),我们总共分类出五类,其中四类是crash,一类是hangs。
可以看到本质上crash就是两类,分别是最终结果exif_entry_fix
和exif_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所指向的部分。
显然由于其没有对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这样方便的图形化工具,还是有些麻烦的。